Saving the work

pull/1/head
Drew Bednar 6 months ago
parent 5425bc78bf
commit 2a537f5b54

@ -8,6 +8,11 @@ import (
_ "github.com/mattn/go-sqlite3"
)
func showTransactionStmtClose(p *sql.Stmt) {
log.Println("Closing statement!")
p.Close()
}
func main() {
log.Println("Hello, world!")
@ -66,7 +71,8 @@ func main() {
// You don't wait for the garbage collector to do it.
// as long as theres an open result set (represented by rows), the underlying connection is busy and cant be used for
//any other query.
//any other query. It's busy because sql.Rows objects don't retrieve all results a query returns. It does it lazily to prevent over runing memory.
// it depends on the driver but there is usually and internal buffer that stores results.
// Also never defer in a loop. Defer only runs on function exit.
defer rows.Close()
@ -114,11 +120,11 @@ func main() {
//Prepared Queries
// If you will be using the same query over and over you should just prepare the query.
// $N is the postgres query param which SQLite can use too ? is mysql's
// $N (e.g. $1, $2, etc...) is the postgres query param which SQLite can use too ? is mysql's
//Under the hood, db.Query() actually prepares, executes, and closes a prepared statement
// Thats three round-trips to the database.
stmt, err := db.Prepare("SELECT * FROM todos WHERE id = $N")
stmt, err := db.Prepare("SELECT todo as other_todo FROM todos WHERE id = $1")
if err != nil {
log.Fatal(err)
}
@ -126,4 +132,138 @@ func main() {
//This prepared statement *sql.Stmt is tied to the database connection from which it was created, and it holds onto that connection until you call stmt.Close()
defer stmt.Close()
// You can also call with just params the .QueryRow method since the statement is already prepared
var other_todo string
err = stmt.QueryRow(1).Scan(&other_todo)
if err != nil {
log.Fatal(err)
}
log.Printf("Other Todo: %s", other_todo)
stmt.Close()
// MODIFYING DATA
// Use db.Exec() with a prepared statement when INSERTING, UPDATING, or DELETING
// Or another statement that doesn't return rows.
stmt, err = db.Prepare("INSERT INTO todos (subject, todo) VALUES ($1, $2)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
// stmt.Exec returns a sql.Result. That also has access to the statement metadata
// use _ if you don't care about the result and only want to check the err.
// Exec will NOT reserve a connection. unlike db.Query which returns a sql.Rows that
// will hold on to a connection until .Close() is called.
res, err := stmt.Exec("programming", "learn golang database queries!")
if err != nil {
log.Print("Dirp...")
log.Fatal(err)
}
lastId, err := res.LastInsertId()
if err != nil {
log.Fatal(err)
}
log.Printf("Last inserted id: %d", lastId)
rowCnt, err := res.RowsAffected()
if err != nil {
log.Fatal(err)
}
log.Printf("The number of rows affected: %d", rowCnt)
// close insert
stmt.Close()
stmt, err = db.Prepare("DELETE FROM todos WHERE subject = 'programming'")
if err != nil {
log.Fatal(err)
}
res, err = stmt.Exec()
if err != nil {
log.Fatal(err)
}
rowCnt, err = res.RowsAffected()
if err != nil {
log.Fatal(err)
}
log.Printf("The number of rows affected: %d", rowCnt)
// TRANSACTIONS
//In Go, a transaction is essentially an object that reserves a connection to the datastore.
//It lets you do all of the operations weve seen thus far, but guarantees that theyll be
//executed on the same connection.
// Starts with db.Begin() returning a sql.Tx and ends with a .Commit() or .Rollback() on the results sql.Tx
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// Prepared statement in a transaction are bound to that transaction exclusively.
// DO NOT use sql BEGIN or COMMIT in a sql.Tx object. It could leave the DB and client in different states.
// or mess havoc with the connection pool and connection lifetime
insert_stmt, err := tx.Prepare("INSERT INTO todos (subject, todo) VALUES ($1, $2)")
if err != nil {
log.Fatal(err)
}
defer insert_stmt.Close() // This closes at the end of the transaction, not the end of the function.
_, _ = insert_stmt.Exec("dirp", "just a place holder")
// There is no Close method on a transaction. It is closed by using .Commit or .Rollback
tx.Rollback()
// PREPARED STATEMENTS IN MORE DEPTH
//At the database level, a prepared statement is bound to a single database connection.
//The typical flow is that the client sends a SQL statement with placeholders to the server
//for preparation, the server responds with a statement ID, and then the client executes
//the statement by sending its ID and parameters.
// THE ABOVE DOES NOT HAPPEN IN database/sql. Instead the statement is managed at the driver level
// Go doesn't expose connections directly to users. The sql.Stmt object remembers which connection
// from the pool it is associated with.
//Know that if a connection is busy it may automatically get a new connection and re-prepare the
// statement on a new connection. It could cause more connections than you think, more statement
// recreation, or even hitting a server limit on prepared statements.
// NOTE db.Query(sql, param1, param2) will create a prepared statement under the hood.
// If you dont want to use a prepared statement, you need to use fmt.Sprint() or similar to
//assemble the SQL, and pass this as the only argument to db.Query() or db.QueryRow() And your driver
//needs to support plaintext query execution,
// NOTE prepared statements on Tx are bound exclusively to the Tx. You cannot use a prepared statement
// created by db.Prepare nor can you use a Tx.Prepare statement with a db.Query. Tx.Stmt() can be used
// to copy a db.Prepared statement into a transaction, but don't do this.
// HANDLING ERRORS
// You should always explicitly close a sql.Rows result set. If rows.Close() returns an error, its
// unclear what you should do, so logging the error message and ignoring or panic-ing might be the only sensible
// thing to do.
// Use sql.ErrNoRows in the event that no data is returned.
// rememeber db.QueryRow defers errors till Scan is called.
err = db.QueryRow("select subject from todos where id = $1", 100_000).Scan(&subject)
if err != nil {
if err == sql.ErrNoRows {
// there were no rows, but otherwise no error occurred
log.Println("No rows were returned! But that is okay. We expected that could happen")
} else {
log.Fatal(err)
}
}
}

Loading…
Cancel
Save