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