Finally making progress

pull/1/head
Drew Bednar 6 months ago
parent 6e434e487d
commit f42a41fa1c

@ -0,0 +1,23 @@
# Regular SQLite in Go
## Install the Migrations Tool
This will in stall the migrate tool for our system.
```
go install -tags 'sqlite3' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
```
You could also use golang-migrate as a library and migrate your DB in code. For that you would need to install that package as part of your module dependencies.
```
go get github.com/mattn/go-sqlite3
```
## Running Migrations
### Using the CLI
```
migrate -database sqlite3://./data/trysqlite.db -path ./migrations
```

@ -0,0 +1,5 @@
module try-sqlite
go 1.21.0
require github.com/mattn/go-sqlite3 v1.14.22 // indirect

@ -0,0 +1,2 @@
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=

@ -0,0 +1,114 @@
package main
import (
"database/sql"
"flag"
"log"
_ "github.com/mattn/go-sqlite3"
)
func main() {
log.Println("Hello, world!")
db_path := flag.String("database", "./data/trysqlite.db", "Path to a Sqlite3 database")
flag.Parse()
// TODO figure out what query string options to be using
full_database_path := "file:" + *db_path + "?cache=shared"
log.Printf("Using Database: %s", full_database_path)
// db is a Handle backed by a go database/sql connection pool. It is not a connection in of itself, nor is it the actual connection pool
// The first connection is made lazily by the handle, and this doesn't validate any of the connection parameters either at this point.
// It simply prepares the abstraction for use.
db, err := sql.Open("sqlite3", full_database_path)
if err != nil {
log.Fatalf("Failed to connect to sqlite database: %s", full_database_path)
}
// It is idiomatic to defer db.Close() if the sql.DB should not have a lifetime beyond the scope of the function.
//Although its idiomatic to Close() the database when youre finished with it, the sql.DB object is designed to be long-lived.
//Dont Open() and Close() databases frequently. Instead, create one sql.DB object for each distinct datastore you need to access,
// and keep it until the program is done accessing that datastore.
// Pass it around as needed, or make it available somehow globally, but keep it open.
// And dont Open() and Close() from a short-lived function. Instead, pass the sql.DB
// into that short-lived function as an argument.
//If you dont treat the sql.DB as a long-lived object, you could experience problems such as poor reuse and sharing of connections,
// running out of available network resources, or sporadic failures due to a lot of TCP connections remaining in TIME_WAIT status.
// Such problems are signs that youre not using database/sql as it was designed.
defer db.Close()
// If you want to check right away that the db is available and accessible you can do this and check for errs
err = db.Ping()
if err != nil {
log.Fatal("DB Ping failed. Check database and database connection parameters")
}
var (
id int
subject string
todo string
)
// db.Query vs db.Exec. If a function name includes Query, it is designed to ask a question of the database,
// and will return a set of rows, even if its empty. Statements that dont return rows should not use Query functions;
// they should use Exec()
rows, err := db.Query("SELECT subject FROM todos")
if err != nil {
log.Fatal(err)
}
// It is important to defer closing a row object also. Why? It will release the memory and the network connection. So it
// also prevents resource leaks. So you are a good steward, you clean up after yourself.
// 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.
// Also never defer in a loop. Defer only runs on function exit.
defer rows.Close()
// Iterate through each. Internally rows.Next will hit an EOF error it will call rows.close() for you freeing up the connection resources.
// but you shouldn't rely on that alone. rows.Close() will run on function exit, but this example is a Main func, so it's not realistic
// still get in the habit
for rows.Next() {
err = rows.Scan(&subject)
if err != nil {
log.Fatal(err)
}
log.Printf("Subject is %s", subject)
}
// Dont just assume that the loop iterates until youve processed all the rows. Always check.
err = rows.Err()
if err != nil {
log.Fatal(err)
}
// You could also clean up at this point. The call is supposed to be idempotent, but defer is more idiomatic.
// You don't want to call close if there was an Error because of that could cause a Panic.
rows.Close()
// If never want to do string concat from user input into a sql statement you run unless you like sql injection attacks
// expects to return one row. If no row it will delay throwing error until Scan is called
row := db.QueryRow("SELECT id, subject, todo FROM todos WHERE id = ?", 1)
err = row.Scan(&id, &subject, &todo)
if err != nil {
log.Fatal(err)
}
log.Printf("id: %d, subject: %s, todo: %s", id, subject, todo)
// This should error on scan
row = db.QueryRow("SELECT * FROM todos WHERE id = ?", 100_000)
err = row.Scan(&id, &subject, &todo)
if err != nil {
log.Print(err.Error())
}
// There is no row.Close(). Rows objects have a Close() method. No explicit closing required.
}

@ -0,0 +1,2 @@
PRAGMA foreign_keys=1;
CREATE TABLE todos (id INTEGER PRIMARY KEY AUTOINCREMENT, subject TEXT, TODO TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);

@ -0,0 +1 @@
ALTER TABLE todos ADD completed BOOL DEFAULT false;

@ -1,3 +1,10 @@
module gowiki
go 1.21.0
require (
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 // indirect
github.com/tursodatabase/go-libsql v0.0.0-20240429120401-651096bbee0b // indirect
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
)

@ -0,0 +1,8 @@
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM=
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM=
github.com/tursodatabase/go-libsql v0.0.0-20240429120401-651096bbee0b h1:R7hev4b96zgXjKbS2ZNbHBnDvyFZhH+LlMqtKH6hIkU=
github.com/tursodatabase/go-libsql v0.0.0-20240429120401-651096bbee0b/go.mod h1:TjsB2miB8RW2Sse8sdxzVTdeGlx74GloD5zJYUC38d8=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
Loading…
Cancel
Save