Home > Software engineering >  Golang error for SQL when trying to create a new user
Golang error for SQL when trying to create a new user

Time:12-14

I am receiving this error in my new_user.go when creating a user using Golang packages viper and cobra. The error is as follows:

cannot use result (variable of type sql.Result) as error value in return statement: sql.Result does not implement error (missing method Error)

My code is broken into 2 files which talk to each other, here is the tree hierarchy:

.
├── Makefile
├── README.md
├── cli
│   ├── config.go
│   ├── db-creds.yaml
│   ├── go.mod
│   ├── go.sum
│   ├── new_user.go
│   └── root.go
├── docker-compose.yaml
├── go.work
└── main.go

To connect to the database, I created a YAML file db-creds.yaml to pull the creds for config.go. No errors are popping up here:

config.go file

package cli

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    _ "github.com/lib/pq"
    "github.com/spf13/viper"
)

// var dialects = map[string]gorp.Dialect{
//  "postgres": gorp.PostgresDialect{},
//  "mysql":    gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"},
// }

// initConfig reads in config file and ENV variables if set
func initConfig() {
    if cfgFile != "" {
        viper.SetConfigFile(cfgFile)
    } else {
        viper.AddConfigPath("./")
        viper.SetConfigName("db-creds")
        viper.SetConfigType("yaml")
    }

    // If a config file is found, read it in:
    err := viper.ReadInConfig()
    if err == nil {
        fmt.Println("Fatal error config file: ", viper.ConfigFileUsed())
    }
    return
}

func getConnection() *sql.DB {

    // Make sure we only accept dialects that were compiled in.
    // dialect := viper.GetString("database.dialect")
    // _, exists := dialects[dialect]
    // if !exists {
    //  return nil, "", fmt.Errorf("Unsupported dialect: %s", dialect)
    // }

    // Will want to create another command that will use a mapping
    // to connect to a preset db in the yaml file.
    dsn := fmt.Sprintf("%s:%s@%s(%s)?parseTime=true",
        viper.GetString("mysql-5.7-dev.user"),
        viper.GetString("mysql-5.7-dev.password"),
        viper.GetString("mysql-5.7-dev.protocol"),
        viper.GetString("mysql-5.7-dev.address"),
    )
    viper.Set("database.datasource", dsn)

    db, err := sql.Open("msyql", viper.GetString("database.datasource"))
    if err != nil {
        fmt.Errorf("Cannot connect to database: %s", err)
    }

    return db
}

The error that I put at the top is where the error appears when I return my result. I am implementing the flag option for cobra to use -n follow by the name to represent the new user being added`.

new_user.go file

package cli

import (
    "log"

    "github.com/sethvargo/go-password/password"
    "github.com/spf13/cobra"
)

var name string

// newCmd represents the new command
var newCmd = &cobra.Command{
    Use:   "new",
    Short: "Create a new a user which will accommodate the individuals user name",
    Long:  `Create a new a user that will randomize a password to the specified user`,
    RunE: func(cmd *cobra.Command, args []string) error {

        db := getConnection()

        superSecretPassword, err := password.Generate(64, 10, 10, false, false)

        result, err := db.Exec("CREATE USER"   name   "'@'%'"   "IDENTIFIED BY"   superSecretPassword)
        if err != nil {
            log.Fatal(err)
        }

        // Will output the secret password combined with the user.
        log.Printf(superSecretPassword)

        return result <---- Error is here
    },
}

func init() {
    rootCmd.AddCommand(newCmd)

    newCmd.Flags().StringVarP(&name, "name", "n", "", "The name of user to be added")
    _ = newCmd.MarkFlagRequired("name")
}

The main purpose of this project are three things: 1.) Create a new user, 2.) Give any user specific permissions 3.) Delete them. That is my end goal. Going one step at a time and just ran into this error. Hope anyone can help me. Golang is new to me and started about 2 weeks ago.

CodePudding user response:

Go gives you a pretty clear indication about what's going on. The RunE member of cobra expects its callback to return an error (or nil, in case of success).

Here, you are returning result, which is not an error, but a specific type returned by your sql query. This is what your function should look like.

RunE: func(cmd *cobra.Command, args []string) error {

    db := getConnection()

    superSecretPassword, err := password.Generate(64, 10, 10, false, false)

    _, err := db.Exec("CREATE USER"   name   "'@'%'"   "IDENTIFIED BY"   superSecretPassword)
    if err != nil {
        // Don't Fatal uselessly, let cobra handle your error the way it
        // was designed to do it.
        return err
    }

    // Will output the secret password combined with the user.
    log.Printf(superSecretPassword)

    // Here is how you should indicate your callback terminated successfully.
    // Error is an interface, so it accepts nil values.
    return nil
}

If you need the result of the db.Exec command (which does not seem to be the case), you'll need to do all the required processing within your cobra callback, as it is not designed to return values to the main thread.

Error handling

A few bad practices about handling error I noticed in your code:

  • If a go function has an error return, don't panic or kill the program if something unexpected occurs (like you did with log.Fatal). Instead, use that error return to propagate the error to the main thread, and let it decide what to do.

  • On the other hand, don't return results if something went wrong. Your getConnection function should be able to return an error if it fails: func getConnection() (*sql.DB, error). You should then handle this error in your RunE function, instead of just logging it and processing normally.

  •  Tags:  
  • go
  • Related