Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
68a378c00f | |||
d96eaf14f4 | |||
01e488b69d | |||
7a450b16ba | |||
1ad8cbf15b |
@ -28,12 +28,19 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
|
|||||||
var k []byte
|
var k []byte
|
||||||
state := expectKey
|
state := expectKey
|
||||||
instr := false
|
instr := false
|
||||||
|
slash := 0
|
||||||
|
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
if instr && b[i] == '\\' {
|
||||||
|
slash++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[i] == '"' && (slash%2 == 0) {
|
||||||
|
instr = !instr
|
||||||
|
}
|
||||||
|
|
||||||
if state == expectObjClose || state == expectListClose {
|
if state == expectObjClose || state == expectListClose {
|
||||||
if b[i-1] != '\\' && b[i] == '"' {
|
|
||||||
instr = !instr
|
|
||||||
}
|
|
||||||
if !instr {
|
if !instr {
|
||||||
switch b[i] {
|
switch b[i] {
|
||||||
case '{', '[':
|
case '{', '[':
|
||||||
@ -70,7 +77,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
|
|||||||
state = expectKeyClose
|
state = expectKeyClose
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
||||||
state = expectColon
|
state = expectColon
|
||||||
k = b[(s + 1):i]
|
k = b[(s + 1):i]
|
||||||
|
|
||||||
@ -80,7 +87,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
|
|||||||
case state == expectValue && b[i] == '"':
|
case state == expectValue && b[i] == '"':
|
||||||
state = expectString
|
state = expectString
|
||||||
|
|
||||||
case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
||||||
e = i
|
e = i
|
||||||
|
|
||||||
case state == expectValue && b[i] == '[':
|
case state == expectValue && b[i] == '[':
|
||||||
|
19
jsn/get.go
19
jsn/get.go
@ -52,12 +52,19 @@ func Get(b []byte, keys [][]byte) []Field {
|
|||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
instr := false
|
instr := false
|
||||||
|
slash := 0
|
||||||
|
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
if instr && b[i] == '\\' {
|
||||||
|
slash++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[i] == '"' && (slash%2 == 0) {
|
||||||
|
instr = !instr
|
||||||
|
}
|
||||||
|
|
||||||
if state == expectObjClose || state == expectListClose {
|
if state == expectObjClose || state == expectListClose {
|
||||||
if b[i-1] != '\\' && b[i] == '"' {
|
|
||||||
instr = !instr
|
|
||||||
}
|
|
||||||
if !instr {
|
if !instr {
|
||||||
switch b[i] {
|
switch b[i] {
|
||||||
case '{', '[':
|
case '{', '[':
|
||||||
@ -73,7 +80,7 @@ func Get(b []byte, keys [][]byte) []Field {
|
|||||||
state = expectKeyClose
|
state = expectKeyClose
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
||||||
state = expectColon
|
state = expectColon
|
||||||
k = b[(s + 1):i]
|
k = b[(s + 1):i]
|
||||||
|
|
||||||
@ -84,7 +91,7 @@ func Get(b []byte, keys [][]byte) []Field {
|
|||||||
state = expectString
|
state = expectString
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
||||||
e = i
|
e = i
|
||||||
|
|
||||||
case state == expectValue && b[i] == '[':
|
case state == expectValue && b[i] == '[':
|
||||||
@ -155,6 +162,8 @@ func Get(b []byte, keys [][]byte) []Field {
|
|||||||
state = expectKey
|
state = expectKey
|
||||||
e = 0
|
e = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slash = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return res[:n]
|
return res[:n]
|
||||||
|
@ -2,7 +2,9 @@ package jsn
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -163,7 +165,9 @@ var (
|
|||||||
input6 = `
|
input6 = `
|
||||||
{"users" : [{"id" : 1, "email" : "vicram@gmail.com", "slug" : "vikram-rangnekar", "threads" : [], "threads_cursor" : null}, {"id" : 3, "email" : "marareilly@lang.name", "slug" : "raymundo-corwin", "threads" : [{"id" : 9, "title" : "Et alias et aut porro praesentium nam in voluptatem reiciendis quisquam perspiciatis inventore eos quia et et enim qui amet."}, {"id" : 25, "title" : "Ipsam quam nemo culpa tempore amet optio sit sed eligendi autem consequatur quaerat rem velit quibusdam quibusdam optio a voluptatem."}], "threads_cursor" : 25}], "users_cursor" : 3}`
|
{"users" : [{"id" : 1, "email" : "vicram@gmail.com", "slug" : "vikram-rangnekar", "threads" : [], "threads_cursor" : null}, {"id" : 3, "email" : "marareilly@lang.name", "slug" : "raymundo-corwin", "threads" : [{"id" : 9, "title" : "Et alias et aut porro praesentium nam in voluptatem reiciendis quisquam perspiciatis inventore eos quia et et enim qui amet."}, {"id" : 25, "title" : "Ipsam quam nemo culpa tempore amet optio sit sed eligendi autem consequatur quaerat rem velit quibusdam quibusdam optio a voluptatem."}], "threads_cursor" : 25}], "users_cursor" : 3}`
|
||||||
|
|
||||||
input7, _ = ioutil.ReadFile("test.json")
|
input7, _ = ioutil.ReadFile("test7.json")
|
||||||
|
|
||||||
|
input8, _ = ioutil.ReadFile("test8.json")
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -268,6 +272,23 @@ func TestGet3(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGet4(t *testing.T) {
|
||||||
|
exp := `"# \n\n@@@java\npackage main\n\nimport (\n \"net/http\"\n \"strings\"\n\n \"github.com/gin-gonic/gin\"\n)\n\nfunc main() {\n r := gin.Default()\n r.LoadHTMLGlob(\"templates/*\")\n\n r.GET(\"/\", handleIndex)\n r.GET(\"/to/:name\", handleIndex)\n r.Run()\n}\n\n// Hello is page data for the template\ntype Hello struct {\n Name string\n}\n\nfunc handleIndex(c *gin.Context) {\n name := c.Param(\"name\")\n if name != \"\" {\n name = strings.TrimPrefix(c.Param(\"name\"), \"/\")\n }\n c.HTML(http.StatusOK, \"hellofly.tmpl\", gin.H{\"Name\": name})\n}\n@@@\n\n\\"`
|
||||||
|
|
||||||
|
exp = strings.ReplaceAll(exp, "@", "`")
|
||||||
|
|
||||||
|
values := Get(input8, [][]byte{[]byte("body")})
|
||||||
|
|
||||||
|
if string(values[0].Key) != "body" {
|
||||||
|
t.Fatal("unexpected key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(values[0].Value) != exp {
|
||||||
|
fmt.Println(string(values[0].Value))
|
||||||
|
t.Fatal("unexpected value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValue(t *testing.T) {
|
func TestValue(t *testing.T) {
|
||||||
v1 := []byte("12345")
|
v1 := []byte("12345")
|
||||||
if !bytes.Equal(Value(v1), v1) {
|
if !bytes.Equal(Value(v1), v1) {
|
||||||
|
18
jsn/keys.go
18
jsn/keys.go
@ -11,12 +11,19 @@ func Keys(b []byte) [][]byte {
|
|||||||
st := NewStack()
|
st := NewStack()
|
||||||
ae := 0
|
ae := 0
|
||||||
instr := false
|
instr := false
|
||||||
|
slash := 0
|
||||||
|
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
if instr && b[i] == '\\' {
|
||||||
|
slash++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[i] == '"' && (slash%2 == 0) {
|
||||||
|
instr = !instr
|
||||||
|
}
|
||||||
|
|
||||||
if state == expectObjClose || state == expectListClose {
|
if state == expectObjClose || state == expectListClose {
|
||||||
if b[i-1] != '\\' && b[i] == '"' {
|
|
||||||
instr = !instr
|
|
||||||
}
|
|
||||||
if !instr {
|
if !instr {
|
||||||
switch b[i] {
|
switch b[i] {
|
||||||
case '{', '[':
|
case '{', '[':
|
||||||
@ -52,7 +59,7 @@ func Keys(b []byte) [][]byte {
|
|||||||
state = expectKeyClose
|
state = expectKeyClose
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
||||||
state = expectColon
|
state = expectColon
|
||||||
k = b[(s + 1):i]
|
k = b[(s + 1):i]
|
||||||
|
|
||||||
@ -63,7 +70,7 @@ func Keys(b []byte) [][]byte {
|
|||||||
state = expectString
|
state = expectString
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
||||||
e = i
|
e = i
|
||||||
|
|
||||||
case state == expectValue && b[i] == '{':
|
case state == expectValue && b[i] == '{':
|
||||||
@ -135,6 +142,7 @@ func Keys(b []byte) [][]byte {
|
|||||||
e = 0
|
e = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slash = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
@ -12,6 +12,11 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|||||||
return errors.New("'from' and 'to' must be of the same length")
|
return errors.New("'from' and 'to' must be of the same length")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(from) == 0 || len(to) == 0 {
|
||||||
|
_, err := w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
h := xxhash.New()
|
h := xxhash.New()
|
||||||
tmap := make(map[uint64]int, len(from))
|
tmap := make(map[uint64]int, len(from))
|
||||||
|
|
||||||
@ -33,17 +38,24 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|||||||
ws, we := -1, len(b)
|
ws, we := -1, len(b)
|
||||||
|
|
||||||
instr := false
|
instr := false
|
||||||
|
slash := 0
|
||||||
|
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
if instr && b[i] == '\\' {
|
||||||
|
slash++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// skip any left padding whitespace
|
// skip any left padding whitespace
|
||||||
if ws == -1 && (b[i] == '{' || b[i] == '[') {
|
if ws == -1 && (b[i] == '{' || b[i] == '[') {
|
||||||
ws = i
|
ws = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b[i] == '"' && (slash%2 == 0) {
|
||||||
|
instr = !instr
|
||||||
|
}
|
||||||
|
|
||||||
if state == expectObjClose || state == expectListClose {
|
if state == expectObjClose || state == expectListClose {
|
||||||
if b[i-1] != '\\' && b[i] == '"' {
|
|
||||||
instr = !instr
|
|
||||||
}
|
|
||||||
if !instr {
|
if !instr {
|
||||||
switch b[i] {
|
switch b[i] {
|
||||||
case '{', '[':
|
case '{', '[':
|
||||||
@ -59,7 +71,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|||||||
state = expectKeyClose
|
state = expectKeyClose
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
||||||
state = expectColon
|
state = expectColon
|
||||||
if _, err := h.Write(b[(s + 1):i]); err != nil {
|
if _, err := h.Write(b[(s + 1):i]); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -73,7 +85,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|||||||
state = expectString
|
state = expectString
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
||||||
e = i
|
e = i
|
||||||
|
|
||||||
case state == expectValue && b[i] == '[':
|
case state == expectValue && b[i] == '[':
|
||||||
@ -167,6 +179,8 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|||||||
e = 0
|
e = 0
|
||||||
d = 0
|
d = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slash = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if ws == -1 || (ws == 0 && we == len(b)) {
|
if ws == -1 || (ws == 0 && we == len(b)) {
|
||||||
|
19
jsn/strip.go
19
jsn/strip.go
@ -12,12 +12,19 @@ func Strip(b []byte, path [][]byte) []byte {
|
|||||||
pm := false
|
pm := false
|
||||||
state := expectKey
|
state := expectKey
|
||||||
instr := false
|
instr := false
|
||||||
|
slash := 0
|
||||||
|
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
|
if instr && b[i] == '\\' {
|
||||||
|
slash++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if b[i] == '"' && (slash%2 == 0) {
|
||||||
|
instr = !instr
|
||||||
|
}
|
||||||
|
|
||||||
if state == expectObjClose || state == expectListClose {
|
if state == expectObjClose || state == expectListClose {
|
||||||
if b[i-1] != '\\' && b[i] == '"' {
|
|
||||||
instr = !instr
|
|
||||||
}
|
|
||||||
if !instr {
|
if !instr {
|
||||||
switch b[i] {
|
switch b[i] {
|
||||||
case '{', '[':
|
case '{', '[':
|
||||||
@ -33,7 +40,7 @@ func Strip(b []byte, path [][]byte) []byte {
|
|||||||
state = expectKeyClose
|
state = expectKeyClose
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
||||||
state = expectColon
|
state = expectColon
|
||||||
if pi == len(path) {
|
if pi == len(path) {
|
||||||
pi = 0
|
pi = 0
|
||||||
@ -50,7 +57,7 @@ func Strip(b []byte, path [][]byte) []byte {
|
|||||||
state = expectString
|
state = expectString
|
||||||
s = i
|
s = i
|
||||||
|
|
||||||
case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
||||||
e = i
|
e = i
|
||||||
|
|
||||||
case state == expectValue && b[i] == '[':
|
case state == expectValue && b[i] == '[':
|
||||||
@ -107,6 +114,8 @@ func Strip(b []byte, path [][]byte) []byte {
|
|||||||
state = expectKey
|
state = expectKey
|
||||||
e = 0
|
e = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slash = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return ob
|
return ob
|
||||||
|
7
jsn/test8.json
Normal file
7
jsn/test8.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"slug": "javapackage-mainimport-nethttp-strings-githubcomgi-2786",
|
||||||
|
"published": true,
|
||||||
|
"body": "# \n\n```java\npackage main\n\nimport (\n \"net/http\"\n \"strings\"\n\n \"github.com/gin-gonic/gin\"\n)\n\nfunc main() {\n r := gin.Default()\n r.LoadHTMLGlob(\"templates/*\")\n\n r.GET(\"/\", handleIndex)\n r.GET(\"/to/:name\", handleIndex)\n r.Run()\n}\n\n// Hello is page data for the template\ntype Hello struct {\n Name string\n}\n\nfunc handleIndex(c *gin.Context) {\n name := c.Param(\"name\")\n if name != \"\" {\n name = strings.TrimPrefix(c.Param(\"name\"), \"/\")\n }\n c.HTML(http.StatusOK, \"hellofly.tmpl\", gin.H{\"Name\": name})\n}\n```\n\n\\"
|
||||||
|
}
|
||||||
|
}
|
@ -93,7 +93,7 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
|
|||||||
io.WriteString(c.w, `SELECT json_build_object(`)
|
io.WriteString(c.w, `SELECT json_build_object(`)
|
||||||
for _, id := range qc.Roots {
|
for _, id := range qc.Roots {
|
||||||
root := &qc.Selects[id]
|
root := &qc.Selects[id]
|
||||||
if root.SkipRender {
|
if root.SkipRender || len(root.Cols) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +126,10 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
|
|||||||
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 0, err
|
return 0, err
|
||||||
|
@ -66,7 +66,14 @@ func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, t := range info.Tables {
|
for i, t := range info.Tables {
|
||||||
err := schema.updateRelationships(t, info.Columns[i])
|
err := schema.firstDegreeRels(t, info.Columns[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, t := range info.Tables {
|
||||||
|
err := schema.secondDegreeRels(t, info.Columns[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -131,8 +138,7 @@ func (s *DBSchema) addTable(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DBSchema) updateRelationships(t DBTable, cols []DBColumn) error {
|
func (s *DBSchema) firstDegreeRels(t DBTable, cols []DBColumn) error {
|
||||||
jcols := make([]DBColumn, 0, len(cols))
|
|
||||||
ct := t.Key
|
ct := t.Key
|
||||||
cti, ok := s.t[ct]
|
cti, ok := s.t[ct]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -230,6 +236,51 @@ func (s *DBSchema) updateRelationships(t DBTable, cols []DBColumn) error {
|
|||||||
if err := s.SetRel(ft, ct, rel2); err != nil {
|
if err := s.SetRel(ft, ct, rel2); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DBSchema) secondDegreeRels(t DBTable, cols []DBColumn) error {
|
||||||
|
jcols := make([]DBColumn, 0, len(cols))
|
||||||
|
ct := t.Key
|
||||||
|
cti, ok := s.t[ct]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid foreign key table '%s'", ct)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range cols {
|
||||||
|
c := cols[i]
|
||||||
|
|
||||||
|
if len(c.FKeyTable) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreign key column name
|
||||||
|
ft := strings.ToLower(c.FKeyTable)
|
||||||
|
|
||||||
|
ti, ok := s.t[ft]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid foreign key table '%s'", ft)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an embedded relationship like when a json/jsonb column
|
||||||
|
// is exposed as a table
|
||||||
|
if c.Name == c.FKeyTable && len(c.FKeyColID) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.FKeyColID) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreign key column id
|
||||||
|
fcid := c.FKeyColID[0]
|
||||||
|
|
||||||
|
if _, ok := ti.ColIDMap[fcid]; !ok {
|
||||||
|
return fmt.Errorf("invalid foreign key column id '%d' for table '%s'",
|
||||||
|
fcid, ti.Name)
|
||||||
|
}
|
||||||
|
|
||||||
jcols = append(jcols, c)
|
jcols = append(jcols, c)
|
||||||
}
|
}
|
||||||
@ -322,6 +373,9 @@ func (s *DBSchema) GetTable(table string) (*DBTableInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DBSchema) SetRel(child, parent string, rel *DBRel) error {
|
func (s *DBSchema) SetRel(child, parent string, rel *DBRel) error {
|
||||||
|
sp := strings.ToLower(flect.Singularize(parent))
|
||||||
|
pp := strings.ToLower(flect.Pluralize(parent))
|
||||||
|
|
||||||
sc := strings.ToLower(flect.Singularize(child))
|
sc := strings.ToLower(flect.Singularize(child))
|
||||||
pc := strings.ToLower(flect.Pluralize(child))
|
pc := strings.ToLower(flect.Pluralize(child))
|
||||||
|
|
||||||
@ -333,9 +387,6 @@ func (s *DBSchema) SetRel(child, parent string, rel *DBRel) error {
|
|||||||
s.rm[pc] = make(map[string]*DBRel)
|
s.rm[pc] = make(map[string]*DBRel)
|
||||||
}
|
}
|
||||||
|
|
||||||
sp := strings.ToLower(flect.Singularize(parent))
|
|
||||||
pp := strings.ToLower(flect.Pluralize(parent))
|
|
||||||
|
|
||||||
if _, ok := s.rm[sc][sp]; !ok {
|
if _, ok := s.rm[sc][sp]; !ok {
|
||||||
s.rm[sc][sp] = rel
|
s.rm[sc][sp] = rel
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ func (rt RelType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (re *DBRel) String() string {
|
func (re *DBRel) String() string {
|
||||||
|
if re.Type == RelOneToManyThrough {
|
||||||
|
return fmt.Sprintf("'%s.%s' --(Through: %s)--> '%s.%s'",
|
||||||
|
re.Left.Table, re.Left.Col, re.Through, 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)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,14 @@ func getTestSchema() *DBSchema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, t := range tables {
|
for i, t := range tables {
|
||||||
err := schema.updateRelationships(t, columns[i])
|
err := schema.firstDegreeRels(t, columns[i])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, t := range tables {
|
||||||
|
err := schema.secondDegreeRels(t, columns[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,10 @@ func (c *compilerContext) renderDelete(qc *qcode.QCode, w io.Writer,
|
|||||||
quoted(c.w, ti.Name)
|
quoted(c.w, ti.Name)
|
||||||
io.WriteString(c.w, ` WHERE `)
|
io.WriteString(c.w, ` WHERE `)
|
||||||
|
|
||||||
|
if root.Where == nil {
|
||||||
|
return 0, errors.New("'where' clause missing in delete mutation")
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.renderWhere(root, ti); err != nil {
|
if err := c.renderWhere(root, ti); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -59,12 +59,6 @@ func buildRoleStmt(gql, vars []byte, role string) ([]stmt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the 'anon' role in production only compile
|
|
||||||
// queries for tables defined in the config file.
|
|
||||||
if conf.Production && ro.Name == "anon" && !hasTablesWithConfig(qc, ro) {
|
|
||||||
return nil, errors.New("query contains tables with no 'anon' role config")
|
|
||||||
}
|
|
||||||
|
|
||||||
stmts := []stmt{stmt{role: ro, qc: qc}}
|
stmts := []stmt{stmt{role: ro, qc: qc}}
|
||||||
w := &bytes.Buffer{}
|
w := &bytes.Buffer{}
|
||||||
|
|
||||||
|
@ -83,12 +83,6 @@ func prepareStmt(item allow.Item) error {
|
|||||||
qt := qcode.GetQType(gql)
|
qt := qcode.GetQType(gql)
|
||||||
q := []byte(gql)
|
q := []byte(gql)
|
||||||
|
|
||||||
if len(vars) == 0 {
|
|
||||||
logger.Debug().Msgf("Prepared statement:\n%s\n", gql)
|
|
||||||
} else {
|
|
||||||
logger.Debug().Msgf("Prepared statement:\n%s\n%s\n", vars, gql)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := db.Begin(context.Background())
|
tx, err := db.Begin(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -110,7 +104,7 @@ func prepareStmt(item allow.Item) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug().Msg("Prepared statement role: user")
|
logger.Debug().Msgf("Prepared statement 'query %s' (user)", item.Name)
|
||||||
|
|
||||||
err = prepare(tx, stmts1, stmtHash(item.Name, "user"))
|
err = prepare(tx, stmts1, stmtHash(item.Name, "user"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,7 +112,7 @@ func prepareStmt(item allow.Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if conf.isAnonRoleDefined() {
|
if conf.isAnonRoleDefined() {
|
||||||
logger.Debug().Msg("Prepared statement for role: anon")
|
logger.Debug().Msgf("Prepared statement 'query %s' (anon)", item.Name)
|
||||||
|
|
||||||
stmts2, err := buildRoleStmt(q, vars, "anon")
|
stmts2, err := buildRoleStmt(q, vars, "anon")
|
||||||
if err == psql.ErrAllTablesSkipped {
|
if err == psql.ErrAllTablesSkipped {
|
||||||
@ -136,11 +130,17 @@ func prepareStmt(item allow.Item) error {
|
|||||||
|
|
||||||
case qcode.QTMutation:
|
case qcode.QTMutation:
|
||||||
for _, role := range conf.Roles {
|
for _, role := range conf.Roles {
|
||||||
logger.Debug().Msgf("Prepared statement for role: %s", role.Name)
|
logger.Debug().Msgf("Prepared statement 'mutation %s' (%s)", item.Name, role.Name)
|
||||||
|
|
||||||
stmts, err := buildRoleStmt(q, vars, role.Name)
|
stmts, err := buildRoleStmt(q, vars, role.Name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if len(item.Vars) == 0 {
|
||||||
|
logger.Warn().Err(err).Msg(item.Query)
|
||||||
|
} else {
|
||||||
|
logger.Warn().Err(err).Msgf("%s %s", item.Vars, item.Query)
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = prepare(tx, stmts, stmtHash(item.Name, role.Name))
|
err = prepare(tx, stmts, stmtHash(item.Name, role.Name))
|
||||||
|
Reference in New Issue
Block a user