| <!--{ |
| "Title": "Tutorial: Accessing a relational database", |
| "Breadcrumb": true |
| }--> |
| |
| This tutorial introduces the basics of accessing a relational database with |
| Go and the `database/sql` package in its standard library. |
| |
| You'll get the most out of this tutorial if you have a basic familiarity with |
| Go and its tooling. If this is your first exposure to Go, please see |
| [Tutorial: Get started with Go](/doc/tutorial/getting-started) |
| for a quick introduction. |
| |
| The [`database/sql`](https://pkg.go.dev/database/sql) package you'll |
| be using includes types and functions for connecting to databases, executing |
| transactions, canceling an operation in progress, and more. For more details |
| on using the package, see |
| [Accessing databases](/doc/database/index). |
| |
| In this tutorial, you'll create a database, then write code to access the |
| database. Your example project will be a repository of data about vintage |
| jazz records. |
| |
| In this tutorial, you'll progress through the following sections: |
| |
| 1. Create a folder for your code. |
| 2. Set up a database. |
| 3. Import the database driver. |
| 4. Get a database handle and connect. |
| 5. Query for multiple rows. |
| 6. Query for a single row. |
| 7. Add data. |
| |
| **Note:** For other tutorials, see [Tutorials](/doc/tutorial/index.html). |
| |
| ## Prerequisites {#prerequisites} |
| |
| * **An installation of the [MySQL](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/) |
| relational database management system (DBMS).** |
| * **An installation of Go.** For installation instructions, see |
| [Installing Go](/doc/install). |
| * **A tool to edit your code.** Any text editor you have will work fine. |
| * **A command terminal.** Go works well using any terminal on Linux and Mac, |
| and on PowerShell or cmd in Windows. |
| |
| ## Create a folder for your code {#create_folder} |
| |
| To begin, create a folder for the code you'll write. |
| |
| 1. Open a command prompt and change to your home directory. |
| |
| On Linux or Mac: |
| |
| ``` |
| $ cd |
| ``` |
| |
| On Windows: |
| |
| ``` |
| C:\> cd %HOMEPATH% |
| ``` |
| |
| For the rest of the tutorial we will show a $ as the prompt. The |
| commands we use will work on Windows too. |
| |
| 2. From the command prompt, create a directory for your code called |
| data-access. |
| |
| ``` |
| $ mkdir data-access |
| $ cd data-access |
| ``` |
| |
| |
| 3. Create a module in which you can manage dependencies you will add during |
| this tutorial. |
| |
| Run the `go mod init` command, giving it your new code's module path. |
| |
| ``` |
| $ go mod init example/data-access |
| go: creating new go.mod: module example/data-access |
| ``` |
| |
| This command creates a go.mod file in which dependencies you add will be |
| listed for tracking. For more, be sure to see |
| [Managing dependencies](/doc/modules/managing-dependencies). |
| |
| **Note:** In actual development, you'd specify a module path that's |
| more specific to your own needs. For more, see |
| [Managing dependencies](/doc/modules/managing-dependencies#naming_module). |
| |
| Next, you'll create a database. |
| |
| ## Set up a database {#set_up_database} |
| |
| In this step, you'll create the database you'll be working with. You'll use |
| the CLI for the DBMS itself to create the database and table, as well as to |
| add data. |
| |
| You'll be creating a database with data about vintage jazz recordings on vinyl. |
| |
| The code here uses the [MySQL CLI](https://dev.mysql.com/doc/refman/8.0/en/mysql.html), |
| but most DBMSes have their own CLI with similar features. |
| |
| 1. Open a new command prompt. |
| 2. At the command line, log into your DBMS, as in the following example for |
| MySQL. |
| |
| ``` |
| $ mysql -u root -p |
| Enter password: |
| |
| mysql> |
| ``` |
| |
| 3. At the `mysql` command prompt, create a database. |
| |
| ``` |
| mysql> create database recordings; |
| ``` |
| |
| 4. Change to the database you just created so you can add tables. |
| |
| ``` |
| mysql> use recordings; |
| Database changed |
| ``` |
| |
| 5. In your text editor, in the data-access folder, create a file called |
| create-tables.sql to hold SQL script for adding tables. |
| 6. Into the file, paste the following SQL code, then save the file. |
| |
| ``` |
| DROP TABLE IF EXISTS album; |
| CREATE TABLE album ( |
| id INT AUTO_INCREMENT NOT NULL, |
| title VARCHAR(128) NOT NULL, |
| artist VARCHAR(255) NOT NULL, |
| price DECIMAL(5,2) NOT NULL, |
| PRIMARY KEY (`id`) |
| ); |
| |
| INSERT INTO album |
| (title, artist, price) |
| VALUES |
| ('Blue Train', 'John Coltrane', 56.99), |
| ('Giant Steps', 'John Coltrane', 63.99), |
| ('Jeru', 'Gerry Mulligan', 17.99), |
| ('Sarah Vaughan', 'Sarah Vaughan', 34.98); |
| ``` |
| |
| In this SQL code, you: |
| |
| * Delete (drop) a table called `album`. Executing this command first makes |
| it easier for you to re-run the script later if you want to start over |
| with the table. |
| |
| * Create an `album` table with four columns: `title`, `artist`, and `price`. |
| Each row's `id` value is created automatically by the DBMS. |
| |
| * Add four rows with values. |
| |
| 7. From the `mysql` command prompt, run the script you just created. |
| |
| You'll use the `source` command in the following form: |
| |
| ``` |
| mysql> source /path/to/create-tables.sql |
| ``` |
| |
| 8. At your DBMS command prompt, use a `SELECT` statement to verify you've |
| successfully created the table with data. |
| |
| ``` |
| mysql> select * from album; |
| +----+---------------+----------------+-------+ |
| | id | title | artist | price | |
| +----+---------------+----------------+-------+ |
| | 1 | Blue Train | John Coltrane | 56.99 | |
| | 2 | Giant Steps | John Coltrane | 63.99 | |
| | 3 | Jeru | Gerry Mulligan | 17.99 | |
| | 4 | Sarah Vaughan | Sarah Vaughan | 34.98 | |
| +----+---------------+----------------+-------+ |
| 4 rows in set (0.00 sec) |
| ``` |
| |
| Next, you'll write some Go code to connect so you can query. |
| |
| ## Find and import a database driver {#import_driver} |
| |
| Now that you've got a database with some data, get your Go code started. |
| |
| Locate and import a database driver that will translate requests you make |
| through functions in the `database/sql` package into requests the database |
| understands. |
| |
| 1. In your browser, visit the [SQLDrivers](/wiki/SQLDrivers) |
| wiki page to identify a driver you can use. |
| |
| Use the list on the page to identify the driver you'll use. For accessing |
| MySQL in this tutorial, you'll use |
| [Go-MySQL-Driver](https://github.com/go-sql-driver/mysql/). |
| |
| 2. Note the package name for the driver -- here, `github.com/go-sql-driver/mysql`. |
| |
| 3. Using your text editor, create a file in which to write your Go code and |
| save the file as main.go in the data-access directory you created earlier. |
| |
| 4. Into main.go, paste the following code to import the driver package. |
| |
| ``` |
| package main |
| |
| import "github.com/go-sql-driver/mysql" |
| ``` |
| |
| In this code, you: |
| |
| * Add your code to a `main` package so you can execute it independently. |
| |
| * Import the MySQL driver `github.com/go-sql-driver/mysql`. |
| |
| With the driver imported, you'll start writing code to access the database. |
| |
| ## Get a database handle and connect {#get_handle} |
| |
| Now write some Go code that gives you database access with a database handle. |
| |
| You'll use a pointer to an `sql.DB` struct, which represents access to a |
| specific database. |
| |
| #### Write the code |
| |
| 1. Into main.go, beneath the `import` code you just added, paste the following |
| Go code to create a database handle. |
| |
| ``` |
| var db *sql.DB |
| |
| func main() { |
| // Capture connection properties. |
| cfg := mysql.NewConfig() |
| cfg.User = os.Getenv("DBUSER") |
| cfg.Passwd = os.Getenv("DBPASS") |
| cfg.Net = "tcp" |
| cfg.Addr = "127.0.0.1:3306" |
| cfg.DBName = "recordings" |
| |
| // Get a database handle. |
| var err error |
| db, err = sql.Open("mysql", cfg.FormatDSN()) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| pingErr := db.Ping() |
| if pingErr != nil { |
| log.Fatal(pingErr) |
| } |
| fmt.Println("Connected!") |
| } |
| ``` |
| |
| In this code, you: |
| |
| * Declare a `db` variable of type [`*sql.DB`](https://pkg.go.dev/database/sql#DB). |
| This is your database handle. |
| |
| Making `db` a global variable simplifies this example. In |
| production, you'd avoid the global variable, such as by passing the |
| variable to functions that need it or by wrapping it in a struct. |
| |
| * Use the MySQL driver's [`Config`](https://pkg.go.dev/github.com/go-sql-driver/mysql#Config) |
| -- and the type's [`FormatDSN`](https://pkg.go.dev/github.com/go-sql-driver/mysql#Config.FormatDSN) |
| -– to collect connection properties and format them into a DSN for a connection string. |
| |
| The `Config` struct makes for code that's easier to read than a |
| connection string would be. |
| |
| * Call [`sql.Open`](https://pkg.go.dev/database/sql#Open) |
| to initialize the `db` variable, passing the return value of |
| `FormatDSN`. |
| |
| * Check for an error from `sql.Open`. It could fail if, for |
| example, your database connection specifics weren't well-formed. |
| |
| To simplify the code, you're calling `log.Fatal` to end |
| execution and print the error to the console. In production code, you'll |
| want to handle errors in a more graceful way. |
| |
| * Call [`DB.Ping`](https://pkg.go.dev/database/sql#DB.Ping) to |
| confirm that connecting to the database works. At run time, |
| `sql.Open` might not immediately connect, depending on the |
| driver. You're using `Ping` here to confirm that the |
| `database/sql` package can connect when it needs to. |
| |
| * Check for an error from `Ping`, in case the connection failed. |
| |
| * Print a message if `Ping` connects successfully. |
| |
| 2. Near the top of the main.go file, just beneath the package declaration, |
| import the packages you'll need to support the code you've just written. |
| |
| The top of the file should now look like this: |
| |
| ``` |
| package main |
| |
| import ( |
| "database/sql" |
| "fmt" |
| "log" |
| "os" |
| |
| "github.com/go-sql-driver/mysql" |
| ) |
| ``` |
| |
| 3. Save main.go. |
| |
| #### Run the code |
| |
| 1. Begin tracking the MySQL driver module as a dependency. |
| |
| Use the [`go get`](/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) |
| to add the github.com/go-sql-driver/mysql module as a dependency for your |
| own module. Use a dot argument to mean "get dependencies for code in the |
| current directory." |
| |
| ``` |
| $ go get . |
| go: added filippo.io/edwards25519 v1.1.0 |
| go: added github.com/go-sql-driver/mysql v1.8.1 |
| ``` |
| |
| Go downloaded this dependency because you added it to the `import` |
| declaration in the previous step. For more about dependency tracking, |
| see [Adding a dependency](/doc/modules/managing-dependencies#adding_dependency). |
| |
| 2. From the command prompt, set the `DBUSER` and `DBPASS` environment variables |
| for use by the Go program. |
| |
| On Linux or Mac: |
| |
| ``` |
| $ export DBUSER=username |
| $ export DBPASS=password |
| ``` |
| |
| On Windows: |
| |
| ``` |
| C:\Users\you\data-access> set DBUSER=username |
| C:\Users\you\data-access> set DBPASS=password |
| ``` |
| |
| 3. From the command line in the directory containing main.go, run the code by |
| typing `go run` with a dot argument to mean "run the package in the |
| current directory." |
| |
| ``` |
| $ go run . |
| Connected! |
| ``` |
| |
| You can connect! Next, you'll query for some data. |
| |
| ## Query for multiple rows {#multiple_rows} |
| |
| In this section, you'll use Go to execute an SQL query designed to return |
| multiple rows. |
| |
| For SQL statements that might return multiple rows, you use the `Query` method |
| from the `database/sql` package, then loop through the rows it returns. (You'll |
| learn how to query for a single row later, in the section |
| [Query for a single row](#single_row).) |
| #### Write the code |
| |
| 1. Into main.go, immediately above `func main`, paste the following definition |
| of an `Album` struct. You'll use this to hold row data returned from the |
| query. |
| |
| ``` |
| type Album struct { |
| ID int64 |
| Title string |
| Artist string |
| Price float32 |
| } |
| ``` |
| |
| 2. Beneath `func main`, paste the following `albumsByArtist` function to query |
| the database. |
| |
| ``` |
| // albumsByArtist queries for albums that have the specified artist name. |
| func albumsByArtist(name string) ([]Album, error) { |
| // An albums slice to hold data from returned rows. |
| var albums []Album |
| |
| rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name) |
| if err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| defer rows.Close() |
| // Loop through rows, using Scan to assign column data to struct fields. |
| for rows.Next() { |
| var alb Album |
| if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| albums = append(albums, alb) |
| } |
| if err := rows.Err(); err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| return albums, nil |
| } |
| ``` |
| |
| In this code, you: |
| |
| * Declare an `albums` slice of the `Album` type you defined. This will hold |
| data from returned rows. Struct field names and types correspond to |
| database column names and types. |
| |
| * Use [`DB.Query`](https://pkg.go.dev/database/sql#DB.Query) to |
| execute a `SELECT` statement to query for albums with the |
| specified artist name. |
| |
| `Query`'s first parameter is the SQL statement. After the |
| parameter, you can pass zero or more parameters of any type. These provide |
| a place for you to specify the values for parameters in your SQL statement. |
| By separating the SQL statement from parameter values (rather than |
| concatenating them with, say, `fmt.Sprintf`), you enable the |
| `database/sql` package to send the values separate from the SQL |
| text, removing any SQL injection risk. |
| |
| * Defer closing `rows` so that any resources it holds will be released when |
| the function exits. |
| |
| * Loop through the returned rows, using |
| [`Rows.Scan`](https://pkg.go.dev/database/sql#Rows.Scan) to |
| assign each row’s column values to `Album` struct fields. |
| |
| `Scan` takes a list of pointers to Go values, where the column |
| values will be written. Here, you pass pointers to fields in the |
| `alb` variable, created using the `&` operator. |
| `Scan` writes through the pointers to update the struct fields. |
| |
| * Inside the loop, check for an error from scanning column values into the |
| struct fields. |
| |
| * Inside the loop, append the new `alb` to the `albums` slice. |
| |
| * After the loop, check for an error from the overall query, using |
| `rows.Err`. Note that if the query itself fails, checking for an error |
| here is the only way to find out that the results are incomplete. |
| |
| 3. Update your `main` function to call `albumsByArtist`. |
| |
| To the end of `func main`, add the following code. |
| |
| ``` |
| albums, err := albumsByArtist("John Coltrane") |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("Albums found: %v\n", albums) |
| ``` |
| |
| In the new code, you now: |
| |
| * Call the `albumsByArtist` function you added, assigning its return value to |
| a new `albums` variable. |
| |
| * Print the result. |
| |
| #### Run the code |
| |
| From the command line in the directory containing main.go, run the code. |
| |
| ``` |
| $ go run . |
| Connected! |
| Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}] |
| ``` |
| |
| Next, you'll query for a single row. |
| |
| ## Query for a single row {#single_row} |
| |
| In this section, you'll use Go to query for a single row in the database. |
| |
| For SQL statements you know will return at most a single row, you can use |
| `QueryRow`, which is simpler than using a `Query` loop. |
| |
| #### Write the code |
| |
| 1. Beneath `albumsByArtist`, paste the following `albumByID` function. |
| |
| ``` |
| // albumByID queries for the album with the specified ID. |
| func albumByID(id int64) (Album, error) { |
| // An album to hold data from the returned row. |
| var alb Album |
| |
| row := db.QueryRow("SELECT * FROM album WHERE id = ?", id) |
| if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil { |
| if err == sql.ErrNoRows { |
| return alb, fmt.Errorf("albumsById %d: no such album", id) |
| } |
| return alb, fmt.Errorf("albumsById %d: %v", id, err) |
| } |
| return alb, nil |
| } |
| ``` |
| |
| In this code, you: |
| |
| * Use [`DB.QueryRow`](https://pkg.go.dev/database/sql#DB.QueryRow) |
| to execute a `SELECT` statement to query for an album with the |
| specified ID. |
| |
| It returns an `sql.Row`. To simplify the calling code |
| (your code!), `QueryRow` doesn't return an error. Instead, |
| it arranges to return any query error (such as `sql.ErrNoRows`) |
| from `Rows.Scan` later. |
| |
| * Use [`Row.Scan`](https://pkg.go.dev/database/sql#Row.Scan) to copy |
| column values into struct fields. |
| |
| * Check for an error from `Scan`. |
| |
| The special error `sql.ErrNoRows` indicates that the query returned no |
| rows. Typically that error is worth replacing with more specific text, |
| such as “no such album” here. |
| |
| 2. Update `main` to call `albumByID`. |
| |
| To the end of `func main`, add the following code. |
| |
| ``` |
| // Hard-code ID 2 here to test the query. |
| alb, err := albumByID(2) |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("Album found: %v\n", alb) |
| ``` |
| |
| In the new code, you now: |
| |
| * Call the `albumByID` function you added. |
| |
| * Print the album ID returned. |
| |
| #### Run the code |
| |
| From the command line in the directory containing main.go, run the code. |
| |
| |
| ``` |
| $ go run . |
| Connected! |
| Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}] |
| Album found: {2 Giant Steps John Coltrane 63.99} |
| ``` |
| |
| Next, you'll add an album to the database. |
| |
| ## Add data {#add_data} |
| |
| In this section, you'll use Go to execute an SQL `INSERT` statement to add a |
| new row to the database. |
| |
| You’ve seen how to use `Query` and `QueryRow` with SQL statements that |
| return data. To execute SQL statements that _don't_ return data, you use `Exec`. |
| |
| #### Write the code |
| |
| 1. Beneath `albumByID`, paste the following `addAlbum` function to insert a new |
| album in the database, then save the main.go. |
| |
| ``` |
| // addAlbum adds the specified album to the database, |
| // returning the album ID of the new entry |
| func addAlbum(alb Album) (int64, error) { |
| result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price) |
| if err != nil { |
| return 0, fmt.Errorf("addAlbum: %v", err) |
| } |
| id, err := result.LastInsertId() |
| if err != nil { |
| return 0, fmt.Errorf("addAlbum: %v", err) |
| } |
| return id, nil |
| } |
| ``` |
| |
| In this code, you: |
| |
| * Use [`DB.Exec`](https://pkg.go.dev/database/sql#DB.Exec) to |
| execute an `INSERT` statement. |
| |
| Like `Query`, `Exec` takes an SQL statement followed |
| by parameter values for the SQL statement. |
| |
| * Check for an error from the attempt to `INSERT`. |
| |
| * Retrieve the ID of the inserted database row using |
| [`Result.LastInsertId`](https://pkg.go.dev/database/sql#Result.LastInsertId). |
| |
| * Check for an error from the attempt to retrieve the ID. |
| |
| 2. Update `main` to call the new `addAlbum` function. |
| |
| To the end of `func main`, add the following code. |
| |
| ``` |
| albID, err := addAlbum(Album{ |
| Title: "The Modern Sound of Betty Carter", |
| Artist: "Betty Carter", |
| Price: 49.99, |
| }) |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("ID of added album: %v\n", albID) |
| ``` |
| |
| In the new code, you now: |
| |
| * Call `addAlbum` with a new album, assigning the ID of the album you're |
| adding to an `albID` variable. |
| |
| #### Run the code |
| |
| From the command line in the directory containing main.go, run the code. |
| |
| ``` |
| $ go run . |
| Connected! |
| Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}] |
| Album found: {2 Giant Steps John Coltrane 63.99} |
| ID of added album: 5 |
| ``` |
| |
| ## Conclusion {#conclusion} |
| |
| Congratulations! You've just used Go to perform simple actions with a |
| relational database. |
| |
| Suggested next topics: |
| |
| * Take a look at the data access guide, which includes more information |
| about the subjects only touched on here. |
| |
| * If you're new to Go, you'll find useful best practices described in |
| [Effective Go](/doc/effective_go) and [How to write Go code](/doc/code). |
| |
| * The [Go Tour](/tour/) is a great step-by-step |
| introduction to Go fundamentals. |
| |
| ## Completed code {#completed_code} |
| |
| This section contains the code for the application you build with this tutorial. |
| |
| ``` |
| package main |
| |
| import ( |
| "database/sql" |
| "fmt" |
| "log" |
| "os" |
| |
| "github.com/go-sql-driver/mysql" |
| ) |
| |
| var db *sql.DB |
| |
| type Album struct { |
| ID int64 |
| Title string |
| Artist string |
| Price float32 |
| } |
| |
| func main() { |
| // Capture connection properties. |
| cfg := mysql.NewConfig() |
| cfg.User = os.Getenv("DBUSER") |
| cfg.Passwd = os.Getenv("DBPASS") |
| cfg.Net = "tcp" |
| cfg.Addr = "127.0.0.1:3306" |
| cfg.DBName = "recordings" |
| |
| // Get a database handle. |
| var err error |
| db, err = sql.Open("mysql", cfg.FormatDSN()) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| pingErr := db.Ping() |
| if pingErr != nil { |
| log.Fatal(pingErr) |
| } |
| fmt.Println("Connected!") |
| |
| albums, err := albumsByArtist("John Coltrane") |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("Albums found: %v\n", albums) |
| |
| // Hard-code ID 2 here to test the query. |
| alb, err := albumByID(2) |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("Album found: %v\n", alb) |
| |
| albID, err := addAlbum(Album{ |
| Title: "The Modern Sound of Betty Carter", |
| Artist: "Betty Carter", |
| Price: 49.99, |
| }) |
| if err != nil { |
| log.Fatal(err) |
| } |
| fmt.Printf("ID of added album: %v\n", albID) |
| } |
| |
| // albumsByArtist queries for albums that have the specified artist name. |
| func albumsByArtist(name string) ([]Album, error) { |
| // An albums slice to hold data from returned rows. |
| var albums []Album |
| |
| rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name) |
| if err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| defer rows.Close() |
| // Loop through rows, using Scan to assign column data to struct fields. |
| for rows.Next() { |
| var alb Album |
| if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| albums = append(albums, alb) |
| } |
| if err := rows.Err(); err != nil { |
| return nil, fmt.Errorf("albumsByArtist %q: %v", name, err) |
| } |
| return albums, nil |
| } |
| |
| // albumByID queries for the album with the specified ID. |
| func albumByID(id int64) (Album, error) { |
| // An album to hold data from the returned row. |
| var alb Album |
| |
| row := db.QueryRow("SELECT * FROM album WHERE id = ?", id) |
| if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil { |
| if err == sql.ErrNoRows { |
| return alb, fmt.Errorf("albumsById %d: no such album", id) |
| } |
| return alb, fmt.Errorf("albumsById %d: %v", id, err) |
| } |
| return alb, nil |
| } |
| |
| // addAlbum adds the specified album to the database, |
| // returning the album ID of the new entry |
| func addAlbum(alb Album) (int64, error) { |
| result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price) |
| if err != nil { |
| return 0, fmt.Errorf("addAlbum: %v", err) |
| } |
| id, err := result.LastInsertId() |
| if err != nil { |
| return 0, fmt.Errorf("addAlbum: %v", err) |
| } |
| return id, nil |
| } |
| ``` |