diff --git a/examples/README.md b/examples/README.md index 9d7ed79f..23a83ab6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,5 +1,6 @@ # Examples +* 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. diff --git a/examples/todo/README.md b/examples/todo/README.md new file mode 100644 index 00000000..eb3d95ba --- /dev/null +++ b/examples/todo/README.md @@ -0,0 +1,72 @@ +# 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 enviroment variables. + +* TODO_DB_HOST - defaults to localhost +* TODO_DB_USER - defaults to current OS user +* TODO_DB_PASSWORD - defaults to empty string +* TODO_DB_DATABASE - defaults to todo + +You can either export them then run todo: + + export TODO_DB_HOST=/private/tmp + ./todo list + +Or you can prefix the todo execution with the environment variables: + + TODO_DB_HOST=/private/tmp ./todo list + +## Add a todo item + + ./todo add 'Learn go' + +## List tasks + + ./todo list + +## Update a task + + ./todo add 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 TODO_DB_HOST=/private/tmp + 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 diff --git a/examples/todo/main.go b/examples/todo/main.go new file mode 100644 index 00000000..bacdf24e --- /dev/null +++ b/examples/todo/main.go @@ -0,0 +1,140 @@ +package main + +import ( + "fmt" + "github.com/jackc/pgx" + "os" + "strconv" +) + +var conn *pgx.Conn + +func main() { + var err error + conn, err = pgx.Connect(extractConfig()) + 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("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("insert into tasks(description) values($1)", description) + return err +} + +func updateTask(itemNum int32, description string) error { + _, err := conn.Exec("update tasks set description=$1 where id=$2", description, itemNum) + return err +} + +func removeTask(itemNum int32) error { + _, err := conn.Exec("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 +`) +} + +func extractConfig() pgx.ConnConfig { + var config pgx.ConnConfig + + config.Host = os.Getenv("TODO_DB_HOST") + if config.Host == "" { + config.Host = "localhost" + } + + config.User = os.Getenv("TODO_DB_USER") + if config.User == "" { + config.User = os.Getenv("USER") + } + + config.Password = os.Getenv("TODO_DB_PASSWORD") + + config.Database = os.Getenv("TODO_DB_DATABASE") + if config.Database == "" { + config.Database = "todo" + } + + return config +} diff --git a/examples/todo/structure.sql b/examples/todo/structure.sql new file mode 100644 index 00000000..567685d1 --- /dev/null +++ b/examples/todo/structure.sql @@ -0,0 +1,4 @@ +create table tasks ( + id serial primary key, + description text not null +);