github.com/pjdufour-truss/pop@v4.11.2-0.20190705085848-4c90b0ff4d5a+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 err
    48  		}
    49  		id, err = res.LastInsertId()
    50  		if err == nil {
    51  			model.setID(id)
    52  		}
    53  		if err != nil {
    54  			return 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 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 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 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 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 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, err
   112  }
   113  
   114  func genericSelectOne(s store, model *Model, query Query) error {
   115  	sqlQuery, args := query.ToSQL(model)
   116  	log(logging.SQL, sqlQuery, args...)
   117  	err := s.Get(model.Value, sqlQuery, args...)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	return nil
   122  }
   123  
   124  func genericSelectMany(s store, models *Model, query Query) error {
   125  	sqlQuery, args := query.ToSQL(models)
   126  	log(logging.SQL, sqlQuery, args...)
   127  	err := s.Select(models.Value, sqlQuery, args...)
   128  	if err != nil {
   129  		return 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  }