4f4a855d82
Reviewed-on: https://go-review.googlesource.com/c/158019 gotools/: * Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release. (GOTOOLS_TEST_TIMEOUT): Increase to 600. (check-runtime): Export LD_LIBRARY_PATH before computing GOARCH and GOOS. (check-vet): Copy golang.org/x/tools into check-vet-dir. * Makefile.in: Regenerate. gcc/testsuite/: * go.go-torture/execute/names-1.go: Stop using debug/xcoff, which is no longer externally visible. From-SVN: r268084
161 lines
3.9 KiB
Go
161 lines
3.9 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build ignore
|
|
|
|
package sql_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
func Example_openDBService() {
|
|
// Opening a driver typically will not attempt to connect to the database.
|
|
db, err := sql.Open("driver-name", "database=test1")
|
|
if err != nil {
|
|
// This will not be a connection error, but a DSN parse error or
|
|
// another initialization error.
|
|
log.Fatal(err)
|
|
}
|
|
db.SetConnMaxLifetime(0)
|
|
db.SetMaxIdleConns(50)
|
|
db.SetMaxOpenConns(50)
|
|
|
|
s := &Service{db: db}
|
|
|
|
http.ListenAndServe(":8080", s)
|
|
}
|
|
|
|
type Service struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
db := s.db
|
|
switch r.URL.Path {
|
|
default:
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
return
|
|
case "/healthz":
|
|
ctx, cancel := context.WithTimeout(r.Context(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
err := s.db.PingContext(ctx)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("db down: %v", err), http.StatusFailedDependency)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
case "/quick-action":
|
|
// This is a short SELECT. Use the request context as the base of
|
|
// the context timeout.
|
|
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
|
defer cancel()
|
|
|
|
id := 5
|
|
org := 10
|
|
var name string
|
|
err := db.QueryRowContext(ctx, `
|
|
select
|
|
p.name
|
|
from
|
|
people as p
|
|
join organization as o on p.organization = o.id
|
|
where
|
|
p.id = :id
|
|
and o.id = :org
|
|
;`,
|
|
sql.Named("id", id),
|
|
sql.Named("org", org),
|
|
).Scan(&name)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
io.WriteString(w, name)
|
|
return
|
|
case "/long-action":
|
|
// This is a long SELECT. Use the request context as the base of
|
|
// the context timeout, but give it some time to finish. If
|
|
// the client cancels before the query is done the query will also
|
|
// be canceled.
|
|
ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
var names []string
|
|
rows, err := db.QueryContext(ctx, "select p.name from people as p where p.active = true;")
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
for rows.Next() {
|
|
var name string
|
|
err = rows.Scan(&name)
|
|
if err != nil {
|
|
break
|
|
}
|
|
names = append(names, name)
|
|
}
|
|
// Check for errors during rows "Close".
|
|
// This may be more important if multiple statements are executed
|
|
// in a single batch and rows were written as well as read.
|
|
if closeErr := rows.Close(); closeErr != nil {
|
|
http.Error(w, closeErr.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Check for row scan error.
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Check for errors during row iteration.
|
|
if err = rows.Err(); err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(names)
|
|
return
|
|
case "/async-action":
|
|
// This action has side effects that we want to preserve
|
|
// even if the client cancels the HTTP request part way through.
|
|
// For this we do not use the http request context as a base for
|
|
// the timeout.
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var orderRef = "ABC123"
|
|
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
|
|
_, err = tx.ExecContext(ctx, "stored_proc_name", orderRef)
|
|
|
|
if err != nil {
|
|
tx.Rollback()
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
http.Error(w, "action in unknown state, check state before attempting again", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
}
|