Prevent race conditions in when fetching document fields
This commit is contained in:
parent
b94a944299
commit
11a9fa05e6
2 changed files with 34 additions and 42 deletions
16
db/fetch.go
16
db/fetch.go
|
@ -84,7 +84,7 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
return val, queryIdentFailed
|
return val, queryIdentFailed
|
||||||
case doc != nil:
|
case doc != nil:
|
||||||
|
|
||||||
doc.Fetch(func(key string, val interface{}, path []string) interface{} {
|
fnc := func(key string, val interface{}, path []string) interface{} {
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
switch res := val.(type) {
|
switch res := val.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
|
@ -96,9 +96,11 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
})
|
}
|
||||||
|
|
||||||
return e.fetch(ctx, doc.Get(val.ID).Data(), doc)
|
res := doc.Fetch(fnc, val.ID).Data()
|
||||||
|
|
||||||
|
return e.fetch(ctx, res, doc)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +110,7 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
|
|
||||||
if obj, ok := ctx.Value(s).(*data.Doc); ok {
|
if obj, ok := ctx.Value(s).(*data.Doc); ok {
|
||||||
|
|
||||||
obj.Fetch(func(key string, val interface{}, path []string) interface{} {
|
fnc := func(key string, val interface{}, path []string) interface{} {
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
switch res := val.(type) {
|
switch res := val.(type) {
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
|
@ -120,9 +122,11 @@ func (e *executor) fetch(ctx context.Context, val interface{}, doc *data.Doc) (o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
})
|
}
|
||||||
|
|
||||||
if res := obj.Get(val.ID).Data(); res != nil {
|
res := obj.Fetch(fnc, val.ID).Data()
|
||||||
|
|
||||||
|
if res != nil {
|
||||||
return e.fetch(ctx, res, doc)
|
return e.fetch(ctx, res, doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ const (
|
||||||
// Doc holds a reference to the core data object, or a selected path.
|
// Doc holds a reference to the core data object, or a selected path.
|
||||||
type Doc struct {
|
type Doc struct {
|
||||||
data interface{}
|
data interface{}
|
||||||
call Fetcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetcher is used when fetching values.
|
// Fetcher is used when fetching values.
|
||||||
|
@ -58,11 +57,6 @@ func Consume(input interface{}) *Doc {
|
||||||
return &Doc{data: input}
|
return &Doc{data: input}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume converts a GO interface into a data object.
|
|
||||||
func ConsumeWithFetch(input interface{}, fetcher Fetcher) *Doc {
|
|
||||||
return &Doc{data: input, call: fetcher}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data returns the internal data object as an interface.
|
// Data returns the internal data object as an interface.
|
||||||
func (d *Doc) Data() interface{} {
|
func (d *Doc) Data() interface{} {
|
||||||
return d.data
|
return d.data
|
||||||
|
@ -73,11 +67,6 @@ func (d *Doc) Copy() *Doc {
|
||||||
return &Doc{data: deep.Copy(d.data)}
|
return &Doc{data: deep.Copy(d.data)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch adds a function to be used for retrieving and converting values.
|
|
||||||
func (d *Doc) Fetch(fetcher Fetcher) {
|
|
||||||
d.call = fetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes the data object to a byte slice.
|
// Encode encodes the data object to a byte slice.
|
||||||
func (d *Doc) Encode() (dst []byte) {
|
func (d *Doc) Encode() (dst []byte) {
|
||||||
dst = pack.Encode(d.data)
|
dst = pack.Encode(d.data)
|
||||||
|
@ -430,18 +419,12 @@ func (d *Doc) Exists(path ...string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == one {
|
if r == one {
|
||||||
if d.call != nil {
|
return Consume(c[0]).Exists(path[k+1:]...)
|
||||||
c[0] = d.call(p, c[0], path[k+1:])
|
|
||||||
}
|
|
||||||
return ConsumeWithFetch(c[0], d.call).Exists(path[k+1:]...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == many {
|
if r == many {
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
if d.call != nil {
|
if !Consume(v).Exists(path[k+1:]...) {
|
||||||
v = d.call(p, v, path[k+1:])
|
|
||||||
}
|
|
||||||
if !ConsumeWithFetch(v, d.call).Exists(path[k+1:]...) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,6 +445,11 @@ func (d *Doc) Exists(path ...string) bool {
|
||||||
|
|
||||||
// Get gets the value or values at a specified path.
|
// Get gets the value or values at a specified path.
|
||||||
func (d *Doc) Get(path ...string) *Doc {
|
func (d *Doc) Get(path ...string) *Doc {
|
||||||
|
return d.Fetch(nil, path...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch gets the value or values at a specified path, allowing for a custom fetch method.
|
||||||
|
func (d *Doc) Fetch(call Fetcher, path ...string) *Doc {
|
||||||
|
|
||||||
path = d.path(path...)
|
path = d.path(path...)
|
||||||
|
|
||||||
|
@ -494,8 +482,8 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
if m, ok := object.(map[string]interface{}); ok {
|
if m, ok := object.(map[string]interface{}); ok {
|
||||||
switch p {
|
switch p {
|
||||||
default:
|
default:
|
||||||
if d.call != nil {
|
if call != nil {
|
||||||
object = d.call(p, m[p], path[k+1:])
|
object = call(p, m[p], path[k+1:])
|
||||||
} else {
|
} else {
|
||||||
object = m[p]
|
object = m[p]
|
||||||
}
|
}
|
||||||
|
@ -518,19 +506,19 @@ func (d *Doc) Get(path ...string) *Doc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == one {
|
if r == one {
|
||||||
if d.call != nil {
|
if call != nil {
|
||||||
c[0] = d.call(p, c[0], path[k+1:])
|
c[0] = call(p, c[0], path[k+1:])
|
||||||
}
|
}
|
||||||
return ConsumeWithFetch(c[0], d.call).Get(path[k+1:]...)
|
return Consume(c[0]).Fetch(call, path[k+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r == many {
|
if r == many {
|
||||||
out := []interface{}{}
|
out := []interface{}{}
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
if d.call != nil {
|
if call != nil {
|
||||||
v = d.call(p, v, path[k+1:])
|
v = call(p, v, path[k+1:])
|
||||||
}
|
}
|
||||||
res := ConsumeWithFetch(v, d.call).Get(path[k+1:]...)
|
res := Consume(v).Fetch(call, path[k+1:]...)
|
||||||
out = append(out, res.data)
|
out = append(out, res.data)
|
||||||
}
|
}
|
||||||
return &Doc{data: out}
|
return &Doc{data: out}
|
||||||
|
@ -612,7 +600,7 @@ func (d *Doc) Set(value interface{}, path ...string) (*Doc, error) {
|
||||||
object = a[i[0]]
|
object = a[i[0]]
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
return ConsumeWithFetch(a[i[0]], d.call).Set(value, path[k+1:]...)
|
return Consume(a[i[0]]).Set(value, path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +611,7 @@ func (d *Doc) Set(value interface{}, path ...string) (*Doc, error) {
|
||||||
a[i[j]] = value
|
a[i[j]] = value
|
||||||
out = append(out, value)
|
out = append(out, value)
|
||||||
} else {
|
} else {
|
||||||
res, _ := ConsumeWithFetch(v, d.call).Set(value, path[k+1:]...)
|
res, _ := Consume(v).Set(value, path[k+1:]...)
|
||||||
if res.data != nil {
|
if res.data != nil {
|
||||||
out = append(out, res.data)
|
out = append(out, res.data)
|
||||||
}
|
}
|
||||||
|
@ -704,7 +692,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
if len(c) != 0 {
|
if len(c) != 0 {
|
||||||
return ConsumeWithFetch(c[0], d.call).Del(path[k+1:]...)
|
return Consume(c[0]).Del(path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -715,7 +703,7 @@ func (d *Doc) Del(path ...string) error {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
ConsumeWithFetch(v, d.call).Del(path[k+1:]...)
|
Consume(v).Del(path[k+1:]...)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -772,7 +760,7 @@ func (d *Doc) ArrayAdd(value interface{}, path ...string) (*Doc, error) {
|
||||||
} else {
|
} else {
|
||||||
for _, v := range a {
|
for _, v := range a {
|
||||||
if reflect.DeepEqual(v, value) {
|
if reflect.DeepEqual(v, value) {
|
||||||
return ConsumeWithFetch(a, d.call), nil
|
return Consume(a), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a = append(a, value)
|
a = append(a, value)
|
||||||
|
@ -980,7 +968,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
|
||||||
var keep []string
|
var keep []string
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, k)
|
keep = append(keep, k)
|
||||||
ConsumeWithFetch(v, d.call).each(exec, keep)
|
Consume(v).each(exec, keep)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -995,7 +983,7 @@ func (d *Doc) each(exec Iterator, prev []string) error {
|
||||||
var keep []string
|
var keep []string
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i))
|
keep = append(keep, fmt.Sprintf("[%d]", i))
|
||||||
ConsumeWithFetch(v, d.call).each(exec, keep)
|
Consume(v).each(exec, keep)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1078,7 +1066,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, path[:k]...)
|
keep = append(keep, path[:k]...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
|
keep = append(keep, fmt.Sprintf("[%d]", i[0]))
|
||||||
return ConsumeWithFetch(c[0], d.call).walk(exec, keep, path[k+1:]...)
|
return Consume(c[0]).walk(exec, keep, path[k+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,7 +1085,7 @@ func (d *Doc) walk(exec Iterator, prev []string, path ...string) error {
|
||||||
keep = append(keep, prev...)
|
keep = append(keep, prev...)
|
||||||
keep = append(keep, path[:k]...)
|
keep = append(keep, path[:k]...)
|
||||||
keep = append(keep, fmt.Sprintf("[%d]", i[j]))
|
keep = append(keep, fmt.Sprintf("[%d]", i[j]))
|
||||||
if err := ConsumeWithFetch(v, d.call).walk(exec, keep, path[k+1:]...); err != nil {
|
if err := Consume(v).walk(exec, keep, path[k+1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue