diff --git a/core/internal/allow/allow.go b/core/internal/allow/allow.go index 59ccf68..5efe277 100644 --- a/core/internal/allow/allow.go +++ b/core/internal/allow/allow.go @@ -9,6 +9,8 @@ import ( "os" "sort" "strings" + + "github.com/dosco/super-graph/jsn" ) const ( @@ -230,6 +232,8 @@ func (al *List) Load() ([]Item, error) { } func (al *List) save(item Item) error { + var buf bytes.Buffer + item.Name = QueryName(item.Query) item.key = strings.ToLower(item.Name) @@ -298,9 +302,16 @@ func (al *List) save(item Item) error { } if len(v.Vars) != 0 && !bytes.Equal(v.Vars, []byte("{}")) { - vj, err := json.MarshalIndent(v.Vars, "", " ") + buf.Reset() + + if err := jsn.Clear(&buf, v.Vars); err != nil { + return fmt.Errorf("failed to clean vars: %w", err) + } + vj := json.RawMessage(buf.Bytes()) + + vj, err = json.MarshalIndent(vj, "", " ") if err != nil { - return fmt.Errorf("failed to marshal vars: %v", err) + return fmt.Errorf("failed to marshal vars: %w", err) } _, err = f.WriteString(fmt.Sprintf("variables %s\n\n", vj)) diff --git a/core/internal/psql/query.go b/core/internal/psql/query.go index 584d263..4c7a3a5 100644 --- a/core/internal/psql/query.go +++ b/core/internal/psql/query.go @@ -921,8 +921,6 @@ func (c *compilerContext) renderExp(ex *qcode.Exp, ti *DBTableInfo, skipNested b st.Push('(') case qcode.OpNot: - //fmt.Printf("1> %s %d %s %s\n", val.Op, len(val.Children), val.Children[0].Op, val.Children[1].Op) - st.Push(val.Children[0]) st.Push(qcode.OpNot) diff --git a/core/internal/qcode/parse.go b/core/internal/qcode/parse.go index f263b6c..94c0315 100644 --- a/core/internal/qcode/parse.go +++ b/core/internal/qcode/parse.go @@ -602,7 +602,7 @@ func (t parserType) String() string { // nodePool.Put(n) // freeList = append(freeList, Frees{n, loc}) // } else { -// fmt.Printf(">>>>(%d) RE_FREE %d %p %s %s\n", loc, freeList[j].loc, freeList[j].n, n.Name, n.Type) +// fmt.Printf("(%d) RE_FREE %d %p %s %s\n", loc, freeList[j].loc, freeList[j].n, n.Name, n.Type) // } // } diff --git a/jsn/clear.go b/jsn/clear.go new file mode 100644 index 0000000..cac5550 --- /dev/null +++ b/jsn/clear.go @@ -0,0 +1,112 @@ +package jsn + +import ( + "bytes" + "encoding/json" + "io" +) + +// Clear function wipes all scalar values from the json including those directly in an array +func Clear(w *bytes.Buffer, v []byte) error { + dec := json.NewDecoder(bytes.NewReader(v)) + + st := newIntStack() + isValue := false + inArray := false + n := 0 + + for { + var t json.Token + var err error + + if t, err = dec.Token(); err == io.EOF { + break + } else if err != nil { + return err + } + + switch v1 := t.(type) { + case int: + if isValue && !inArray { + w.WriteByte('0') + isValue = false + n++ + } + + case float64: + if isValue && !inArray { + w.WriteString(`0.0`) + isValue = false + n++ + } + + case bool: + if isValue && !inArray { + w.WriteString(`false`) + isValue = false + n++ + } + + case json.Number: + if isValue && !inArray { + w.WriteString(`0`) + isValue = false + n++ + } + + case nil: + if isValue && !inArray { + w.WriteString(`null`) + isValue = false + n++ + } + + case string: + if !isValue { + if n != 0 { + w.WriteByte(',') + } + + io := int(dec.InputOffset()) + w.Write(v[io-len(v1)-2 : io]) + w.WriteString(`:`) + isValue = true + + } else if !inArray { + w.WriteString(`""`) + isValue = false + n++ + } + + case json.Delim: + switch t.(json.Delim) { + case '[': + st.Push(n) + inArray = true + n = 0 + case ']': + n = st.Pop() + inArray = false + isValue = false + n++ + case '{': + if n != 0 && !isValue { + w.WriteByte(',') + } + st.Push(n) + inArray = false + isValue = false + n = 0 + case '}': + n = st.Pop() + isValue = false + n++ + } + w.WriteByte(v[dec.InputOffset()-1]) + } + + dec.More() + } + + return nil +} diff --git a/jsn/intstack.go b/jsn/intstack.go new file mode 100644 index 0000000..aa4d84d --- /dev/null +++ b/jsn/intstack.go @@ -0,0 +1,47 @@ +package jsn + +type intStack struct { + stA [20]int + st []int + top int +} + +// Create a new intStack +func newIntStack() *intStack { + s := &intStack{top: -1} + s.st = s.stA[:0] + return s +} + +// Return the number of items in the intStack +func (s *intStack) Len() int { + return (s.top + 1) +} + +// View the top item on the intStack +func (s *intStack) Peek() int { + if s.top == -1 { + return -1 + } + return s.st[s.top] +} + +// Pop the top item of the intStack and return it +func (s *intStack) Pop() int { + if s.top == -1 { + return -1 + } + + s.top-- + return s.st[(s.top + 1)] +} + +// Push a value onto the top of the intStack +func (s *intStack) Push(value int) { + s.top++ + if len(s.st) <= s.top { + s.st = append(s.st, value) + } else { + s.st[s.top] = value + } +} diff --git a/jsn/json_test.go b/jsn/json_test.go index 0f093e9..c473205 100644 --- a/jsn/json_test.go +++ b/jsn/json_test.go @@ -509,6 +509,34 @@ func TestKeys3(t *testing.T) { } } +func TestClear(t *testing.T) { + var buf bytes.Buffer + + json := `{ + "insert": { + "created_at": "now", + "test_1a": { "type1": "a", "type2": [{ "a": 2 }] }, + "name": "Hello", + "updated_at": "now", + "description": "World" + }, + "user": 123, + "tags": [1, 2, "what"] + }` + + expected := `{"insert":{"created_at":"","test_1a":{"type1":"","type2":[{"a":0.0}]},"name":"","updated_at":"","description":""},"user":0.0,"tags":[]}` + + err := Clear(&buf, []byte(json)) + if err != nil { + t.Fatal(err) + } + + if buf.String() != expected { + t.Log(buf.String()) + t.Error("Does not match expected json") + } +} + func BenchmarkGet(b *testing.B) { b.ReportAllocs() diff --git a/jsn/keys.go b/jsn/keys.go index d986e9d..4bbd31c 100644 --- a/jsn/keys.go +++ b/jsn/keys.go @@ -10,7 +10,7 @@ func Keys(b []byte) [][]byte { var k []byte state := expectValue - st := newStack() + st := newSkipInfoStack() ae := 0 instr := false slash := 0 diff --git a/jsn/sistack.go b/jsn/sistack.go new file mode 100644 index 0000000..00775cd --- /dev/null +++ b/jsn/sistack.go @@ -0,0 +1,51 @@ +package jsn + +type skipInfo struct { + ss, se int +} + +type siStack struct { + stA [20]skipInfo + st []skipInfo + top int +} + +// Create a new siStack +func newSkipInfoStack() *siStack { + s := &siStack{top: -1} + s.st = s.stA[:0] + return s +} + +// Return the number of items in the siStack +func (s *siStack) Len() int { + return (s.top + 1) +} + +// View the top item on the siStack +func (s *siStack) Peek() *skipInfo { + if s.top == -1 { + return nil + } + return &s.st[s.top] +} + +// Pop the top item of the siStack and return it +func (s *siStack) Pop() *skipInfo { + if s.top == -1 { + return nil + } + + s.top-- + return &s.st[(s.top + 1)] +} + +// Push a value onto the top of the siStack +func (s *siStack) Push(value skipInfo) { + s.top++ + if len(s.st) <= s.top { + s.st = append(s.st, value) + } else { + s.st[s.top] = value + } +} diff --git a/jsn/stack.go b/jsn/stack.go deleted file mode 100644 index 4bcc0dd..0000000 --- a/jsn/stack.go +++ /dev/null @@ -1,51 +0,0 @@ -package jsn - -type skipInfo struct { - ss, se int -} - -type stack struct { - stA [20]skipInfo - st []skipInfo - top int -} - -// Create a new stack -func newStack() *stack { - s := &stack{top: -1} - s.st = s.stA[:0] - return s -} - -// Return the number of items in the stack -func (s *stack) Len() int { - return (s.top + 1) -} - -// View the top item on the stack -func (s *stack) Peek() *skipInfo { - if s.top == -1 { - return nil - } - return &s.st[s.top] -} - -// Pop the top item of the stack and return it -func (s *stack) Pop() *skipInfo { - if s.top == -1 { - return nil - } - - s.top-- - return &s.st[(s.top + 1)] -} - -// Push a value onto the top of the stack -func (s *stack) Push(value skipInfo) { - s.top++ - if len(s.st) <= s.top { - s.st = append(s.st, value) - } else { - s.st[s.top] = value - } -}