diff --git a/api/datastore/sql/sql.go b/api/datastore/sql/sql.go index 2d8844f9e..70f88b392 100644 --- a/api/datastore/sql/sql.go +++ b/api/datastore/sql/sql.go @@ -72,9 +72,22 @@ const ( type sqlStore struct { db *sqlx.DB - - // TODO we should prepare all of the statements, rebind them - // and store them all here. + insertAppQuery string + updateAppQuery string + getAppQuery string + selectAppConfigQuery string + removeAppQuery string + insertRouteQuery string + updateRouteQuery string + checkRouteAppQuery string + checkRouteQuery string + getRouteQuery string + removeRouteQuery string + insertCallQuery string + getCallQuery string + insertLogQuery string + getLogQuery string + deleteLogQuery string } // New will open the db specified by url, create any tables necessary @@ -133,7 +146,56 @@ func New(url *url.URL) (models.Datastore, error) { } } - return &sqlStore{db: db}, nil + dstore := &sqlStore{ + db: db, + insertAppQuery: db.Rebind("INSERT INTO apps (name, config) VALUES (?, ?);"), + selectAppConfigQuery: db.Rebind(`SELECT config FROM apps WHERE name=?`), + updateAppQuery: db.Rebind(`UPDATE apps SET config=? WHERE name=?`), + removeAppQuery: db.Rebind(`DELETE FROM apps WHERE name = ?`), + getAppQuery: db.Rebind(`SELECT name, config FROM apps WHERE name=?`), + checkRouteAppQuery: db.Rebind(`SELECT 1 FROM apps WHERE name=?`), + checkRouteQuery: db.Rebind(`SELECT 1 FROM routes WHERE app_name=? AND path=?`), + insertRouteQuery: db.Rebind(`INSERT INTO routes ( + app_name, + path, + image, + format, + memory, + type, + timeout, + idle_timeout, + headers, + config + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`), + getRouteQuery: db.Rebind(fmt.Sprintf("%s WHERE app_name=? AND path=?", routeSelector)), + updateRouteQuery: db.Rebind(`UPDATE routes SET + image = ?, + format = ?, + memory = ?, + type = ?, + timeout = ?, + idle_timeout = ?, + headers = ?, + config = ? + WHERE app_name=? AND path=?;`), + removeRouteQuery: db.Rebind(`DELETE FROM routes WHERE path = ? AND app_name = ?`), + insertCallQuery: db.Rebind(`INSERT INTO calls ( + id, + created_at, + started_at, + completed_at, + status, + app_name, + path + ) + VALUES (?, ?, ?, ?, ?, ?, ?);`), + getCallQuery: db.Rebind(fmt.Sprintf(`%s WHERE id=? AND app_name=?`, callSelector)), + insertLogQuery: db.Rebind(`INSERT INTO logs (id, log) VALUES (?, ?);`), + getLogQuery: db.Rebind(`SELECT log FROM logs WHERE id=?`), + deleteLogQuery: db.Rebind(`DELETE FROM logs WHERE id=?`), + } + return dstore, nil } func (ds *sqlStore) InsertApp(ctx context.Context, app *models.App) (*models.App, error) { @@ -146,8 +208,7 @@ func (ds *sqlStore) InsertApp(ctx context.Context, app *models.App) (*models.App } } - query := ds.db.Rebind("INSERT INTO apps (name, config) VALUES (?, ?);") - _, err = ds.db.Exec(query, app.Name, string(cbyte)) + _, err = ds.db.ExecContext(ctx, ds.insertAppQuery, app.Name, string(cbyte)) if err != nil { switch err := err.(type) { case *mysql.MySQLError: @@ -172,8 +233,7 @@ func (ds *sqlStore) InsertApp(ctx context.Context, app *models.App) (*models.App func (ds *sqlStore) UpdateApp(ctx context.Context, newapp *models.App) (*models.App, error) { app := &models.App{Name: newapp.Name} err := ds.Tx(func(tx *sqlx.Tx) error { - query := tx.Rebind(`SELECT config FROM apps WHERE name=?`) - row := tx.QueryRow(query, app.Name) + row := tx.QueryRowContext(ctx, ds.selectAppConfigQuery, app.Name) var config string if err := row.Scan(&config); err != nil { @@ -197,8 +257,7 @@ func (ds *sqlStore) UpdateApp(ctx context.Context, newapp *models.App) (*models. return err } - query = tx.Rebind(`UPDATE apps SET config=? WHERE name=?`) - res, err := tx.Exec(query, string(cbyte), app.Name) + res, err := tx.ExecContext(ctx, ds.updateAppQuery, string(cbyte), app.Name) if err != nil { return err } @@ -220,14 +279,12 @@ func (ds *sqlStore) UpdateApp(ctx context.Context, newapp *models.App) (*models. } func (ds *sqlStore) RemoveApp(ctx context.Context, appName string) error { - query := ds.db.Rebind(`DELETE FROM apps WHERE name = ?`) - _, err := ds.db.Exec(query, appName) + _, err := ds.db.ExecContext(ctx, ds.removeAppQuery, appName) return err } func (ds *sqlStore) GetApp(ctx context.Context, name string) (*models.App, error) { - query := ds.db.Rebind(`SELECT name, config FROM apps WHERE name=?`) - row := ds.db.QueryRow(query, name) + row := ds.db.QueryRowContext(ctx, ds.getAppQuery, name) var resName, config string err := row.Scan(&resName, &config) @@ -257,7 +314,7 @@ func (ds *sqlStore) GetApps(ctx context.Context, filter *models.AppFilter) ([]*m res := []*models.App{} query, args := buildFilterAppQuery(filter) query = ds.db.Rebind(fmt.Sprintf("SELECT DISTINCT name, config FROM apps %s", query)) - rows, err := ds.db.Query(query, args...) + rows, err := ds.db.QueryContext(ctx, query, args...) if err != nil { return nil, err } @@ -294,15 +351,13 @@ func (ds *sqlStore) InsertRoute(ctx context.Context, route *models.Route) (*mode } err = ds.Tx(func(tx *sqlx.Tx) error { - query := tx.Rebind(`SELECT 1 FROM apps WHERE name=?`) - r := tx.QueryRow(query, route.AppName) + r := tx.QueryRowContext(ctx, ds.checkRouteAppQuery, route.AppName) if err := r.Scan(new(int)); err != nil { if err == sql.ErrNoRows { return models.ErrAppsNotFound } } - query = tx.Rebind(`SELECT 1 FROM routes WHERE app_name=? AND path=?`) - same, err := tx.Query(query, route.AppName, route.Path) + same, err := tx.QueryContext(ctx, ds.checkRouteQuery, route.AppName, route.Path) if err != nil { return err } @@ -311,21 +366,7 @@ func (ds *sqlStore) InsertRoute(ctx context.Context, route *models.Route) (*mode return models.ErrRoutesAlreadyExists } - query = tx.Rebind(`INSERT INTO routes ( - app_name, - path, - image, - format, - memory, - type, - timeout, - idle_timeout, - headers, - config - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`) - - _, err = tx.Exec(query, + _, err = tx.ExecContext(ctx, ds.insertRouteQuery, route.AppName, route.Path, route.Image, @@ -347,8 +388,7 @@ func (ds *sqlStore) InsertRoute(ctx context.Context, route *models.Route) (*mode func (ds *sqlStore) UpdateRoute(ctx context.Context, newroute *models.Route) (*models.Route, error) { var route models.Route err := ds.Tx(func(tx *sqlx.Tx) error { - query := tx.Rebind(fmt.Sprintf("%s WHERE app_name=? AND path=?", routeSelector)) - row := tx.QueryRow(query, newroute.AppName, newroute.Path) + row := tx.QueryRowContext(ctx, ds.getRouteQuery, newroute.AppName, newroute.Path) if err := scanRoute(row, &route); err == sql.ErrNoRows { return models.ErrRoutesNotFound } else if err != nil { @@ -367,18 +407,7 @@ func (ds *sqlStore) UpdateRoute(ctx context.Context, newroute *models.Route) (*m return err } - query = tx.Rebind(`UPDATE routes SET - image = ?, - format = ?, - memory = ?, - type = ?, - timeout = ?, - idle_timeout = ?, - headers = ?, - config = ? - WHERE app_name=? AND path=?;`) - - res, err := tx.Exec(query, + res, err := tx.ExecContext(ctx, ds.updateAppQuery, route.Image, route.Format, route.Memory, @@ -412,8 +441,7 @@ func (ds *sqlStore) UpdateRoute(ctx context.Context, newroute *models.Route) (*m } func (ds *sqlStore) RemoveRoute(ctx context.Context, appName, routePath string) error { - query := ds.db.Rebind(`DELETE FROM routes WHERE path = ? AND app_name = ?`) - res, err := ds.db.Exec(query, routePath, appName) + res, err := ds.db.ExecContext(ctx, ds.removeRouteQuery, routePath, appName) if err != nil { return err } @@ -431,9 +459,7 @@ func (ds *sqlStore) RemoveRoute(ctx context.Context, appName, routePath string) } func (ds *sqlStore) GetRoute(ctx context.Context, appName, routePath string) (*models.Route, error) { - rSelectCondition := "%s WHERE app_name=? AND path=?" - query := ds.db.Rebind(fmt.Sprintf(rSelectCondition, routeSelector)) - row := ds.db.QueryRow(query, appName, routePath) + row := ds.db.QueryRowContext(ctx, ds.getRouteQuery, appName, routePath) var route models.Route err := scanRoute(row, &route) @@ -449,9 +475,8 @@ func (ds *sqlStore) GetRoute(ctx context.Context, appName, routePath string) (*m func (ds *sqlStore) GetRoutes(ctx context.Context, filter *models.RouteFilter) ([]*models.Route, error) { res := []*models.Route{} query, args := buildFilterRouteQuery(filter) - query = fmt.Sprintf("%s %s", routeSelector, query) - query = ds.db.Rebind(query) - rows, err := ds.db.Query(query, args...) + query = ds.db.Rebind(fmt.Sprintf("%s %s", routeSelector, query)) + rows, err := ds.db.QueryContext(ctx, query, args...) // todo: check for no rows so we don't respond with a sql 500 err if err != nil { return nil, err @@ -490,7 +515,7 @@ func (ds *sqlStore) GetRoutesByApp(ctx context.Context, appName string, filter * query := fmt.Sprintf("%s %s", routeSelector, filterQuery) query = ds.db.Rebind(query) - rows, err := ds.db.Query(query, args...) + rows, err := ds.db.QueryContext(ctx, query, args...) // todo: check for no rows so we don't respond with a sql 500 err if err != nil { return nil, err @@ -527,18 +552,7 @@ func (ds *sqlStore) Tx(f func(*sqlx.Tx) error) error { } func (ds *sqlStore) InsertCall(ctx context.Context, call *models.Call) error { - query := ds.db.Rebind(`INSERT INTO calls ( - id, - created_at, - started_at, - completed_at, - status, - app_name, - path - ) - VALUES (?, ?, ?, ?, ?, ?, ?);`) - - _, err := ds.db.Exec(query, call.ID, call.CreatedAt.String(), + _, err := ds.db.ExecContext(ctx, ds.insertCallQuery, call.ID, call.CreatedAt.String(), call.StartedAt.String(), call.CompletedAt.String(), call.Status, call.AppName, call.Path) if err != nil { @@ -552,9 +566,7 @@ func (ds *sqlStore) InsertCall(ctx context.Context, call *models.Call) error { // if we store the whole thing then it adds a lot of disk space and then we can // make async only queue hints instead of entire calls (mq a lot smaller space wise). pick. func (ds *sqlStore) GetCall(ctx context.Context, appName, callID string) (*models.Call, error) { - query := fmt.Sprintf(`%s WHERE id=? AND app_name=?`, callSelector) - query = ds.db.Rebind(query) - row := ds.db.QueryRow(query, callID, appName) + row := ds.db.QueryRowContext(ctx, ds.getCallQuery, callID, appName) var call models.Call err := scanCall(row, &call) @@ -569,7 +581,7 @@ func (ds *sqlStore) GetCalls(ctx context.Context, filter *models.CallFilter) ([] query, args := buildFilterCallQuery(filter) query = fmt.Sprintf("%s %s", callSelector, query) query = ds.db.Rebind(query) - rows, err := ds.db.Query(query, args...) + rows, err := ds.db.QueryContext(ctx, query, args...) if err != nil { return nil, err } @@ -590,14 +602,12 @@ func (ds *sqlStore) GetCalls(ctx context.Context, filter *models.CallFilter) ([] } func (ds *sqlStore) InsertLog(ctx context.Context, callID, callLog string) error { - query := ds.db.Rebind(`INSERT INTO logs (id, log) VALUES (?, ?);`) - _, err := ds.db.Exec(query, callID, callLog) + _, err := ds.db.ExecContext(ctx, ds.insertLogQuery, callID, callLog) return err } func (ds *sqlStore) GetLog(ctx context.Context, callID string) (*models.CallLog, error) { - query := ds.db.Rebind(`SELECT log FROM logs WHERE id=?`) - row := ds.db.QueryRow(query, callID) + row := ds.db.QueryRowContext(ctx, ds.getLogQuery, callID) var log string err := row.Scan(&log) @@ -615,8 +625,7 @@ func (ds *sqlStore) GetLog(ctx context.Context, callID string) (*models.CallLog, } func (ds *sqlStore) DeleteLog(ctx context.Context, callID string) error { - query := ds.db.Rebind(`DELETE FROM logs WHERE id=?`) - _, err := ds.db.Exec(query, callID) + _, err := ds.db.ExecContext(ctx, ds.deleteLogQuery, callID) return err }