github.com/solongordon/pop@v4.10.0+incompatible/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/columns" 15 "github.com/gobuffalo/pop/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 return fmt.Sprintf(`"%s"`, key) 35 } 36 37 func genericCreate(s store, model *Model, cols columns.Columns) error { 38 keyType := model.PrimaryKeyType() 39 switch keyType { 40 case "int", "int64": 41 var id int64 42 w := cols.Writeable() 43 query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", model.TableName(), w.String(), w.SymbolizedString()) 44 log(logging.SQL, query) 45 res, err := s.NamedExec(query, model.Value) 46 if err != nil { 47 return errors.WithStack(err) 48 } 49 id, err = res.LastInsertId() 50 if err == nil { 51 model.setID(id) 52 } 53 if err != nil { 54 return errors.WithStack(err) 55 } 56 return nil 57 case "UUID", "string": 58 if keyType == "UUID" { 59 if model.ID() == emptyUUID { 60 u, err := uuid.NewV4() 61 if err != nil { 62 return errors.WithStack(err) 63 } 64 model.setID(u) 65 } 66 } else if model.ID() == "" { 67 return fmt.Errorf("missing ID value") 68 } 69 w := cols.Writeable() 70 w.Add("id") 71 query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", model.TableName(), w.String(), w.SymbolizedString()) 72 log(logging.SQL, query) 73 stmt, err := s.PrepareNamed(query) 74 if err != nil { 75 return errors.WithStack(err) 76 } 77 _, err = stmt.Exec(model.Value) 78 if err != nil { 79 if err := stmt.Close(); err != nil { 80 return errors.WithMessage(err, "failed to close statement") 81 } 82 return errors.WithStack(err) 83 } 84 return errors.WithMessage(stmt.Close(), "failed to close statement") 85 } 86 return errors.Errorf("can not use %s as a primary key type!", keyType) 87 } 88 89 func genericUpdate(s store, model *Model, cols columns.Columns) error { 90 stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s", model.TableName(), cols.Writeable().UpdateString(), model.whereNamedID()) 91 log(logging.SQL, stmt, model.ID()) 92 _, err := s.NamedExec(stmt, model.Value) 93 if err != nil { 94 return errors.WithStack(err) 95 } 96 return nil 97 } 98 99 func genericDestroy(s store, model *Model) error { 100 stmt := fmt.Sprintf("DELETE FROM %s WHERE %s", model.TableName(), model.whereID()) 101 _, err := genericExec(s, stmt, model.ID()) 102 if err != nil { 103 return errors.WithStack(err) 104 } 105 return nil 106 } 107 108 func genericExec(s store, stmt string, args ...interface{}) (sql.Result, error) { 109 log(logging.SQL, stmt, args...) 110 res, err := s.Exec(stmt, args...) 111 return res, errors.WithStack(err) 112 } 113 114 func genericSelectOne(s store, model *Model, query Query) error { 115 sql, args := query.ToSQL(model) 116 log(logging.SQL, sql, args...) 117 err := s.Get(model.Value, sql, args...) 118 if err != nil { 119 return errors.WithStack(err) 120 } 121 return nil 122 } 123 124 func genericSelectMany(s store, models *Model, query Query) error { 125 sql, args := query.ToSQL(models) 126 log(logging.SQL, sql, args...) 127 err := s.Select(models.Value, sql, args...) 128 if err != nil { 129 return errors.WithStack(err) 130 } 131 return nil 132 } 133 134 func genericLoadSchema(deets *ConnectionDetails, migrationURL string, r io.Reader) error { 135 // Open DB connection on the target DB 136 db, err := sqlx.Open(deets.Dialect, migrationURL) 137 if err != nil { 138 return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database)) 139 } 140 defer db.Close() 141 142 // Get reader contents 143 contents, err := ioutil.ReadAll(r) 144 if err != nil { 145 return err 146 } 147 148 if len(contents) == 0 { 149 log(logging.Info, "schema is empty for %s, skipping", deets.Database) 150 return nil 151 } 152 153 _, err = db.Exec(string(contents)) 154 if err != nil { 155 return errors.WithMessage(err, fmt.Sprintf("unable to load schema for %s", deets.Database)) 156 } 157 158 log(logging.Info, "loaded schema for %s", deets.Database) 159 return nil 160 } 161 162 func genericDumpSchema(deets *ConnectionDetails, cmd *exec.Cmd, w io.Writer) error { 163 log(logging.SQL, strings.Join(cmd.Args, " ")) 164 165 bb := &bytes.Buffer{} 166 mw := io.MultiWriter(w, bb) 167 168 cmd.Stdout = mw 169 cmd.Stderr = os.Stderr 170 171 err := cmd.Run() 172 if err != nil { 173 return err 174 } 175 176 x := bytes.TrimSpace(bb.Bytes()) 177 if len(x) == 0 { 178 return errors.Errorf("unable to dump schema for %s", deets.Database) 179 } 180 181 log(logging.Info, "dumped schema for %s", deets.Database) 182 return nil 183 }