summaryrefslogtreecommitdiffstats
path: root/src/database/sql/example_service_test.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/database/sql/example_service_test.go
parentInitial commit. (diff)
downloadgolang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz
golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/database/sql/example_service_test.go')
-rw-r--r--src/database/sql/example_service_test.go158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/database/sql/example_service_test.go b/src/database/sql/example_service_test.go
new file mode 100644
index 0000000..768307c
--- /dev/null
+++ b/src/database/sql/example_service_test.go
@@ -0,0 +1,158 @@
+// 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.
+
+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
+ }
+}