#952·cayley

`LoadToDepth` from `schema` with property on depth 0 and depth 1 crashes

Author: oxistoCreated May 19, 2021Updated May 19, 2021

Description

I am trying to build up an ontology of a service, its endpoint and a TLS configuration and trying to persist it into Cayley and then load it again. If I try to load it again into objects, there seems to be some kind of type confusion between objects on depth 0 and depth 1, resulting into the crash below.

go
package main

import (
	"context"
	"fmt"
	"log"
	"math/rand"

	"github.com/cayleygraph/cayley"
	"github.com/cayleygraph/cayley/graph"
	"github.com/cayleygraph/cayley/schema"
	"github.com/cayleygraph/quad"
	"github.com/cayleygraph/quad/voc"
)

type TlsConfiguration struct {
	rdfType struct{} `quad:"@type > ex:TlsConfig"`

	Version string `quad:"ex:version"`
}

type HttpEndpoint struct {
	rdfType struct{} `quad:"@type > ex:Http"`

	URL       string           `quad:"ex:url"`
	TlsConfig TlsConfiguration `quad:"ex:tls"`
}

type Category struct {
	Name string `quad:"ex:name"`
}

type Service struct {
	rdfType struct{} `quad:"@type > ex:Service"`

	ID           quad.IRI     `json:"@id"`
	HttpEndpoint HttpEndpoint `quad:"ex:http"`
	Category     Category     `quad:"ex:category"`
}

func main() {
	store, err := cayley.NewMemoryGraph()
	if err != nil {
		log.Fatalln(err)
	}

	voc.RegisterPrefix("ex:", "https://example.io")

	sch := schema.NewConfig()
	// Override a function to generate IDs. Can be changed to generate UUIDs, for example.
	sch.GenerateID = func(_ interface{}) quad.Value {
		return quad.BNode(fmt.Sprintf("node%d", rand.Intn(1000)))
	}

	qw := graph.NewWriter(store)

	s1 := &Service{
		ID: quad.IRI("s1"),
		HttpEndpoint: HttpEndpoint{
			URL: "https://example.io",
			TlsConfig: TlsConfiguration{
				Version: "1.2",
			},
		},
		Category: Category{Name: "An example service"},
	}

	sch.WriteAsQuads(qw, s1)

	s2 := &Service{
		ID: quad.IRI("s2"),
		HttpEndpoint: HttpEndpoint{
			URL: "https://cayley.io",
			TlsConfig: TlsConfiguration{
				Version: "1.3",
			},
		},
		Category: Category{Name: "A graph service"},
	}

	sch.WriteAsQuads(qw, s2)

	qw.Close()

	// Print quads
	fmt.Println("\nquads:")
	it := store.QuadsAllIterator().Iterate()
	defer it.Close()

	for it.Next(context.TODO()) {
		fmt.Println(store.Quad(it.Result()))
	}

	var services []Service
	sch.LoadToDepth(context.TODO(), store, &services, 1) // depth=0 will work, 1 will crash

	for _, v := range services {
		fmt.Printf("%+v\n", v)
	}
}

Steps to reproduce the issue:

  1. Build up a schema with objects on depth 0 and depth 1
  2. Store it
  3. Load it

Received results:

<s1> -- <rdf:type> -> <ex:Actor>
_:node81 -- <rdf:type> -> <ex:Http>
_:node81 -- <ex:url> -> "https://example.io"
_:node887 -- <rdf:type> -> <ex:TlsConfig>
_:node887 -- <ex:version> -> "1.2"
_:node81 -- <ex:tls> -> _:node887
<s1> -- <ex:http> -> _:node81
_:node847 -- <ex:name> -> "An example service"
<s1> -- <ex:category> -> _:node847
<s2> -- <rdf:type> -> <ex:Actor>
_:node59 -- <rdf:type> -> <ex:Http>
_:node59 -- <ex:url> -> "https://cayley.io"
_:node81 -- <rdf:type> -> <ex:TlsConfig>
_:node81 -- <ex:version> -> "1.3"
_:node59 -- <ex:tls> -> _:node81
<s2> -- <ex:http> -> _:node59
_:node318 -- <ex:name> -> "A graph service"
<s2> -- <ex:category> -> _:node318

panic: reflect.Set: value of type main.HttpEndpoint is not assignable to type main.TlsConfiguration
goroutine 1 [running]:

reflect.Value.assignTo(0x100505fc0, 0x140001468a0, 0x199, 0x1004ce012, 0xb, 0x100500a60, 0x0, 0x0, 0x0, 0x0)
	/opt/homebrew/Cellar/go/1.16.3/libexec/src/reflect/value.go:2451 +0x3f0
reflect.Value.Set(0x100500a60, 0x140001055d0, 0x199, 0x100505fc0, 0x140001468a0, 0x199)
	/opt/homebrew/Cellar/go/1.16.3/libexec/src/reflect/value.go:1564 +0xa4
github.com/cayleygraph/cayley/schema.(*loader).loadIteratorToDepth(0x14000108a20, 0x100520230, 0x14000181b30, 0x100500a60, 0x140001055d0, 0x199, 0x0, 0x100520770, 0x140001217e8, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:470 +0x790
github.com/cayleygraph/cayley/schema.(*loader).loadToValue(0x14000108a20, 0x100520230, 0x14000181800, 0x100505fc0, 0x14000147320, 0x199, 0x1, 0x1400018ac48, 0x0, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:372 +0xe08
github.com/cayleygraph/cayley/schema.(*loader).loadIteratorToDepth(0x14000108a20, 0x100520230, 0x14000181800, 0x100505fc0, 0x14000147320, 0x199, 0x1, 0x100520770, 0x140001213f8, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:511 +0xd58
github.com/cayleygraph/cayley/schema.(*loader).loadToValue(0x14000108a20, 0x100520230, 0x140001096e0, 0x10050b840, 0x14000182780, 0x199, 0x2, 0x1400018b848, 0x0, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:372 +0xe08
github.com/cayleygraph/cayley/schema.(*loader).loadIteratorToDepth(0x14000108a20, 0x100520230, 0x140001096e0, 0x1004ef100, 0x140001203f0, 0x197, 0x2, 0x0, 0x0, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:511 +0xd58
github.com/cayleygraph/cayley/schema.(*Config).LoadIteratorToDepth(0x1400012c0c0, 0x1005201f8, 0x1400010e008, 0x100521398, 0x14000146000, 0x1004ea340, 0x140001203f0, 0x16, 0x2, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:142 +0xa8
github.com/cayleygraph/cayley/schema.(*Config).LoadToDepth(0x1400012c0c0, 0x1005201f8, 0x1400010e008, 0x100521398, 0x14000146000, 0x1004ea340, 0x140001203f0, 0x1, 0x0, 0x0, ...)
	/Users/oxisto/go/pkg/mod/github.com/cayleygraph/[email protected]/schema/loader.go:116 +0x240
main.main()
	/Users/oxisto/Downloads/cayley-test/main.go:95 +0x750

Expected results:

I expect it to work - or at least give me a proper error message why it does not.

Output of cayley version or commit hash:

github.com/cayleygraph/cayley v0.7.7-0.20210518204410-08381efb7f81

Environment details:

Backend database: in-memory