github.com/cayleygraph/cayley@v0.7.7/graph/sql/database.go (about) 1 package sql 2 3 import ( 4 "database/sql" 5 "fmt" 6 "strings" 7 8 "github.com/cayleygraph/cayley/graph" 9 "github.com/cayleygraph/cayley/graph/log" 10 "github.com/cayleygraph/quad" 11 ) 12 13 var types = make(map[string]Registration) 14 15 func Register(name string, f Registration) { 16 if f.Driver == "" { 17 panic("no sql driver in type definition") 18 } 19 types[name] = f 20 21 registerQuadStore(name, name) 22 } 23 24 type Registration struct { 25 Driver string // sql driver to use on dial 26 HashType string // type for hash fields 27 BytesType string // type for binary fields 28 TimeType string // type for datetime fields 29 HorizonType string // type for horizon counter 30 NodesTableExtra string // extra SQL to append to nodes table definition 31 ConditionalIndexes bool // database supports conditional indexes 32 FillFactor bool // database supports fill percent on indexes 33 NoForeignKeys bool // database has no support for FKs 34 35 QueryDialect 36 NoOffsetWithoutLimit bool // SELECT ... OFFSET can be used only with LIMIT 37 38 Error func(error) error // error conversion function 39 Estimated func(table string) string // query that string that returns an estimated number of rows in table 40 RunTx func(tx *sql.Tx, nodes []graphlog.NodeUpdate, quads []graphlog.QuadUpdate, opts graph.IgnoreOpts) error 41 TxRetry func(tx *sql.Tx, stmts func() error) error 42 NoSchemaChangesInTx bool 43 } 44 45 func (r Registration) nodesTable() string { 46 htyp := r.HashType 47 if htyp == "" { 48 htyp = "BYTEA" 49 } 50 btyp := r.BytesType 51 if btyp == "" { 52 btyp = "BYTEA" 53 } 54 ttyp := r.TimeType 55 if ttyp == "" { 56 ttyp = "timestamp with time zone" 57 } 58 end := "\n);" 59 if r.NodesTableExtra != "" { 60 end = ",\n" + r.NodesTableExtra + end 61 } 62 return `CREATE TABLE nodes ( 63 hash ` + htyp + ` PRIMARY KEY, 64 refs INT NOT NULL, 65 value ` + btyp + `, 66 value_string TEXT, 67 datatype TEXT, 68 language TEXT, 69 iri BOOLEAN, 70 bnode BOOLEAN, 71 value_int BIGINT, 72 value_bool BOOLEAN, 73 value_float double precision, 74 value_time ` + ttyp + 75 end 76 } 77 78 func (r Registration) quadsTable() string { 79 htyp := r.HashType 80 if htyp == "" { 81 htyp = "BYTEA" 82 } 83 hztyp := r.HorizonType 84 if hztyp == "" { 85 hztyp = "SERIAL" 86 } 87 return `CREATE TABLE quads ( 88 horizon ` + hztyp + ` PRIMARY KEY, 89 subject_hash ` + htyp + ` NOT NULL, 90 predicate_hash ` + htyp + ` NOT NULL, 91 object_hash ` + htyp + ` NOT NULL, 92 label_hash ` + htyp + `, 93 ts timestamp 94 );` 95 } 96 97 func (r Registration) quadIndexes(options graph.Options) []string { 98 indexes := make([]string, 0, 10) 99 if r.ConditionalIndexes { 100 indexes = append(indexes, 101 `CREATE UNIQUE INDEX spo_unique ON quads (subject_hash, predicate_hash, object_hash) WHERE label_hash IS NULL;`, 102 `CREATE UNIQUE INDEX spol_unique ON quads (subject_hash, predicate_hash, object_hash, label_hash) WHERE label_hash IS NOT NULL;`, 103 ) 104 } else { 105 indexes = append(indexes, 106 `CREATE UNIQUE INDEX spo_unique ON quads (subject_hash, predicate_hash, object_hash);`, 107 `CREATE UNIQUE INDEX spol_unique ON quads (subject_hash, predicate_hash, object_hash, label_hash);`, 108 ) 109 } 110 if !r.NoForeignKeys { 111 indexes = append(indexes, 112 `ALTER TABLE quads ADD CONSTRAINT subject_hash_fk FOREIGN KEY (subject_hash) REFERENCES nodes (hash);`, 113 `ALTER TABLE quads ADD CONSTRAINT predicate_hash_fk FOREIGN KEY (predicate_hash) REFERENCES nodes (hash);`, 114 `ALTER TABLE quads ADD CONSTRAINT object_hash_fk FOREIGN KEY (object_hash) REFERENCES nodes (hash);`, 115 `ALTER TABLE quads ADD CONSTRAINT label_hash_fk FOREIGN KEY (label_hash) REFERENCES nodes (hash);`, 116 ) 117 } 118 quadIndexes := [][3]quad.Direction{ 119 {quad.Subject, quad.Predicate, quad.Object}, 120 {quad.Object, quad.Predicate, quad.Subject}, 121 {quad.Predicate, quad.Object, quad.Subject}, 122 {quad.Object, quad.Subject, quad.Predicate}, 123 } 124 factor, _ := options.IntKey("db_fill_factor", 50) 125 for _, ind := range quadIndexes { 126 var ( 127 name string 128 cols []string 129 ) 130 for _, d := range ind { 131 name += string(d.Prefix()) 132 cols = append(cols, d.String()+"_hash") 133 } 134 q := fmt.Sprintf(`CREATE INDEX %s_index ON quads (%s)`, 135 name, strings.Join(cols, ", ")) 136 if r.FillFactor { 137 q += fmt.Sprintf(" WITH (FILLFACTOR = %d)", factor) 138 } 139 indexes = append(indexes, q+";") 140 } 141 return indexes 142 }