Use hash's as ids for table relationships
This commit is contained in:
parent
e1d3bb9055
commit
58408eadc1
15
psql/psql.go
15
psql/psql.go
|
@ -30,7 +30,7 @@ func NewCompiler(conf Config) *Compiler {
|
|||
return &Compiler{conf.Schema, conf.Vars, conf.TableMap}
|
||||
}
|
||||
|
||||
func (c *Compiler) AddRelationship(key TTKey, val *DBRel) {
|
||||
func (c *Compiler) AddRelationship(key uint64, val *DBRel) {
|
||||
c.schema.RelMap[key] = val
|
||||
}
|
||||
|
||||
|
@ -139,9 +139,8 @@ func (v *selectBlock) processChildren() (uint32, []*qcode.Column) {
|
|||
|
||||
for _, id := range v.sel.Children {
|
||||
child := &v.qc.Query.Selects[id]
|
||||
k := TTKey{child.Table, v.sel.Table}
|
||||
|
||||
rel, ok := v.schema.RelMap[k]
|
||||
rel, ok := v.schema.RelMap[child.RelID]
|
||||
if !ok {
|
||||
skipped |= (1 << uint(id))
|
||||
continue
|
||||
|
@ -290,8 +289,7 @@ func (v *joinClose) render(w io.Writer) error {
|
|||
}
|
||||
|
||||
func (v *selectBlock) renderJoinTable(w io.Writer) {
|
||||
k := TTKey{v.sel.Table, v.parent.Table}
|
||||
rel, ok := v.schema.RelMap[k]
|
||||
rel, ok := v.schema.RelMap[v.sel.RelID]
|
||||
if !ok {
|
||||
panic(errors.New("no relationship found"))
|
||||
}
|
||||
|
@ -315,14 +313,12 @@ func (v *selectBlock) renderColumns(w io.Writer) {
|
|||
}
|
||||
|
||||
func (v *selectBlock) renderRemoteRelColumns(w io.Writer) {
|
||||
k := TTKey{Table2: v.sel.Table}
|
||||
i := 0
|
||||
|
||||
for _, id := range v.sel.Children {
|
||||
child := &v.qc.Query.Selects[id]
|
||||
k.Table1 = child.Table
|
||||
|
||||
rel, ok := v.schema.RelMap[k]
|
||||
rel, ok := v.schema.RelMap[child.RelID]
|
||||
if !ok || rel.Type != RelRemote {
|
||||
continue
|
||||
}
|
||||
|
@ -489,8 +485,7 @@ func (v *selectBlock) renderOrderByColumns(w io.Writer) {
|
|||
}
|
||||
|
||||
func (v *selectBlock) renderRelationship(w io.Writer) {
|
||||
k := TTKey{v.sel.Table, v.parent.Table}
|
||||
rel, ok := v.schema.RelMap[k]
|
||||
rel, ok := v.schema.RelMap[v.sel.RelID]
|
||||
if !ok {
|
||||
panic(errors.New("no relationship found"))
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func TestMain(m *testing.M) {
|
|||
|
||||
schema := &DBSchema{
|
||||
Tables: make(map[string]*DBTableInfo),
|
||||
RelMap: make(map[TTKey]*DBRel),
|
||||
RelMap: make(map[uint64]*DBRel),
|
||||
}
|
||||
|
||||
for i, t := range tables {
|
||||
|
|
|
@ -4,20 +4,13 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/go-pg/pg"
|
||||
)
|
||||
|
||||
type TCKey struct {
|
||||
Table, Column string
|
||||
}
|
||||
|
||||
type TTKey struct {
|
||||
Table1, Table2 string
|
||||
}
|
||||
|
||||
type DBSchema struct {
|
||||
Tables map[string]*DBTableInfo
|
||||
RelMap map[TTKey]*DBRel
|
||||
RelMap map[uint64]*DBRel
|
||||
}
|
||||
|
||||
type DBTableInfo struct {
|
||||
|
@ -47,7 +40,7 @@ type DBRel struct {
|
|||
func NewDBSchema(db *pg.DB) (*DBSchema, error) {
|
||||
schema := &DBSchema{
|
||||
Tables: make(map[string]*DBTableInfo),
|
||||
RelMap: make(map[TTKey]*DBRel),
|
||||
RelMap: make(map[uint64]*DBRel),
|
||||
}
|
||||
|
||||
tables, err := GetTables(db)
|
||||
|
@ -87,6 +80,8 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
|
|||
ct := strings.ToLower(t.Name)
|
||||
s.Tables[ct] = ti
|
||||
|
||||
h := xxhash.New()
|
||||
|
||||
for _, c := range cols {
|
||||
switch {
|
||||
case c.Type == "tsvector":
|
||||
|
@ -110,12 +105,12 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
|
|||
// Belongs-to relation between current table and the
|
||||
// table in the foreign key
|
||||
rel1 := &DBRel{RelBelongTo, "", "", c.Name, fc.Name}
|
||||
s.RelMap[TTKey{ct, ft}] = rel1
|
||||
s.RelMap[relID(h, ct, ft)] = rel1
|
||||
|
||||
// One-to-many relation between the foreign key table and the
|
||||
// the current table
|
||||
rel2 := &DBRel{RelOneToMany, "", "", fc.Name, c.Name}
|
||||
s.RelMap[TTKey{ft, ct}] = rel2
|
||||
s.RelMap[relID(h, ft, ct)] = rel2
|
||||
|
||||
jcols = append(jcols, c)
|
||||
}
|
||||
|
@ -131,7 +126,7 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
|
|||
for i := range jcols {
|
||||
for n := range jcols {
|
||||
if n != i {
|
||||
s.updateSchemaOTMT(ct, jcols[i], jcols[n], colByID)
|
||||
s.updateSchemaOTMT(h, ct, jcols[i], jcols[n], colByID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +134,10 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
|
|||
}
|
||||
|
||||
func (s *DBSchema) updateSchemaOTMT(
|
||||
ct string, col1, col2 *DBColumn, colByID map[int]*DBColumn) {
|
||||
h *xxhash.Digest,
|
||||
ct string,
|
||||
col1, col2 *DBColumn,
|
||||
colByID map[int]*DBColumn) {
|
||||
|
||||
t1 := strings.ToLower(col1.FKeyTable)
|
||||
t2 := strings.ToLower(col2.FKeyTable)
|
||||
|
@ -157,13 +155,13 @@ func (s *DBSchema) updateSchemaOTMT(
|
|||
// 2nd foreign key table
|
||||
//rel1 := &DBRel{RelOneToManyThrough, ct, fc1.Name, col1.Name}
|
||||
rel1 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name, col1.Name}
|
||||
s.RelMap[TTKey{t1, t2}] = rel1
|
||||
s.RelMap[relID(h, 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}
|
||||
s.RelMap[TTKey{t2, t1}] = rel2
|
||||
s.RelMap[relID(h, t2, t1)] = rel2
|
||||
}
|
||||
|
||||
type DBTable struct {
|
||||
|
@ -264,3 +262,11 @@ func (s *DBSchema) GetTable(table string) (*DBTableInfo, error) {
|
|||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func relID(h *xxhash.Digest, child, parent string) uint64 {
|
||||
h.WriteString(child)
|
||||
h.WriteString(parent)
|
||||
v := h.Sum64()
|
||||
h.Reset()
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -73,3 +73,57 @@ func TestEmptyCompile(t *testing.T) {
|
|||
t.Fatal(errors.New("expecting an error"))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQCompile(b *testing.B) {
|
||||
qcompile, _ := NewCompiler(Config{})
|
||||
|
||||
val := `query {
|
||||
products(
|
||||
where: {
|
||||
and: {
|
||||
not: { id: { is_null: true } },
|
||||
price: { gt: 10 }
|
||||
}}) {
|
||||
id
|
||||
name
|
||||
price
|
||||
}
|
||||
}`
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := qcompile.CompileQuery(val)
|
||||
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLex(b *testing.B) {
|
||||
val := `query {
|
||||
products(
|
||||
where: {
|
||||
and: {
|
||||
not: { id: { is_null: true } },
|
||||
price: { gt: 10 }
|
||||
}}) {
|
||||
id
|
||||
name
|
||||
price
|
||||
}
|
||||
}`
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := lex(val)
|
||||
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/dosco/super-graph/util"
|
||||
"github.com/gobuffalo/flect"
|
||||
)
|
||||
|
@ -30,6 +31,7 @@ type Column struct {
|
|||
type Select struct {
|
||||
ID uint16
|
||||
ParentID uint16
|
||||
RelID uint64
|
||||
Args map[string]*Node
|
||||
AsList bool
|
||||
Table string
|
||||
|
@ -255,6 +257,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
|
|||
|
||||
selects := make([]Select, 0, 5)
|
||||
st := util.NewStack()
|
||||
h := xxhash.New()
|
||||
|
||||
if len(op.Fields) == 0 {
|
||||
return nil, errors.New("empty query")
|
||||
|
@ -294,6 +297,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
|
|||
if s.ID != 0 {
|
||||
p := &selects[s.ParentID]
|
||||
p.Children = append(p.Children, s.ID)
|
||||
s.RelID = relID(h, tn, p.Table)
|
||||
}
|
||||
|
||||
if fn == tn {
|
||||
|
@ -875,3 +879,11 @@ func buildPath(a []string) string {
|
|||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func relID(h *xxhash.Digest, child, parent string) uint64 {
|
||||
h.WriteString(child)
|
||||
h.WriteString(parent)
|
||||
v := h.Sum64()
|
||||
h.Reset()
|
||||
return v
|
||||
}
|
||||
|
|
23
serv/core.go
23
serv/core.go
|
@ -34,8 +34,6 @@ type coreContext struct {
|
|||
func (c *coreContext) handleReq(w io.Writer, req *http.Request) error {
|
||||
var err error
|
||||
|
||||
//cacheEnabled := (conf.EnableTracing == false)
|
||||
|
||||
qc, err := qcompile.CompileQuery(c.req.Query)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -130,17 +128,12 @@ func (c *coreContext) handleReq(w io.Writer, req *http.Request) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// if cacheEnabled {
|
||||
// if err = cache.Set(key, []byte(finalSQL)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
return c.render(w, ob.Bytes())
|
||||
}
|
||||
|
||||
func (c *coreContext) resolveSQL(qc *qcode.QCode, vars variables) (
|
||||
[]byte, uint32, error) {
|
||||
|
||||
// var entry []byte
|
||||
// var key string
|
||||
|
||||
|
@ -180,13 +173,23 @@ func (c *coreContext) resolveSQL(qc *qcode.QCode, vars variables) (
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
finalSQL := sqlStmt.String()
|
||||
finalSQL := sqlStmt.Bytes()
|
||||
|
||||
if conf.DebugLevel > 0 {
|
||||
fmt.Println(finalSQL)
|
||||
}
|
||||
|
||||
st := time.Now()
|
||||
// if cacheEnabled {
|
||||
// if err = cache.Set(key, finalSQL); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
var st time.Time
|
||||
|
||||
if conf.EnableTracing {
|
||||
st = time.Now()
|
||||
}
|
||||
|
||||
var root json.RawMessage
|
||||
_, err = db.Query(pg.Scan(&root), finalSQL)
|
||||
|
|
|
@ -45,7 +45,12 @@ func initRemotes(t configTable) {
|
|||
|
||||
// register a relationship between the remote data
|
||||
// and the database table
|
||||
key := psql.TTKey{strings.ToLower(r.Name), t.Name}
|
||||
|
||||
h.WriteString(strings.ToLower(r.Name))
|
||||
h.WriteString(t.Name)
|
||||
key := h.Sum64()
|
||||
h.Reset()
|
||||
|
||||
val := &psql.DBRel{
|
||||
Type: psql.RelRemote,
|
||||
Col1: idcol,
|
||||
|
|
Loading…
Reference in New Issue