-
Notifications
You must be signed in to change notification settings - Fork 5
/
connection.go
153 lines (134 loc) · 4.25 KB
/
connection.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package orm
import (
"database/sql"
"fmt"
"github.com/jedib0t/go-pretty/table"
)
type connection struct {
Name string
Dialect *Dialect
DB *sql.DB
Schemas map[string]*schema
DBSchema map[string][]columnSpec
DatabaseValidations bool
}
func (c *connection) inferedTables() []string {
var tables []string
for t, s := range c.Schemas {
tables = append(tables, t)
for _, relC := range s.relations {
if belongsToManyConfig, is := relC.(BelongsToManyConfig); is {
tables = append(tables, belongsToManyConfig.IntermediateTable)
}
}
}
return tables
}
func (c *connection) validateAllTablesArePresent() error {
for _, inferedTable := range c.inferedTables() {
if _, exists := c.DBSchema[inferedTable]; !exists {
return fmt.Errorf("orm infered %s but it's not found in your database, your database is out of sync", inferedTable)
}
}
return nil
}
func (c *connection) validateTablesSchemas() error {
// check for entity tables: there should not be any struct field that does not have a coresponding column
for table, sc := range c.Schemas {
if columns, exists := c.DBSchema[table]; exists {
for _, f := range sc.fields {
found := false
for _, c := range columns {
if c.Name == f.Name {
found = true
}
}
if !found {
return fmt.Errorf("column %s not found while it was inferred", f.Name)
}
}
} else {
return fmt.Errorf("tables are out of sync, %s was inferred but not present in database", table)
}
}
// check for relation tables: for HasMany,HasOne relations check if OWNER pk column is in PROPERTY,
// for BelongsToMany check intermediate table has 2 pk for two entities
for table, sc := range c.Schemas {
for _, rel := range sc.relations {
switch rel.(type) {
case BelongsToConfig:
columns := c.DBSchema[table]
var found bool
for _, col := range columns {
if col.Name == rel.(BelongsToConfig).LocalForeignKey {
found = true
}
}
if !found {
return fmt.Errorf("cannot find local foreign key %s for relation", rel.(BelongsToConfig).LocalForeignKey)
}
case BelongsToManyConfig:
columns := c.DBSchema[rel.(BelongsToManyConfig).IntermediateTable]
var foundOwner bool
var foundProperty bool
for _, col := range columns {
if col.Name == rel.(BelongsToManyConfig).IntermediateOwnerID {
foundOwner = true
}
if col.Name == rel.(BelongsToManyConfig).IntermediatePropertyID {
foundProperty = true
}
}
if !foundOwner || !foundProperty {
return fmt.Errorf("table schema for %s is not correct one of foreign keys is not present", rel.(BelongsToManyConfig).IntermediateTable)
}
}
}
}
return nil
}
func (c *connection) Schematic() {
fmt.Printf("SQL Dialect: %s\n", c.Dialect.DriverName)
for t, schema := range c.Schemas {
fmt.Printf("t: %s\n", t)
w := table.NewWriter()
w.AppendHeader(table.Row{"SQL Name", "Type", "Is Primary Key", "Is Virtual"})
for _, field := range schema.fields {
w.AppendRow(table.Row{field.Name, field.Type, field.IsPK, field.Virtual})
}
fmt.Println(w.Render())
for _, rel := range schema.relations {
switch rel.(type) {
case HasOneConfig:
fmt.Printf("%s 1-1 %s => %+v\n", t, rel.(HasOneConfig).PropertyTable, rel)
case HasManyConfig:
fmt.Printf("%s 1-N %s => %+v\n", t, rel.(HasManyConfig).PropertyTable, rel)
case BelongsToConfig:
fmt.Printf("%s N-1 %s => %+v\n", t, rel.(BelongsToConfig).OwnerTable, rel)
case BelongsToManyConfig:
fmt.Printf("%s N-N %s => %+v\n", t, rel.(BelongsToManyConfig).IntermediateTable, rel)
}
}
fmt.Println("")
}
}
func (c *connection) getSchema(t string) *schema {
return c.Schemas[t]
}
func (c *connection) setSchema(e Entity, s *schema) {
var configurator EntityConfigurator
e.ConfigureEntity(&configurator)
c.Schemas[configurator.table] = s
}
func GetConnection(name string) *connection {
return globalConnections[name]
}
func (c *connection) exec(q string, args ...any) (sql.Result, error) {
return c.DB.Exec(q, args...)
}
func (c *connection) query(q string, args ...any) (*sql.Rows, error) {
return c.DB.Query(q, args...)
}
func (c *connection) queryRow(q string, args ...any) *sql.Row {
return c.DB.QueryRow(q, args...)
}