affected/package: database/sql "Deficiency in the code" - go

go version: go 1.19
my problem is with the database/sql package. I have plsql code available in service. values are sent to the service via map. but the number is not limited,
I need to send to db.exec() via sql.Named, I checked with loop and with interface, it didn't work.
Please help me.
my codes are:
in the controller
const begin = "
jsonData.Put('stateid', :stateid);
"
array_for_param := map[string]string{
"stateid": "a",
}
temp, err := services.Perform(c, begin, array_for_param, i_User_Id)
if err != nil {
log.Fatal(err)
}
service code:
var params []interface{}
for key, value := range array_for_param {
params = append(params, sql.Named(key, value))
}
if _, err := db.Exec(declare+begin+end,
sql.Named("i_User_Id", i_User_Id),
params...,
); err != nil {
log.Fatal(err)
}
The main problem I have is that I need to send the sql.Named code using a for, which is an unknown number
I did that too
if _, err := db.Exec(declare+begin+end,
sql.Named("i_User_Id", i_User_Id),
for key, value := range array_for_param {
return sql.Named(key, value)
}
); err != nil {
log.Fatal(err)
}

Related

How to pass in variable length variable to statement.Exec?

For example
func Query(myvarlist []string) {
stmt, err := tx.Prepare("SELECT * FROM mytable WHERE
myvar = $1 AND myvar2 = $2".......)
defer stmt.Close()
if _, err := stmt.Exec(myvarlist....); err != nil {
}
Can we pass in Exec a variable length variable?
You can do something like the this:
func Query() {
db, err := sql.Open("pgx", `postgresql://CONNSTRING`)
if err != nil {
panic(err)
}
queryParamMap := map[string]string{
"id": "6",
"name": "1",
}
// Build up statement and params
cols := make([]string, len(queryParamMap))
args := make([]any, len(queryParamMap))
i := 0
for k, v := range queryParamMap {
cols[i] = fmt.Sprintf("%s = $%d", k, i+1) // WARNING - SQL Injection possible here if col names are not sanitised
args[i] = v
i++
}
// Using Prepare because the question used it but this is only worthwhile if you will run stmt multiple times
stmt, err := db.Prepare(`SELECT id FROM devices WHERE ` + strings.Join(cols, " and "))
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.Query(args...)
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var id int
if err = rows.Scan(&id); err != nil {
panic(err)
}
fmt.Println("value:", id)
}
// Should check rows.Error() etc but this is just an example...
}
I've put the column names and values into a map because it was not clear where any extra column names would come from in your question but hopefully this provides the info you need.
This example is also using Query rather than Exec (because it's easier to test) but the same approach will work with Exec.
Note: Take a look at squirrel for an example of how to take this a lot further....

Update mongo db collection to create new field with unique values without impacting existing data using mongo go driver

I am new to mongo and mongo go driver. Need to add new field "uri" to my collection with existing data - using mongo go driver. New field needs to be populated with unique values so that unique index can be created on it. the collection uses _id as well, if there is a way we can populate new field based on _id field that will work as well.
I am trying below code, not sure how to populate unique values.
//Step1: update all documents to add new field with unique values
_, err := myColl.UpdateMany(
ctx,
bson.D{},// select all docs in collection
bson.D{
{"$set", bson.D{{"uri", GenerateRandomUniqueString()}}},
},
)
if err != nil {
return err
}
// then next step is to create index on this field:
key := bson.D{{"uri", 1}}
opt := options.Index().SetName("uri-index").SetUnique(true)
model := mongo.IndexModel{Keys: key, Options: opt}
_, err = myColl.Indexes().CreateOne(ctx, model)
if err != nil {
return err
}
Once the index is set up, old records will marked read only, but we can not delete those. New data will have unique 'uri' string value.
Any help is much appreciated.
Using above code fails while unique index creation, as the same value is used for backfill.
I tried this as well:
func BackFillUri(db *mongo.Database) error {
myColl := db.Collection("myColl")
ctx := context.Background()
cursor, err := myColl.Find(ctx, bson.M{})
if err != nil {
return err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var ds bson.M
if err = cursor.Decode(&ds); err != nil {
return err
}
_, err1 := myColl.UpdateOne(
ctx,
bson.D{"_id": ds.ObjectId},
bson.D{
{"$set", bson.D{{"uri", rand.Float64()}}},
},
)
if err1 != nil {
return err1
}
}
return nil
}
But i am getting quite a few errors and not sure if any of the above logic is correct
I finally used below code, hope it helps someone who's new like me :-)
const charset = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func RandomUniqueString(length int) string {
return StringWithCharset(length, charset)
}
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
// Adds index on uri
func AddUriIndex(db *mongo.Database) error {
mycoll := db.Collection("mycoll")
ctx := context.Background()
//backfill code starts
type attribute struct {
Key string `bson:"key"`
Value interface{} `bson:"value"`
}
type item struct {
ID primitive.ObjectID `bson:"_id"`
ResourceAttributes []attribute `bson:"resourceAttributes,omitempty"`
}
cursor, err := mycoll.Find(ctx, primitive.M{})
if err != nil {
return err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var result item
if err = cursor.Decode(&result); err != nil {
return err
}
//fmt.Println("Found() result:", result)
filter := primitive.M{"_id": result.ID}
update := primitive.M{"$set": primitive.M{"uri": RandomUniqueString(32)}}
if _, err := mycoll.UpdateOne(ctx, filter, update); err != nil {
return err
}
}
//add uri-index starts
key := bson.D{{"uri", 1}}
opt := options.Index().
SetName("uri-index").
SetUnique(true)
model := mongo.IndexModel{Keys: key, Options: opt}
_, err = mycoll.Indexes().CreateOne(ctx, model)
if err != nil {
return err
}
return nil
}

Why am I always getting random results regardless of seed value?

I have a fairly complex Go application. It generates many random results in a long chain. It is seeded only once - when an HTTP request comes in.
No matter what the seed is - whether it's unix time, or whether it's my own alphanumeric seed function - it always generates completely random results.
I've attempted cutting out my alphanumeric seed function, but that doesn't alter the behavior. I have also tried setting the seed always to 1111. This has no effect.
Here is a sample (verbose and lifted directly from the source, since that's what was requested):
func main() {
sentryDSN := os.Getenv("SENTRY_DSN")
sentry.Init(sentry.ClientOptions{
Dsn: sentryDSN,
})
sentryHandler := sentryhttp.New(sentryhttp.Options{
Repanic: true,
})
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.URLFormat)
r.Use(middleware.SetHeader("Content-Type", "application/json"))
r.Use(middleware.Timeout(60 * time.Second))
r.Get("/buildingstyle", sentryHandler.HandleFunc(getBuildingStyleRandom))
r.Get("/buildingstyle/{id}", sentryHandler.HandleFunc(getBuildingStyle))
r.Get("/character", sentryHandler.HandleFunc(getCharacterRandom))
r.Get("/character/{id}", sentryHandler.HandleFunc(getCharacter))
r.Get("/climate", sentryHandler.HandleFunc(getClimateRandom))
r.Get("/climate/{id}", sentryHandler.HandleFunc(getClimate))
port := 7531
fmt.Printf("World Generator API is running on http://localhost:%d.\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), r))
}
func SeedFromString(source string) error {
h := md5.New()
_, err := io.WriteString(h, source)
if err != nil {
err = fmt.Errorf("Failed to seed random number generator: %w", err)
return err
}
seed := binary.BigEndian.Uint64(h.Sum(nil))
rand.Seed(int64(seed))
return nil
}
func getClimate(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
var o climate.SimplifiedClimate
err := random.SeedFromString(id)
if err != nil {
handleError(w, r, err)
return
}
randomClimate, err := climate.Random()
if err != nil {
handleError(w, r, err)
return
}
o = randomClimate.Simplify()
json.NewEncoder(w).Encode(o)
}
// Generate generates a climate with a given name
func Generate(name string) (Climate, error) {
rawClimate, err := ByName(name)
if err != nil {
err = fmt.Errorf("Could not generate climate by name: %w", err)
return Climate{}, err
}
climate, err := rawClimate.populate()
if err != nil {
err = fmt.Errorf("Could not generate climate by name: %w", err)
return Climate{}, err
}
return climate, nil
}
func (climate Climate) populate() (Climate, error) {
gems := mineral.Gems()
insects := climate.getFilteredInsects()
metals := mineral.Metals()
stones := mineral.Stones()
trees := climate.getFilteredTrees()
climate.Seasons = climate.getSeasons()
lakeChance := rand.Intn(100)
riverChance := rand.Intn(100)
oceanChance := rand.Intn(100)
wetlandsChance := rand.Intn(100)
if lakeChance > 30 {
climate.HasLakes = true
}
if riverChance > 20 {
climate.HasRivers = true
}
if oceanChance > 80 {
climate.HasOcean = true
}
if wetlandsChance > 80 {
climate.HasWetlands = true
}
soils := climate.getFilteredSoils()
if climate.HasLakes || climate.HasRivers || climate.HasOcean {
climate.Fish = climate.getFish()
} else {
climate.Fish = []fish.Fish{}
}
climate.Insects = insect.RandomSubset(7, insects)
filteredMetals, err := mineral.RandomWeightedSet(climate.MaxMetals, metals)
if err != nil {
err = fmt.Errorf("Could not populate climate: %w", err)
return Climate{}, err
}
climate.Metals = filteredMetals
climate.Gems = mineral.Random(climate.MaxGems, gems)
climate.OtherMinerals = mineral.OtherMinerals()
climate.Animals, err = climate.getAnimals()
if err != nil {
err = fmt.Errorf("Could not populate climate: %w", err)
return Climate{}, err
}
climate.Plants, err = climate.getPlants()
if err != nil {
err = fmt.Errorf("Could not populate climate: %w", err)
return Climate{}, err
}
climate.Soils = soil.Random(climate.MaxSoils, soils)
climate.Stones = mineral.Random(climate.MaxStones, stones)
climate.Trees = tree.RandomSubset(climate.MaxTrees, trees)
resources := climate.getResources()
climate.Resources = resources
description, err := climate.getDescription()
if err != nil {
err = fmt.Errorf("Could not populate climate: %w", err)
return Climate{}, err
}
climate.Description = description
climate.Habitability = climate.calculateHabitability()
return climate, nil
}
Many of the functions like doStuff() return n random elements from a slice with some filtering applied.
All of this I expect to be consistent and identical when the same seed is used for multiple runs. However, this is not the case. Instead, the results are random every single time, regardless of seed value.
Is there some fundamental piece of rand.Intn()'s or rand.Seed()'s operation that I'm unaware of?
You're seeding the default source, which is used by many parts of the system. In a large, complicated project, it is very likely that there is some other part that is consuming an indeterminate number of random values dependent on the environment. It's even possible that somewhere else in your code you have a call to rand.Seed().
If you want your random values to be independent, create your own rand.Rand and use that for the pieces you want to be determined by your seed.
If you can reproduce this in a self-contained piece of code, then we can explore the specific case, but I expect when you create a self-contained piece of code the problem will go away because you will have removed the other value consumer(s).

Map response to a struct using Golang

I am attempting to map a response from an API to a struct using Golang.
The JSON that comes back when I view the link in the browser is below:
{
"GBP": 657.54
}
And I just want to map it to a simple struct like so:
type Price struct {
Name string
Value float64
}
Here is my current code.
func FetchCoinPrice(fsym string, tsyms string) Price {
url := fmt.Sprintf("https://min-api.cryptocompare.com/data/price?fsym=" + fsym + "&tsyms=" + tsyms)
fmt.Println("Requesting data from " + url)
price := Price{}
// getting the data using http
request, err := http.Get(url)
if err != nil {
log.Fatal(err.Error())
}
// Read the response body using ioutil
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Fatal(err.Error())
}
defer request.Body.Close()
if request.StatusCode == http.StatusOK {
json.Unmarshal(body, &price)
}
return price
}
At the moment all I receive is an empty struct, I know the link is bringing back the correct data and I've tested it in my browser.
The mapping doesn't work that way. Instead, you should use a map:
data := []byte(`{
"GBP": 657.54
}`)
priceMap := map[string]float64{}
err := json.Unmarshal(data, &priceMap)
// Check your errors!
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(priceMap)
This will print:
map[GBP:657.54]
You can then iterate over the map and build the struct you mentioned above, or just access the entry directly if you know the currency. eg: priceMap["GBP"]
You should really check your errors, especially if you're not getting the output you expect from Unmarshal.
The problem is that the unmarshaler cannot guess that keys in a JSON object should correspond to some value in a struct. Golang JSON mapping simply doesn't work that way.
However, you can make your "Price" type implement json.Unmarshaler to deserialize a message into a map of floats (map[string]float64) then ensure the shape is right and populate the struct accordingly:
func (p *Price) UnmarshalJSON(bs []byte) error {
kvs := map[string]float64{}
err := json.Unmarshal(bs, &kvs)
if err != nil {
return err
}
if len(kvs) != 1 {
return fmt.Errorf("expected 1 key, got %d", len(kvs))
}
for name, value := range kvs {
p.Name, p.Value = name, value
}
return nil
}
func main() {
jsonstr := `[{"GBP":657.54},{"USD":123.45}]`
ps := []Price{}
err := json.Unmarshal([]byte(jsonstr), &ps)
if err != nil {
panic(err)
}
// ps=[]main.Price{
// main.Price{Name:"GBP", Value:657.54},
// main.Price{Name:"USD", Value:123.45}
// }
}

How to query Redis db from golang using redigo library

I am trying to figure out what is the best way to query Redis db for multiple keys in one command.
I have seen MGET which can be used for redis-cli. But how you do that using redigo library from GOlang code. Imagine I have an array of keys and I want to take from Redis db all the values for those keys in one query.
Thanks in advance!
Assuming that c is a Redigo connection and keys is a []string of your keys:
var args []interface{}
for _, k := range keys {
args = append(args, k)
}
values, err := redis.Strings(c.Do("MGET", args...))
if err != nil {
// handle error
}
for _, v := range values {
fmt.Println(v)
}
The Go FAQ explains why you need to copy the keys. The spec describes how to pass a slice to a variadic param.
http://play.golang.org/p/FJazj_PuCq
func main() {
// connect to localhost, make sure to have redis-server running on the default port
conn, err := redis.Dial("tcp", ":6379")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// add some keys
if _, err = conn.Do("SET", "k1", "a"); err != nil {
log.Fatal(err)
}
if _, err = conn.Do("SET", "k2", "b"); err != nil {
log.Fatal(err)
}
// for fun, let's leave k3 non-existing
// get many keys in a single MGET, ask redigo for []string result
strs, err := redis.Strings(conn.Do("MGET", "k1", "k2", "k3"))
if err != nil {
log.Fatal(err)
}
// prints [a b ]
fmt.Println(strs)
// now what if we want some integers instead?
if _, err = conn.Do("SET", "k4", "1"); err != nil {
log.Fatal(err)
}
if _, err = conn.Do("SET", "k5", "2"); err != nil {
log.Fatal(err)
}
// get the keys, but ask redigo to give us a []interface{}
// (it doesn't have a redis.Ints helper).
vals, err := redis.Values(conn.Do("MGET", "k4", "k5", "k6"))
if err != nil {
log.Fatal(err)
}
// scan the []interface{} slice into a []int slice
var ints []int
if err = redis.ScanSlice(vals, &ints); err != nil {
log.Fatal(err)
}
// prints [1 2 0]
fmt.Println(ints)
}
UPDATE March 10th 2015: redigo now has a redis.Ints helper.

Resources