Wrapped sql.DB by struct can't invoke methods in instance - go

My goal is using a struct to wrap sql.DB to do something more than sql.DB.
The instance I create can't work and give me this error when I run it.
DbConn.db.prepare undefined (cannot refer to unexported field or method sql.(*DB)."".prepare)
My code is:
type DatabaseConn struct {
driverName string
databaseName string
userName string
password string
dataSourceName string
db *sql.DB
}
func (d DatabaseConn)Open() error {
d.driverName = DB_DRNAME
d.userName = DB_UNAME
d.password = DB_PWD
d.databaseName = DB_DBNAME
d.dataSourceName = fmt.Sprintf("%s:%s#/%s?charset=utf8",d.userName, d.password, d.databaseName)
db, err := sql.Open(d.driverName, d.dataSourceName)
return err
}
func (d *DatabaseConn)Close() error {
defer func() {
if err := recover(); err != nil {
fmt.Println("Trying to handle error in DatabaseConn.Close(): ", err)
}
}()
err := d.db.Close()
return err
}
I am trying to create an instance and invoke sql.DB method.
var dbConn DatabaseConn
dbConn.Open()
defer dbConn.Close()
dbQuery := fmt.Sprintf("SELECT *, FROM ms_node WHERE node_id = ?")
getNodeRecord, err := dbConn.db.prepare(dbQuery)
The error message is pointed here.
./server.go:343: dbConn.db.prepare undefined (cannot refer to unexported field or method sql.(*DB)."".prepare)
(Personal background: I am newbie for 2 weeks)

The function Prepare starts with a capital letter ;)
So you should have:
getNodeRecord, err := dbConn.db.Prepare(dbQuery)

Related

What type should result be in golang smart contract binding call

I am calling this code:
package multicall
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
var multicallContractAddress = common.HexToAddress("0x5e227AD1969Ea493B43F840cfF78d08a6fc17796")
var multicallContractEthBalanceSelector = "4d2301cc"
func GetBalances(addresses []string, ETHProviderURL string) ([]string, error) {
ethProvider, err := ethclient.Dial(ETHProviderURL)
if err != nil {
panic(err)
}
multicallContract, err := NewMulticallCaller(multicallContractAddress, ethProvider)
if err != nil {
panic(err)
}
fmt.Println("multicallContract: ", multicallContract)
var calls = []MulticallCall{}
for _, address := range addresses {
hashAddress := common.HexToHash(address)
call := MulticallCall{multicallContractAddress, []byte("0x"+multicallContractEthBalanceSelector+hashAddress.String()[2:])}
calls = append(calls, call)
}
fmt.Println(string(calls[0].CallData))
var results []byte
err = multicallContract.contract.Call(&bind.CallOpts{}, results, "aggregate", common.Hex2Bytes(addresses[0]))
if err != nil {
panic(err)
}
fmt.Println("Result: ", &results)
return nil, nil
}
like this:
multicall.GetBalances([]string{"<MY_ADDRESS>"}, "<INFURA_API_KEY>")
And it returns this error:
# github.com/mteam88/keyswarm/multicall
multicall/multicall.go:37:58: cannot use results (variable of type []byte) as type *[]interface{} in argument to multicallContract.contract.Call
It seems like results should be a specific type/interface (maybe []uint8) but I can't seem to pass it like that to the Call function.
I have tried tons of things and I can't seem to get it. My Google diving has uncovered this Why am I getting a compile error 'cannot use ... as type uint8 in argument to ...' when the parameter is an int which led me to my previous assumption.
If you -1 please comment (:
Please provide links to relevant documentation in your answer.
NOTE: https://github.com/mteam88/keyswarm/blob/multicall/multicall/multicallContract.go is the location of the defenitions of MulticallCall and other Multicall prefixed defenitions.

Error while trying to fetch queryresult.KV object in JSON.Unmarshal

I am a little bit confused here and although I have searched a lot on this, something is clearly missing from my knowledge and I am asking your help.
I have created a Hyperledger Fabric Network and installed a chaincode in it. And I want to make a function that retrieves all the World State inputs about the Keys. I have done it already with the bytes.Buffer and it worked. But what I want to do is to do it with a struct.
So, I created the following struct that has only the key:
type WSKeys struct {
Key string `json: "key"`
Namespace string `json: "Namespace"`
}
And this is my code function:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArrayStr []WSKeys
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
var qry_key_json WSKeys
json.Unmarshal([]byte(queryResponse), &qry_key_json)
keyArray = append(keyArray, qry_key_json)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
When executing the above I get the following error:
cannot convert queryResponse (type *queryresult.KV) to type []byte
I can get the results correctly if I, for example do this:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArray []string
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
keyArray = append(keyArray, queryResponse.Key)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
But, why I get the above error when trying to add the queryResponse into a custom struct?
Do I need to add it to a struct that is only its type?
Please someone can explain what I am missing here?
The error statement is verbose enough to indicate, that your []byte conversion failed for the type queryResponse which, with a bit of lookup seems to be a struct type. In Go you cannot natively convert a struct instance to its constituent bytes without encoding using gob or other means.
Perhaps your intention was to use the Key record in the struct for un-marshalling
json.Unmarshal([]byte(queryResponse.Key), &qry_key_json)

Godooc won't read my code, or godoc can't read anonymouse function?

Recently I tried to document my code, but I had some trouble using godoc because there's some function that didn't came up when I ran godoc -http:localhost:6060
This is what my code looks like:
type MongoDBInterface interface {
ExecuteTransaction(operation func(mongoClient MongoDBInterface) error) error
Count(tableName string, clause bson.M) (int, error)
Distinct(tableName, fieldName string, clause bson.M) ([]interface{}, error)
InsertOrUpdate(tableName string, clause bson.M, data models.BaseModelInterface) (primitive.ObjectID, error)
InsertOrUpdateFields(tableName string, clause bson.M, data interface{}) (primitive.ObjectID, error)
Insert(tableName string, data models.BaseModelInterface) (primitive.ObjectID, error)
Update(tableName string, clause bson.M, data models.BaseModelInterface) error
UpdateFields(tableName string, clause bson.M, data interface{}) error
FindOne(tableName string, clause, opt bson.M, result interface{}) error
FindMany(tableName string, clause, opt bson.M, result interface{}) error
Truncate(tableName string) error
Delete(tableName string, clause bson.M) error
Aggregate(tableName string, pipelines interface{}, result interface{}) error
EnsureCollections() error
}
type mongoDB struct {
session mongo.Session
db *mongo.Database
ctx context.Context
isTransactionEnabled bool
isConnected bool
connString string
}
// NewMongoDB definition
func NewMongoDB() MongoDBInterface {
mongoClient := new(mongoDB)
mongoClient.ctx = context.Background()
dbHost := os.Getenv("DB_HOST")
if dbHost == "" {
dbHost = "localhost"
}
dbUser := os.Getenv("DB_USERNAME")
if dbUser == "" {
dbUser = "dbadmin"
}
dbPswd := os.Getenv("DB_PASSWORD")
if dbPswd == "" {
dbPswd = "dbpassword"
}
dbName := os.Getenv("DB_NAME")
if dbName == "" {
dbName = "dbname"
}
dbAuth := os.Getenv("DB_AUTH")
if dbAuth == "" {
dbAuth = "admin"
}
dbMode := os.Getenv("DB_MODE")
if dbMode == "" {
dbMode = "admin"
}
// temporary
connString := fmt.Sprintf("mongodb+srv://%s:%s#%s/%s?retryWrites=true&w=majority", dbUser, dbPswd, dbHost, dbName)
mongoClient.connString = connString
return mongoClient
}
// connect to mongodb server
func (s *mongoDB) connect() error {
// get query string from env,
// then parse it to get db name
// connString := os.Getenv("MONGODB_CONN_STRING")
connString := s.connString
log.Println("ConnString =>", connString)
parts := strings.Split(connString, "/")
dbName := strings.Split(parts[len(parts)-1], "?")[0]
// prepare options object for connecting to mongodb
opt := options.Client()
opt.ApplyURI(connString)
// set the timeout info from data defined in the env
// timeout, _ := strconv.Atoi(os.Getenv("MONGODB_TIMEOUT_IN_SECOND"))
timeout := 120
timeoutDuration := time.Duration(timeout) * time.Second
opt.ConnectTimeout = &timeoutDuration
// create client object
client, err := mongo.NewClient(opt)
if err != nil {
log.Println(err.Error())
return err
}
// connect to the db server
err = client.Connect(context.Background())
if err != nil {
log.Println(err.Error())
return err
}
// start new session
session, err := client.StartSession()
if err != nil {
log.Println(err.Error())
return err
}
// store session and db info into props
s.session = session
s.db = client.Database(dbName)
s.isConnected = true
log.Println("Connected to database")
return nil
}
The problem is godoc will never render func (s *mongoDB) connect() error but I need it to be documented, can you guys explain to me what's going on with godoc? Or maybe you can give me some solutions and tips for documenting Go code.
You can refer this doc: https://pkg.go.dev/golang.org/x/tools/cmd/godoc
The presentation mode of web pages served by godoc can be controlled with the "m" URL parameter; it accepts a comma-separated list of flag names as value:
- all show documentation for all declarations, not just the exported ones
- methods show all embedded methods, not just those of unexported anonymous fields
- src show the original source code rather than the extracted documentation
- flat present flat (not indented) directory listings using full paths
For instance, https://golang.org/pkg/math/big/?m=all shows the documentation for all (not just the exported) declarations of package big.
?m=all documents all declaration including the non-exported methods

Golang Standard Package Structure

I am faily new to Go and I am trying to create a structured application using guidance from Ben Johnson's webpage. Unfortunately, his example is not a complete working application.
His webpage is https://medium.com/#benbjohnson/standard-package-layout-7cdbc8391fc1
I have tried to use his methods and I keep getting "Undefined: db" error. It doesn't tell me what line is causing the error, just the file "MSSQL.go"
Could someone help with guidance to help me fix this error?
Edited code with accepted solution.
StatementPrinter.go
package statementprinter
type Statement struct {
CustomerId string
CustomerName string
}
type StatementService interface {
Statement(id string) (*Statement, error)
}
main.go
package main
import (
"fmt"
"log"
"github.com/ybenjolin/StatementPrinter"
"github.com/ybenjolin/StatementPrinter/mssql"
"database/sql"
_ "github.com/alexbrainman/odbc"
)
const DB_INFO = "Driver={SQL Server};Server=cdc-edb2;Database=CostarReports;Trusted_Connection=yes;"
var db *sql.DB
func init() {
var err error
db, err = sql.Open("odbc", DB_INFO)
if err != nil {
log.Fatal("Error opening database connection.\n", err.Error())
}
err = db.Ping()
if err != nil {
log.Fatal("Error pinging database server.\n", err.Error())
}
fmt.Println("Database connection established.")
}
func main () {
var err error
defer db.Close()
// Create services
// Changes required here. Was ss := &statementprinter.Stat..
ss := &mssql.StatementService{DB: db}
// Use service
var s *statementprinter.Statement
s, err = ss.Statement("101583")
if err != nil {
log.Fatal("Query failed:", err.Error())
}
fmt.Printf("Statement: %+v\n", s)
}
mssql.go
package mssql
import (
_ "github.com/alexbrainman/odbc"
"database/sql"
"github.com/ybenjolin/StatementPrinter"
)
// StatementService represents a MSSQL implementation of statemenetprinter.StatementService.
type StatementService struct {
DB *sql.DB
}
// Statement returns a statement for a given customer.
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
// Changes required here. Was row := db.Query......
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
This seems like it's just a typo. It seems like the problematic line is in the method
func (s *StatementService) Statement(customer string)
in mssql.go,
row := db.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
QueryRow is supposed to be a method of db, but db is not defined. However, in the struct
type StatementService struct {
DB *sql.DB
}
there's a *sql.DB instance. The method you're using has a *StatementService parameter, s. So, my guess is the intention would be to access the sql.DB field in s like so
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
//CHANGED LINE:
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
Then, the method is called in main.go, and is passed a StatementService instance that contains a database:
ss := &statementprinter.StatementService{DB: db}
I believe you need to change this line to
ss := &mssql.StatementService{DB: db}
becuase that's the actual interface implementation. The line you have now treats the StatementService interface like a struct which will not compile.
The global db in main.go lives for the lifetime of the application. It's just a pointer which is copied around for use.

Need type assertion on functions

I'm trying to learn type assertion and conversion. It's kinda complicated for me.
I have this example: (I'm using gin framework)
type Env struct {
db *sql.DB
}
func main() {
r := gin.Default()
// Initiate session management (cookie-based)
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
db, _ := sql.Open("sqlite3", "./libreread.db")
defer db.Close()
env := &Env{db: db}
r.GET("/", env.GetHomePage)
}
func (e *Env) _GetUserId(email string) int64 {
rows, err := e.db.Query("SELECT `id` FROM `user` WHERE `email` = ?", email)
CheckError(err)
var userId int64
if rows.Next() {
err := rows.Scan(&userId)
CheckError(err)
}
rows.Close()
return userId
}
func (e *Env) GetHomePage(c *gin.Context) {
session := sessions.Default(c)
email := session.Get("email")
if email != nil {
name := c.Param("bookname")
userId := e._GetUserId(email) // I'm stuck here.
}
So, in the above code.. I'm setting db Env type and passing it to router functions. From there, I need to call another function. How to do that?
When I call e._GetUserId(email), it says
cannot convert email (type interface {}) to type Env: need type assertion
How to solve this problem?. Do I need to use inferface{} instead of struct for Env type?
Drafting answer based on conversation from my comments.
Method session.Get("email") returns interface{} type.
And method e._GetUserId() accepts string parameter, so you need to do type assertion as string like -
e._GetUserId(email.(string))

Resources