From 20ee2acffa7e9e7886b52a327850dbaf33c7fd5e Mon Sep 17 00:00:00 2001 From: devlights Date: Mon, 29 Jan 2024 08:47:35 +0000 Subject: [PATCH 01/13] Add 04.Exec --- 04.Exec/Taskfile.yml | 13 +++++ 04.Exec/main.go | 125 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 04.Exec/Taskfile.yml create mode 100644 04.Exec/main.go diff --git a/04.Exec/Taskfile.yml b/04.Exec/Taskfile.yml new file mode 100644 index 0000000..197e103 --- /dev/null +++ b/04.Exec/Taskfile.yml @@ -0,0 +1,13 @@ +# https://taskfile.dev + +version: '3' + +vars: + DBFILE: ../chinook.db + +tasks: + default: + cmds: + - go run main.go + - echo "SELECT * FROM artists WHERE ArtistId=999" | sqlite3 -header -table {{.DBFILE}} + - echo "DELETE FROM artists WHERE ArtistId=999" | sqlite3 -header -table {{.DBFILE}} diff --git a/04.Exec/main.go b/04.Exec/main.go new file mode 100644 index 0000000..fe0ec13 --- /dev/null +++ b/04.Exec/main.go @@ -0,0 +1,125 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} +func main() { + if err := run(); err != nil { + log.Panic(err) + } + + /* + $ task -d 04.Exec/ + task: [default] go run main.go + LastInsertId: 999 RowsAffected: 1 + task: [default] echo "SELECT * FROM artists WHERE ArtistId=999" | sqlite3 -header -table ../chinook.db + +----------+------+ + | ArtistId | Name | + +----------+------+ + | 999 | test | + +----------+------+ + task: [default] echo "DELETE FROM artists WHERE ArtistId=999" | sqlite3 -header -table ../chinook.db + */ +} + +const ( + driver = "sqlite3" + datasource = "../chinook.db" +) + +type ( + Artist struct { + Id int + Name string + } +) + +var ( + db *sql.DB +) + +// 04.Exec +// +// データベースに対してINSERT,UPDATE,DELETEを発行するには *DB.Exec() を利用する。 +// +// - https://pkg.go.dev/database/sql@go1.21.6#DB.Exec +// +// *DB.Exec() は、行データを返さず、代わりに sql.Result を返す。 +// +// - https://pkg.go.dev/database/sql@go1.21.6#Result +// +// sql.Result からは、最後に追加されたID値と影響を受けた行数が取得できる。 +// sql.Result.LastInsertId()は、Auto Incrementな列の場合に利用できる。 +// +// 今回は artists テーブルに新たなレコードをINSERTする。 +// artistsテーブルのレイアウトは以下。 +// +// $ sqlite3 chinook.db +// SQLite version 3.37.2 2022-01-06 13:25:41 +// Enter ".help" for usage hints. +// sqlite> .headers on +// sqlite> .mode table +// sqlite> pragma table_info(artists); +// +-----+----------+---------------+---------+------------+----+ +// | cid | name | type | notnull | dflt_value | pk | +// +-----+----------+---------------+---------+------------+----+ +// | 0 | ArtistId | INTEGER | 1 | | 1 | +// | 1 | Name | NVARCHAR(120) | 0 | | 0 | +// +-----+----------+---------------+---------+------------+----+ +// sqlite> SELECT MAX(ArtistId) FROM artists; +// +---------------+ +// | MAX(ArtistId) | +// +---------------+ +// | 275 | +// +---------------+ +// +// # REFERENCES +// - https://go.dev/doc/tutorial/database-access#add_data +func run() error { + var ( + err error + ) + + db, err = sql.Open(driver, datasource) + if err != nil { + return fmt.Errorf("sql.Open: %w", err) + } + + err = db.Ping() + if err != nil { + return fmt.Errorf("db.Ping: %w", err) + } + + var ( + result sql.Result + lastId int64 + affected int64 + ) + + result, err = db.Exec("INSERT INTO artists (ArtistId, Name) VALUES (999, 'test')") + if err != nil { + return fmt.Errorf("db.Exec: %w", err) + } + + lastId, err = result.LastInsertId() + if err != nil { + return fmt.Errorf("Result.LastInsertId: %w", err) + } + + affected, err = result.RowsAffected() + if err != nil { + return fmt.Errorf("Result.RowsAffected: %w", err) + } + + log.Printf("LastInsertId: %v\tRowsAffected: %v", lastId, affected) + + return nil +} From e2fa7bd00cf7d8b48e4c001671cd9c4db03169de Mon Sep 17 00:00:00 2001 From: devlights Date: Mon, 29 Jan 2024 08:57:22 +0000 Subject: [PATCH 02/13] Update 04.Exec/main.go --- 04.Exec/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/04.Exec/main.go b/04.Exec/main.go index fe0ec13..dc4c7b4 100644 --- a/04.Exec/main.go +++ b/04.Exec/main.go @@ -104,7 +104,7 @@ func run() error { affected int64 ) - result, err = db.Exec("INSERT INTO artists (ArtistId, Name) VALUES (999, 'test')") + result, err = db.Exec("INSERT INTO artists (ArtistId, Name) VALUES (?, ?)", 999, "test") if err != nil { return fmt.Errorf("db.Exec: %w", err) } From bb989191ece80f56294c99061c95adc4ff0aa624 Mon Sep 17 00:00:00 2001 From: devlights Date: Mon, 29 Jan 2024 09:17:50 +0000 Subject: [PATCH 03/13] Add 05.Transaction --- 04.Exec/Taskfile.yml | 4 +-- 04.Exec/main.go | 2 +- 05.Transaction/Taskfile.yml | 13 ++++++++ 05.Transaction/main.go | 61 +++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 05.Transaction/Taskfile.yml create mode 100644 05.Transaction/main.go diff --git a/04.Exec/Taskfile.yml b/04.Exec/Taskfile.yml index 197e103..66ba097 100644 --- a/04.Exec/Taskfile.yml +++ b/04.Exec/Taskfile.yml @@ -3,11 +3,11 @@ version: '3' vars: - DBFILE: ../chinook.db + DBFILE: ./chinook.db tasks: default: cmds: + - cp -f ../chinook.db . - go run main.go - echo "SELECT * FROM artists WHERE ArtistId=999" | sqlite3 -header -table {{.DBFILE}} - - echo "DELETE FROM artists WHERE ArtistId=999" | sqlite3 -header -table {{.DBFILE}} diff --git a/04.Exec/main.go b/04.Exec/main.go index dc4c7b4..accf564 100644 --- a/04.Exec/main.go +++ b/04.Exec/main.go @@ -32,7 +32,7 @@ func main() { const ( driver = "sqlite3" - datasource = "../chinook.db" + datasource = "./chinook.db" ) type ( diff --git a/05.Transaction/Taskfile.yml b/05.Transaction/Taskfile.yml new file mode 100644 index 0000000..36a3016 --- /dev/null +++ b/05.Transaction/Taskfile.yml @@ -0,0 +1,13 @@ +# https://taskfile.dev + +version: '3' + +vars: + DBFILE: ./chinook.db + +tasks: + default: + cmds: + - cp -f ../chinook.db . + - go run main.go + - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table {{.DBFILE}} diff --git a/05.Transaction/main.go b/05.Transaction/main.go new file mode 100644 index 0000000..b76f34e --- /dev/null +++ b/05.Transaction/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} + +func main() { + if err := run(); err != nil { + log.Panic(err) + } +} + +func run() error { + var ( + db *sql.DB + err error + ) + + db, err = sql.Open("sqlite3", "./chinook.db") + if err != nil { + return fmt.Errorf("sql.Open: %w", err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + return fmt.Errorf("db.Ping: %w", err) + } + + var ( + tx *sql.Tx + ) + + tx, err = db.Begin() + if err != nil { + return fmt.Errorf("db.Begin: %w", err) + } + defer tx.Rollback() + + for i := 990; i < 1000; i++ { + _, err = tx.Exec("INSERT INTO artists (ArtistId, Name) VALUES (?, ?)", i, fmt.Sprintf("test%d", i)) + if err != nil { + return fmt.Errorf("tx.Exec: %w (%d)", err, i) + } + } + + err = tx.Commit() + if err != nil { + return fmt.Errorf("tx.Commit: %w", err) + } + + return nil +} From c54ec9e5a1758086eb8d58c442e59f5cfa3d9294 Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 30 Jan 2024 04:49:19 +0000 Subject: [PATCH 04/13] Add 06.PreparedQuery --- 06.PreparedQuery/Taskfile.yml | 12 ++++ 06.PreparedQuery/main.go | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 06.PreparedQuery/Taskfile.yml create mode 100644 06.PreparedQuery/main.go diff --git a/06.PreparedQuery/Taskfile.yml b/06.PreparedQuery/Taskfile.yml new file mode 100644 index 0000000..a9f5aba --- /dev/null +++ b/06.PreparedQuery/Taskfile.yml @@ -0,0 +1,12 @@ +# https://taskfile.dev + +version: '3' + +vars: + DBFILE: ./chinook.db + +tasks: + default: + cmds: + - cp -f ../chinook.db . + - go run main.go diff --git a/06.PreparedQuery/main.go b/06.PreparedQuery/main.go new file mode 100644 index 0000000..b40a2df --- /dev/null +++ b/06.PreparedQuery/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "database/sql" + "errors" + "fmt" + "log" + "sync" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} + +// 06.PreparedQuery +// +// Prepared Queryを利用する場合は、*sql.DB.Prepare() を使う。 +// *sql.DB.Prepare() は、*sql.Stmt を返し、これにパラメータを指定することにより実行できる。 +// *sql.Stmt は、利用が終わったら Close() を呼び出す必要がある。 +// +// *sql.Stmt は、複数のgoroutineにて同時に利用することが出来る。 +// +// > Prepare creates a prepared statement for later queries or executions. +// Multiple queries or executions may be run concurrently from the returned statement. +// The caller must call the statement's Close method when the statement is no longer needed. +// +// > Prepareは、後のクエリーや実行のために準備されたステートメントを作成します。 +// 返されたステートメントから複数のクエリや実行を同時に実行することができます。 +// ステートメントが不要になったら、呼び出し元はステートメントの Close メソッドを呼び出さなければなりません。 +// +// # REFERENCES +// - https://go.dev/doc/database/prepared-statements +// - https://pkg.go.dev/database/sql@go1.21.6#DB.Prepare +// - https://stackoverflow.com/a/25327191 +func main() { + if err := run(); err != nil { + log.Panic(err) + } + + /* + $ task -d 06.PreparedQuery/ + task: [default] cp -f ../chinook.db . + task: [default] go run main.go + id=10 name=Billy Cobham + id=8 name=Audioslave + id=9 name=BackBeat + id=6 name=Antônio Carlos Jobim + id=1 name=AC/DC + id=3 name=Aerosmith + id=7 name=Apocalyptica + id=4 name=Alanis Morissette + id=2 name=Accept + id=5 name=Alice In Chains + */ +} + +func run() error { + var ( + db *sql.DB + err error + ) + + db, err = sql.Open("sqlite3", "./chinook.db") + if err != nil { + return fmt.Errorf("sql.Open: %w", err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + return fmt.Errorf("db.Ping: %w", err) + } + + var ( + stmt *sql.Stmt + ) + + stmt, err = db.Prepare("SELECT * FROM artists WHERE ArtistId = ?") + if err != nil { + return fmt.Errorf("db.Prepare: %w", err) + } + defer stmt.Close() + + const LOOP_COUNT = 10 + var ( + wg sync.WaitGroup + errCh = make(chan error, LOOP_COUNT) + ) + + wg.Add(LOOP_COUNT) + + for i := 0; i < LOOP_COUNT; i++ { + go func(i int) { + defer wg.Done() + + var ( + row *sql.Row + id int + name string + ) + + row = stmt.QueryRow(i) + err = row.Scan(&id, &name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + errCh <- fmt.Errorf("ErrNoRows: %d", i) + } + + errCh <- fmt.Errorf("sql.Row.Scan: %w", err) + } + + log.Printf("id=%v\tname=%v", id, name) + }(i + 1) + } + + wg.Wait() + close(errCh) + + for e := range errCh { + return e + } + + return nil +} From c6a9f2efa2909a657504b649b14111170276be73 Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 30 Jan 2024 05:56:28 +0000 Subject: [PATCH 05/13] Update example --- 06.PreparedQuery/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/06.PreparedQuery/main.go b/06.PreparedQuery/main.go index b40a2df..07ed0b4 100644 --- a/06.PreparedQuery/main.go +++ b/06.PreparedQuery/main.go @@ -106,9 +106,11 @@ func run() error { if err != nil { if errors.Is(err, sql.ErrNoRows) { errCh <- fmt.Errorf("ErrNoRows: %d", i) + return } errCh <- fmt.Errorf("sql.Row.Scan: %w", err) + return } log.Printf("id=%v\tname=%v", id, name) From 75af27f875682a63b9bc165f0cabd42e1fab0cfe Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 30 Jan 2024 06:08:15 +0000 Subject: [PATCH 06/13] Update comments --- 05.Transaction/main.go | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/05.Transaction/main.go b/05.Transaction/main.go index b76f34e..eb1057f 100644 --- a/05.Transaction/main.go +++ b/05.Transaction/main.go @@ -12,6 +12,63 @@ func init() { log.SetFlags(0) } +// 05.Transaction +// +// トランザクションを開始する場合、 *sql.DB.Begin() を利用する。 +// トランザクションは *sql.Tx で表される。 +// +// 基本的な使い方は、他の言語と同様で +// +// - *sql.Tx.Query() +// - *sql.Tx.QueryRow() +// - *sql.Tx.Exec() +// - *sql.Tx.Rollback() +// - *sql.Tx.Commit() +// +// を用いてトランザクションを操作する。 +// +// 定型文として、トランザクションを開始したら +// +// defer tx.Rollback() +// +// を呼び出しておく。これにより、エラー発生などに +// ロールバックが行われる。(コミットした後のロールバックは何も影響しない) +// +// https://go.dev/doc/database/execute-transactions に `Best Practice` として以下が記載されている。 +// +// > Use the APIs described in this section to manage transactions. +// Do not use transaction-related SQL statements such as BEGIN and COMMIT directly—doing so can leave your database in an unpredictable state, +// especially in concurrent programs. +// +// > トランザクションを管理するには、このセクションで説明するAPIを使用してください。 +// BEGINやCOMMITのようなトランザクション関連のSQL文を直接使用しないでください。 +// +// > When using a transaction, take care not to call the non-transaction sql.DB methods directly, too, as those will execute outside the transaction, +// giving your code an inconsistent view of the state of the database or even causing deadlocks. +// +// > トランザクションを使用する場合、トランザクション以外のsql.DBメソッドも直接呼び出さないように注意してください。 +// これらのメソッドはトランザクションの外で実行されるため、 +// コードの中でデータベースの状態に一貫性がなくなったり、デッドロックの原因になったりします。 +// +// https://pkg.go.dev/database/sql@go1.21.6#Tx には、以下の記載がある。 +// +// > A transaction must end with a call to Commit or Rollback. +// +// > トランザクションは必ず Commit もしくは Rollback で完了する必要があります。 +// +// > After a call to Commit or Rollback, all operations on the transaction fail with ErrTxDone. +// +// > コミットまたはロールバックを呼び出した後、トランザクションに対するすべての操作は ErrTxDone で失敗する。 +// +// > The statements prepared for a transaction by calling the transaction's Prepare or Stmt methods are closed by the call to Commit or Rollback. +// +// > トランザクションのPrepareメソッドまたはStmtメソッドを呼び出してトランザクションに準備されたステートメントは、CommitまたはRollbackの呼び出しによって閉じられます。 +// +// # REFERENCES +// - https://go.dev/doc/database/execute-transactions +// - https://pkg.go.dev/database/sql@go1.21.6#DB.Begin +// - https://pkg.go.dev/database/sql@go1.21.6#Tx +// - https://stackoverflow.com/a/25327191 func main() { if err := run(); err != nil { log.Panic(err) From 10b8f65bcde4642637e4b83271c0ff360d72d63a Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 30 Jan 2024 07:03:10 +0000 Subject: [PATCH 07/13] Add 07.PreparedQueryInTx --- 07.PreparedQueryInTx/Taskfile.yml | 13 ++++ 07.PreparedQueryInTx/main.go | 119 ++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 07.PreparedQueryInTx/Taskfile.yml create mode 100644 07.PreparedQueryInTx/main.go diff --git a/07.PreparedQueryInTx/Taskfile.yml b/07.PreparedQueryInTx/Taskfile.yml new file mode 100644 index 0000000..36a3016 --- /dev/null +++ b/07.PreparedQueryInTx/Taskfile.yml @@ -0,0 +1,13 @@ +# https://taskfile.dev + +version: '3' + +vars: + DBFILE: ./chinook.db + +tasks: + default: + cmds: + - cp -f ../chinook.db . + - go run main.go + - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table {{.DBFILE}} diff --git a/07.PreparedQueryInTx/main.go b/07.PreparedQueryInTx/main.go new file mode 100644 index 0000000..42e14c7 --- /dev/null +++ b/07.PreparedQueryInTx/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} + +// 07.PreparedQueryInTx +// +// トランザクション (*sql.Tx) からも、Prepared Query を作成することが出来る。 +// この場合、その Prepared Query は、当該トランザクションに紐づいた状態となり +// トランザクションの完了(Commit or Rollback)で、自動的にクローズされる。 +// +// # REFERENCES +// - https://go.dev/doc/database/execute-transactions +// - https://go.dev/doc/database/prepared-statements +// - https://pkg.go.dev/database/sql@go1.21.6#Tx +// - https://pkg.go.dev/database/sql@go1.21.6#Tx.Prepare +// - https://stackoverflow.com/a/25327191 +func main() { + if err := run(); err != nil { + log.Panic(err) + } + + /* + $ task -d 07.PreparedQueryInTx/ + task: [default] cp -f ../chinook.db . + task: [default] go run main.go + id=990 affected=1 + id=991 affected=1 + id=992 affected=1 + id=993 affected=1 + id=994 affected=1 + id=995 affected=1 + id=996 affected=1 + id=997 affected=1 + id=998 affected=1 + id=999 affected=1 + task: [default] echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table ./chinook.db + +----------+---------+ + | ArtistId | Name | + +----------+---------+ + | 999 | test999 | + | 998 | test998 | + | 997 | test997 | + | 996 | test996 | + | 995 | test995 | + | 994 | test994 | + | 993 | test993 | + | 992 | test992 | + | 991 | test991 | + | 990 | test990 | + +----------+---------+ + */ +} + +func run() error { + var ( + db *sql.DB + err error + ) + + db, err = sql.Open("sqlite3", "chinook.db") + if err != nil { + return fmt.Errorf("sql.Open: %w", err) + } + defer db.Close() + + var ( + tx *sql.Tx + ) + + tx, err = db.Begin() + if err != nil { + return fmt.Errorf("db.Begin: %w", err) + } + defer tx.Rollback() + + var ( + stmt *sql.Stmt + ) + + stmt, err = tx.Prepare("INSERT INTO artists (ArtistId, Name) VALUES (?, ?)") + if err != nil { + return fmt.Errorf("db.Prepare: %w", err) + } + defer stmt.Close() // tx経由で *sql.Stmt を作成した場合、トランザクションと共にクローズされるので無くても良い + + var ( + dropErr = func(v any, _ error) any { return v } + ) + + for i := 990; i < 1000; i++ { + var ( + rslt sql.Result + ) + + rslt, err = stmt.Exec(i, fmt.Sprintf("test%d", i)) + if err != nil { + return fmt.Errorf("*sql.Stmt.Exec (in tx): %w", err) + } + + log.Printf("id=%v\taffected=%v", dropErr(rslt.LastInsertId()), dropErr(rslt.RowsAffected())) + } + + err = tx.Commit() + if err != nil { + return fmt.Errorf("tx.Commit: %w", err) + } + + return nil +} From 72849002ad28d8f037fd2a2a28bf6f2c4399da09 Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 6 Feb 2024 09:40:46 +0000 Subject: [PATCH 08/13] Add 08.Conn --- 08.Conn/Taskfile.yml | 12 ++++++ 08.Conn/main.go | 94 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 08.Conn/Taskfile.yml create mode 100644 08.Conn/main.go diff --git a/08.Conn/Taskfile.yml b/08.Conn/Taskfile.yml new file mode 100644 index 0000000..a9f5aba --- /dev/null +++ b/08.Conn/Taskfile.yml @@ -0,0 +1,12 @@ +# https://taskfile.dev + +version: '3' + +vars: + DBFILE: ./chinook.db + +tasks: + default: + cmds: + - cp -f ../chinook.db . + - go run main.go diff --git a/08.Conn/main.go b/08.Conn/main.go new file mode 100644 index 0000000..4331fae --- /dev/null +++ b/08.Conn/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "context" + "database/sql" + "errors" + "fmt" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} + +// 08.Conn +// +// db.Query()などの関数を利用すると、内部のコネクションプールから +// 任意のコネクションが利用される。単一の接続を取得して処理を行いたい場合は +// db.Conn()を利用して、コネクションを取得する。 +// +// >Conn returns a single connection by either opening a new connection or returning an existing connection from the connection pool. +// Conn will block until either a connection is returned or ctx is canceled. +// Queries run on the same Conn will be run in the same database session. +// +// >Connは、新しい接続をオープンするか、接続プールから既存の接続を返すことによって、単一の接続を返します。 +// Connは、接続が返されるかctxがキャンセルされるまでブロックします。 +// 同じConnで実行されるクエリは、同じデータベース・セッションで実行されます。 +// +// 取得したコネクションは Close メソッドを呼び出してリソースを解放する必要がある。 +// +// クエリの発行方法などは、*sql.DB と同じである。 +func main() { + if err := run(); err != nil { + log.Panic(err) + } + + /* + $ task -d 08.Conn/ + task: [default] cp -f ../chinook.db . + task: [default] go run main.go + AC/DC + */ +} + +func run() error { + var ( + db *sql.DB + err error + ) + + db, err = sql.Open("sqlite3", "chinook.db") + if err != nil { + return fmt.Errorf("sql.Open: %w", err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + return fmt.Errorf("db.Ping: %w", err) + } + + var ( + ctx = context.Background() + conn *sql.Conn + ) + + conn, err = db.Conn(ctx) + if err != nil { + return fmt.Errorf("db.Conn: %w", err) + } + defer conn.Close() + + err = conn.PingContext(ctx) + if err != nil { + return fmt.Errorf("conn.PingContext: %w", err) + } + + var ( + row *sql.Row + name string + ) + + row = conn.QueryRowContext(ctx, "SELECT Name from artists") + err = row.Scan(&name) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return fmt.Errorf("conn.QueryContext: %w", err) + } + + log.Println(name) + + return nil +} From 0a7faf7dc405317a10a077e45f4b80d18553ab4f Mon Sep 17 00:00:00 2001 From: devlights Date: Wed, 7 Feb 2024 06:59:51 +0000 Subject: [PATCH 09/13] Bump Go version to 1.22 --- .gitpod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index f8327cf..2cdec15 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,6 +1,6 @@ FROM gitpod/workspace-base:latest -ENV GO_VERSION=1.21.6 +ENV GO_VERSION=1.22.0 # For ref, see: https://github.com/gitpod-io/workspace-images/blob/61df77aad71689504112e1087bb7e26d45a43d10/chunks/lang-go/Dockerfile#L10 ENV GOPATH=$HOME/go-packages From fe1e73a2874f8daec66282ca2a5720f5fe323ec9 Mon Sep 17 00:00:00 2001 From: devlights Date: Wed, 7 Feb 2024 07:18:33 +0000 Subject: [PATCH 10/13] Update go.mod --- .gitpod.yml | 2 +- go.mod | 4 ++-- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitpod.yml b/.gitpod.yml index b4f5f14..003fb5c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -11,7 +11,7 @@ tasks: go install github.com/mgechev/revive@latest && go install github.com/go-delve/delve/cmd/dlv@latest && go install go.uber.org/nilaway/cmd/nilaway@latest && - go install github.com/mattn/go-sqlite3 && + go install github.com/mattn/go-sqlite3@latest && task download vscode: diff --git a/go.mod b/go.mod index ed4dde4..18d07b6 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/devlights/try-golang-database -go 1.21.6 +go 1.22 -require github.com/mattn/go-sqlite3 v1.14.19 +require github.com/mattn/go-sqlite3 v1.14.22 diff --git a/go.sum b/go.sum index 3042612..e8d092a 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= -github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +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= From 4e134e8ff0d8f9721b0fdf8629b90e39a19349f3 Mon Sep 17 00:00:00 2001 From: devlights Date: Thu, 8 Feb 2024 09:28:05 +0000 Subject: [PATCH 11/13] Update .gitpod.yml --- .gitpod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 003fb5c..b4f5f14 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -11,7 +11,7 @@ tasks: go install github.com/mgechev/revive@latest && go install github.com/go-delve/delve/cmd/dlv@latest && go install go.uber.org/nilaway/cmd/nilaway@latest && - go install github.com/mattn/go-sqlite3@latest && + go install github.com/mattn/go-sqlite3 && task download vscode: From c6e28bd75f736f66f336ee8b643f89a7bc08a900 Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 13 Feb 2024 08:58:59 +0000 Subject: [PATCH 12/13] Add 09.Columns --- 09.Columns/Taskfile.yml | 13 ++++++++ 09.Columns/main.go | 69 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 09.Columns/Taskfile.yml create mode 100644 09.Columns/main.go diff --git a/09.Columns/Taskfile.yml b/09.Columns/Taskfile.yml new file mode 100644 index 0000000..234d788 --- /dev/null +++ b/09.Columns/Taskfile.yml @@ -0,0 +1,13 @@ +# https://taskfile.dev + +version: "3" + +vars: + DBFILE: chinook.db + +tasks: + default: + cmds: + - cp -f ../{{.DBFILE}} . + - echo "PRAGMA table_info(tracks)" | sqlite3 -header -table ./{{.DBFILE}} + - go run main.go diff --git a/09.Columns/main.go b/09.Columns/main.go new file mode 100644 index 0000000..1b61925 --- /dev/null +++ b/09.Columns/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "database/sql" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +func init() { + log.SetFlags(0) +} + +// 09.Columns +// +// *sql.DB.Query() などで *sql.Rows を取得した際に +// カラム名を知りたい場合は *sql.Rows.Columns() を利用する。 +// +// https://pkg.go.dev/database/sql@go1.22.0#Rows.Columns +// +// 結果は []string で返ってくる。 +// SELECTで指定した並びで設定されている。 +// +// 上記ドキュメントには以下の記載がある。 +// +// >Columns returns the column names. Columns returns an error if the rows are closed. +// +// >Columnsはカラム名を返す。Columnsは、行が閉じられている場合はエラーを返します。 +func main() { + if err := run(); err != nil { + log.Panic(err) + } +} + +func run() error { + var ( + db *sql.DB + err error + ) + + db, err = sql.Open("sqlite3", "./chinook.db") + if err != nil { + return err + } + defer db.Close() + + var ( + rows *sql.Rows + ) + + rows, err = db.Query("SELECT * FROM tracks LIMIT 1") + if err != nil { + return err + } + defer rows.Close() + + var ( + columns []string + ) + + columns, err = rows.Columns() + if err != nil { + return err + } + + log.Println(columns) + + return nil +} From dc0650f30cbbf60b14cb79e751510ff6832fc0cc Mon Sep 17 00:00:00 2001 From: devlights Date: Tue, 13 Feb 2024 09:03:11 +0000 Subject: [PATCH 13/13] Update Taskfiles --- 01.Open/Taskfile.yml | 4 ++++ 01.Open/main.go | 2 +- 02.Query/Taskfile.yml | 4 ++++ 02.Query/main.go | 2 +- 03.QueryRow/Taskfile.yml | 4 ++++ 03.QueryRow/main.go | 2 +- 04.Exec/Taskfile.yml | 6 +++--- 05.Transaction/Taskfile.yml | 6 +++--- 06.PreparedQuery/Taskfile.yml | 4 ++-- 07.PreparedQueryInTx/Taskfile.yml | 6 +++--- 08.Conn/Taskfile.yml | 4 ++-- 11 files changed, 28 insertions(+), 16 deletions(-) diff --git a/01.Open/Taskfile.yml b/01.Open/Taskfile.yml index a29c8f5..4c9f3f9 100644 --- a/01.Open/Taskfile.yml +++ b/01.Open/Taskfile.yml @@ -2,7 +2,11 @@ version: '3' +vars: + DBFILE: chinook.db + tasks: default: cmds: + - cp -f ../{{.DBFILE}} . - go run main.go diff --git a/01.Open/main.go b/01.Open/main.go index e17f1bf..fb994ac 100644 --- a/01.Open/main.go +++ b/01.Open/main.go @@ -83,7 +83,7 @@ func main() { const ( driver = "sqlite3" - datasource = "../chinook.db" + datasource = "./chinook.db" ) var ( diff --git a/02.Query/Taskfile.yml b/02.Query/Taskfile.yml index a29c8f5..4c9f3f9 100644 --- a/02.Query/Taskfile.yml +++ b/02.Query/Taskfile.yml @@ -2,7 +2,11 @@ version: '3' +vars: + DBFILE: chinook.db + tasks: default: cmds: + - cp -f ../{{.DBFILE}} . - go run main.go diff --git a/02.Query/main.go b/02.Query/main.go index f5705eb..87e47ad 100644 --- a/02.Query/main.go +++ b/02.Query/main.go @@ -31,7 +31,7 @@ func main() { const ( driver = "sqlite3" - datasource = "../chinook.db" + datasource = "./chinook.db" ) type ( diff --git a/03.QueryRow/Taskfile.yml b/03.QueryRow/Taskfile.yml index a29c8f5..4c9f3f9 100644 --- a/03.QueryRow/Taskfile.yml +++ b/03.QueryRow/Taskfile.yml @@ -2,7 +2,11 @@ version: '3' +vars: + DBFILE: chinook.db + tasks: default: cmds: + - cp -f ../{{.DBFILE}} . - go run main.go diff --git a/03.QueryRow/main.go b/03.QueryRow/main.go index 941cd0a..d5074db 100644 --- a/03.QueryRow/main.go +++ b/03.QueryRow/main.go @@ -27,7 +27,7 @@ func main() { const ( driver = "sqlite3" - datasource = "../chinook.db" + datasource = "./chinook.db" ) type ( diff --git a/04.Exec/Taskfile.yml b/04.Exec/Taskfile.yml index 66ba097..2127d23 100644 --- a/04.Exec/Taskfile.yml +++ b/04.Exec/Taskfile.yml @@ -3,11 +3,11 @@ version: '3' vars: - DBFILE: ./chinook.db + DBFILE: chinook.db tasks: default: cmds: - - cp -f ../chinook.db . + - cp -f ../{{.DBFILE}} . - go run main.go - - echo "SELECT * FROM artists WHERE ArtistId=999" | sqlite3 -header -table {{.DBFILE}} + - echo "SELECT * FROM artists WHERE ArtistId=999" | sqlite3 -header -table ./{{.DBFILE}} diff --git a/05.Transaction/Taskfile.yml b/05.Transaction/Taskfile.yml index 36a3016..bc4539d 100644 --- a/05.Transaction/Taskfile.yml +++ b/05.Transaction/Taskfile.yml @@ -3,11 +3,11 @@ version: '3' vars: - DBFILE: ./chinook.db + DBFILE: chinook.db tasks: default: cmds: - - cp -f ../chinook.db . + - cp -f ../{{.DBFILE}} . - go run main.go - - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table {{.DBFILE}} + - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table ./{{.DBFILE}} diff --git a/06.PreparedQuery/Taskfile.yml b/06.PreparedQuery/Taskfile.yml index a9f5aba..4c9f3f9 100644 --- a/06.PreparedQuery/Taskfile.yml +++ b/06.PreparedQuery/Taskfile.yml @@ -3,10 +3,10 @@ version: '3' vars: - DBFILE: ./chinook.db + DBFILE: chinook.db tasks: default: cmds: - - cp -f ../chinook.db . + - cp -f ../{{.DBFILE}} . - go run main.go diff --git a/07.PreparedQueryInTx/Taskfile.yml b/07.PreparedQueryInTx/Taskfile.yml index 36a3016..bc4539d 100644 --- a/07.PreparedQueryInTx/Taskfile.yml +++ b/07.PreparedQueryInTx/Taskfile.yml @@ -3,11 +3,11 @@ version: '3' vars: - DBFILE: ./chinook.db + DBFILE: chinook.db tasks: default: cmds: - - cp -f ../chinook.db . + - cp -f ../{{.DBFILE}} . - go run main.go - - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table {{.DBFILE}} + - echo "SELECT * FROM artists ORDER BY ArtistId DESC LIMIT 10" | sqlite3 -header -table ./{{.DBFILE}} diff --git a/08.Conn/Taskfile.yml b/08.Conn/Taskfile.yml index a9f5aba..4c9f3f9 100644 --- a/08.Conn/Taskfile.yml +++ b/08.Conn/Taskfile.yml @@ -3,10 +3,10 @@ version: '3' vars: - DBFILE: ./chinook.db + DBFILE: chinook.db tasks: default: cmds: - - cp -f ../chinook.db . + - cp -f ../{{.DBFILE}} . - go run main.go