whatcanGOwrong

This commit is contained in:
2024-09-19 21:38:24 -04:00
commit d0ae4d841d
17908 changed files with 4096831 additions and 0 deletions
@@ -0,0 +1,7 @@
# Examples
* chat is a command line chat program using listen/notify.
* todo is a command line todo list that demonstrates basic CRUD actions.
* url_shortener contains a simple example of using pgx in a web context.
* [Tern](https://github.com/jackc/tern) is a migration tool that uses pgx.
* [The Pithy Reader](https://github.com/jackc/tpr) is a RSS aggregator that uses pgx.
@@ -0,0 +1,20 @@
# Description
This is a sample chat program implemented using PostgreSQL's listen/notify
functionality with pgx.
Start multiple instances of this program connected to the same database to chat
between them.
## Connection configuration
The database connection is configured via DATABASE_URL and standard PostgreSQL environment variables (PGHOST, PGUSER, etc.)
You can either export them then run chat:
export PGHOST=/private/tmp
./chat
Or you can prefix the chat execution with the environment variables:
PGHOST=/private/tmp ./chat
@@ -0,0 +1,73 @@
package main
import (
"bufio"
"context"
"fmt"
"os"
"github.com/jackc/pgx/v4/pgxpool"
)
var pool *pgxpool.Pool
func main() {
var err error
pool, err = pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to connect to database:", err)
os.Exit(1)
}
go listen()
fmt.Println(`Type a message and press enter.
This message should appear in any other chat instances connected to the same
database.
Type "exit" to quit.`)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
msg := scanner.Text()
if msg == "exit" {
os.Exit(0)
}
_, err = pool.Exec(context.Background(), "select pg_notify('chat', $1)", msg)
if err != nil {
fmt.Fprintln(os.Stderr, "Error sending notification:", err)
os.Exit(1)
}
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "Error scanning from stdin:", err)
os.Exit(1)
}
}
func listen() {
conn, err := pool.Acquire(context.Background())
if err != nil {
fmt.Fprintln(os.Stderr, "Error acquiring connection:", err)
os.Exit(1)
}
defer conn.Release()
_, err = conn.Exec(context.Background(), "listen chat")
if err != nil {
fmt.Fprintln(os.Stderr, "Error listening to chat channel:", err)
os.Exit(1)
}
for {
notification, err := conn.Conn().WaitForNotification(context.Background())
if err != nil {
fmt.Fprintln(os.Stderr, "Error waiting for notification:", err)
os.Exit(1)
}
fmt.Println("PID:", notification.PID, "Channel:", notification.Channel, "Payload:", notification.Payload)
}
}
@@ -0,0 +1,67 @@
# Description
This is a sample todo list implemented using pgx as the connector to a
PostgreSQL data store.
# Usage
Create a PostgreSQL database and run structure.sql into it to create the
necessary data schema.
Example:
createdb todo
psql todo < structure.sql
Build todo:
go build
## Connection configuration
The database connection is configured via DATABASE_URL and standard PostgreSQL environment variables (PGHOST, PGUSER, etc.)
You can either export them then run todo:
export PGDATABASE=todo
./todo list
Or you can prefix the todo execution with the environment variables:
PGDATABASE=todo ./todo list
## Add a todo item
./todo add 'Learn go'
## List tasks
./todo list
## Update a task
./todo update 1 'Learn more go'
## Delete a task
./todo remove 1
# Example Setup and Execution
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ createdb todo
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ psql todo < structure.sql
Expanded display is used automatically.
Timing is on.
CREATE TABLE
Time: 6.363 ms
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ go build
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ export PGDATABASE=todo
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo add 'Learn Go'
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list
1. Learn Go
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo update 1 'Learn more Go'
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list
1. Learn more Go
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo remove 1
jack@hk-47~/dev/go/src/github.com/jackc/pgx/examples/todo$ ./todo list
@@ -0,0 +1,119 @@
package main
import (
"context"
"fmt"
"os"
"strconv"
"github.com/jackc/pgx/v4"
)
var conn *pgx.Conn
func main() {
var err error
conn, err = pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connection to database: %v\n", err)
os.Exit(1)
}
if len(os.Args) == 1 {
printHelp()
os.Exit(0)
}
switch os.Args[1] {
case "list":
err = listTasks()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to list tasks: %v\n", err)
os.Exit(1)
}
case "add":
err = addTask(os.Args[2])
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to add task: %v\n", err)
os.Exit(1)
}
case "update":
n, err := strconv.ParseInt(os.Args[2], 10, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable convert task_num into int32: %v\n", err)
os.Exit(1)
}
err = updateTask(int32(n), os.Args[3])
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to update task: %v\n", err)
os.Exit(1)
}
case "remove":
n, err := strconv.ParseInt(os.Args[2], 10, 32)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable convert task_num into int32: %v\n", err)
os.Exit(1)
}
err = removeTask(int32(n))
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to remove task: %v\n", err)
os.Exit(1)
}
default:
fmt.Fprintln(os.Stderr, "Invalid command")
printHelp()
os.Exit(1)
}
}
func listTasks() error {
rows, _ := conn.Query(context.Background(), "select * from tasks")
for rows.Next() {
var id int32
var description string
err := rows.Scan(&id, &description)
if err != nil {
return err
}
fmt.Printf("%d. %s\n", id, description)
}
return rows.Err()
}
func addTask(description string) error {
_, err := conn.Exec(context.Background(), "insert into tasks(description) values($1)", description)
return err
}
func updateTask(itemNum int32, description string) error {
_, err := conn.Exec(context.Background(), "update tasks set description=$1 where id=$2", description, itemNum)
return err
}
func removeTask(itemNum int32) error {
_, err := conn.Exec(context.Background(), "delete from tasks where id=$1", itemNum)
return err
}
func printHelp() {
fmt.Print(`Todo pgx demo
Usage:
todo list
todo add task
todo update task_num item
todo remove task_num
Example:
todo add 'Learn Go'
todo list
`)
}
@@ -0,0 +1,4 @@
create table tasks (
id serial primary key,
description text not null
);
@@ -0,0 +1,33 @@
# Description
This is a sample REST URL shortener service implemented using pgx as the connector to a PostgreSQL data store.
# Usage
Create a PostgreSQL database and run structure.sql into it to create the necessary data schema.
Configure the database connection with `DATABASE_URL` or standard PostgreSQL (`PG*`) environment variables or
Run main.go:
```
go run main.go
```
## Create or Update a Shortened URL
```
curl -X PUT -d 'http://www.google.com' http://localhost:8080/google
```
## Get a Shortened URL
```
curl http://localhost:8080/google
```
## Delete a Shortened URL
```
curl -X DELETE http://localhost:8080/google
```
@@ -0,0 +1,98 @@
package main
import (
"context"
"io/ioutil"
"net/http"
"os"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/log/log15adapter"
"github.com/jackc/pgx/v4/pgxpool"
log "gopkg.in/inconshreveable/log15.v2"
)
var db *pgxpool.Pool
func getUrlHandler(w http.ResponseWriter, req *http.Request) {
var url string
err := db.QueryRow(context.Background(), "select url from shortened_urls where id=$1", req.URL.Path).Scan(&url)
switch err {
case nil:
http.Redirect(w, req, url, http.StatusSeeOther)
case pgx.ErrNoRows:
http.NotFound(w, req)
default:
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
}
func putUrlHandler(w http.ResponseWriter, req *http.Request) {
id := req.URL.Path
var url string
if body, err := ioutil.ReadAll(req.Body); err == nil {
url = string(body)
} else {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if _, err := db.Exec(context.Background(), `insert into shortened_urls(id, url) values ($1, $2)
on conflict (id) do update set url=excluded.url`, id, url); err == nil {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
}
func deleteUrlHandler(w http.ResponseWriter, req *http.Request) {
if _, err := db.Exec(context.Background(), "delete from shortened_urls where id=$1", req.URL.Path); err == nil {
w.WriteHeader(http.StatusOK)
} else {
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
}
func urlHandler(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case "GET":
getUrlHandler(w, req)
case "PUT":
putUrlHandler(w, req)
case "DELETE":
deleteUrlHandler(w, req)
default:
w.Header().Add("Allow", "GET, PUT, DELETE")
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func main() {
logger := log15adapter.NewLogger(log.New("module", "pgx"))
poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
if err != nil {
log.Crit("Unable to parse DATABASE_URL", "error", err)
os.Exit(1)
}
poolConfig.ConnConfig.Logger = logger
db, err = pgxpool.ConnectConfig(context.Background(), poolConfig)
if err != nil {
log.Crit("Unable to create connection pool", "error", err)
os.Exit(1)
}
http.HandleFunc("/", urlHandler)
log.Info("Starting URL shortener on localhost:8080")
err = http.ListenAndServe("localhost:8080", nil)
if err != nil {
log.Crit("Unable to start web server", "error", err)
os.Exit(1)
}
}
@@ -0,0 +1,4 @@
create table shortened_urls (
id text primary key,
url text not null
);