github.com/rjgonzale/pop/v5@v5.1.3-dev/dialect_common.go (about) 1 package pop 2 3 import ( 4 "bytes" 5 "database/sql" 6 "encoding/gob" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "strings" 13 14 "github.com/gobuffalo/pop/v5/columns" 15 "github.com/gobuffalo/pop/v5/logging" 16 "github.com/gofrs/uuid" 17 "github.com/jmoiron/sqlx" 18 "github.com/pkg/errors" 19 ) 20 21 func init() { 22 gob.Register(uuid.UUID{}) 23 } 24 25 type commonDialect struct { 26 ConnectionDetails *ConnectionDetails 27 } 28 29 func (commonDialect) Lock(fn func() error) error { 30 return fn() 31 } 32 33 func (commonDialect) Quote(key string) string { 34 parts := strings.Split(key, ".") 35 36 for i, part := range parts { 37 part = strings.Trim(part, `"`) 38 part = strings.TrimSpace(part) 39 40 parts[i] = fmt.Sprintf(`"%v"`, part) 41 } 42 43 return strings.Join(parts, ".") 44 } 45 46 func genericCreate(s store, model *Model, cols columns.Columns, quoter quotable) error { 47 keyType := model.PrimaryKeyType() 48 switch keyType { 49 case "int", "int64": 50 var id int64 51 w := cols.Writeable() 52 query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", quoter.Quote(model.TableName()), w.QuotedString(quoter), w.SymbolizedString()) 53 log(logging.SQL, query) 54 res, err := s.NamedExec(query, model.Value) 55 if err != nil { 56 return err 57 } 58 id, err = res.LastInsertId() 59 if err == nil { 60 model.setID(id) 61 } 62 if err != nil { 63 return err 64 } 65 return nil 66 case "UUID", "string": 67 if keyType == "UUID" { 68 if model.ID() == emptyUUID { 69 u, err := uuid.NewV4() 70 if err != nil { 71 return err 72 } 73 model.setID(u) 74 } 75 } else if model.ID() == "" { 76 return fmt.Errorf("missing ID value") 77 } 78 w := cols.Writeable() 79 w.Add("id") 80 query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", quoter.Quote(model.TableName()), w.QuotedString(quoter), w.SymbolizedString()) 81 log(logging.SQL, query) 82 stmt, err := s.PrepareNamed(query) 83 if err != nil { 84 return err 85 } 86 _, err = stmt.Exec(model.Value) 87 if err != nil { 88 if err := stmt.Close(); err != nil { 89 return errors.WithMessage(err, "failed to close statement") 90 } 91 return err 92 } 93 return errors.WithMessage(stmt.Close(), "failed to close statement") 94 } 95 return errors.Errorf("can not use %s as a primary key type!", keyType) 96 } 97 98 func genericUpdate(s store, model *Model, cols columns.Columns, quoter quotable) error { 99 stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s", quoter.Quote(model.TableName()), cols.Writeable().QuotedUpdateString(quoter), model.whereNamedID()) 100 log(logging.SQL, stmt, model.ID()) 101 _, err := s.NamedExec(stmt, model.Value) 102 if err != nil { 103 return err 104 } 105 return nil 106 } 107 108 func genericDestroy(s store, model *Model, quoter quotable) error { 109 stmt := fmt.Sprintf("DELETE FROM %s WHERE %s", quoter.Quote(model.TableName()), model.whereID()) 110 _, err := genericExec(s, stmt, model.ID()) 111 if err != nil { 112 return err 113 } 114 return nil 115 } 116 117 func genericExec(s store, stmt string, args ...interface{}) (sql.Result, error) { 118 log(logging.SQL, stmt, args...) 119 res, err := s.Exec(stmt, args...) 120 return res, err 121 } 122 123 func genericSelectOne(s store, model *Model, query Query) error { 124 sqlQuery, args := query.ToSQL(model) 125 log(logging.SQL, sqlQuery, args...) 126 err := s.Get(model.Value, sqlQuery, args...) 127 if err != nil { 128 return err 129 } 130 return nil 131 } 132 133 func genericSelectMany(s store, models *Model, query Query) error { 134 sqlQuery, args := query.ToSQL(models) 135 log(logging.SQL, sqlQuery, args...) 136 err := s.Select(models.Value, sqlQuery, args...) 137 if err != nil { 138 return err 139 } 140 return nil 141 } 142 143 func genericLoadSchema(deets *ConnectionDetails, migrationURL string, r io.Reader) error { 144 // Open DB connection on the target DB 145 db, err := sqlx.Open(deets.Dialect, migrationURL) 146 if err != nil { 147 return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database)) 148 } 149 defer db.Close() 150 151 // Get reader contents 152 contents, err := ioutil.ReadAll(r) 153 if err != nil { 154 return err 155 } 156 157 if len(contents) == 0 { 158 log(logging.Info, "schema is empty for %s, skipping", deets.Database) 159 return nil 160 } 161 162 _, err = db.Exec(string(contents)) 163 if err != nil { 164 return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database)) 165 } 166 167 log(logging.Info, "loaded schema for %s", deets.Database) 168 return nil 169 } 170 171 func genericDumpSchema(deets *ConnectionDetails, cmd *exec.Cmd, w io.Writer) error { 172 log(logging.SQL, strings.Join(cmd.Args, " ")) 173 174 bb := &bytes.Buffer{} 175 mw := io.MultiWriter(w, bb) 176 177 cmd.Stdout = mw 178 cmd.Stderr = os.Stderr 179 180 err := cmd.Run() 181 if err != nil { 182 return err 183 } 184 185 x := bytes.TrimSpace(bb.Bytes()) 186 if len(x) == 0 { 187 return errors.Errorf("unable to dump schema for %s", deets.Database) 188 } 189 190 log(logging.Info, "dumped schema for %s", deets.Database) 191 return nil 192 }