How to upload a 0 value for not null in Go? - go

I'm trying to upload data to a PSQL database and receive the following message:
error:
null value in column \"alarm\" violates not-null constraint
I've tracked it down to being that the alarm value is 0, not null, but 0... However Golang does not recognize this... Is there a way to get around this quirk?
type HsDatum struct {
ID int64
UUID uuid.UUID
HsAssociationID int64
Timestamp time.Time `sql:",notnull"`
Value int `sql:",notnull"`
Alarm int `sql:",notnull"`
CreatedAt time.Time
UpdatedAt time.Time
}
hsDatum := hsdata.HsDatum{
HsAssociationID: systemAssoc.ID,
Timestamp: startTime,
Value: 1,
Alarm: int(alarm), // 0 in this example
}
Added more information about the database logic:
func (h *HsDatum) BeforeInsert(db orm.DB) error {
timeNow := timeutil.TimeNow()
h.CreatedAt = timeNow
h.UpdatedAt = timeNow
// Manage distinct alarm table by checking for new value
// Note: HS data needs to arrive in chronological order by timestamp
needsUpdate := false
lastAlarm, err := hsdistinctalarms.GetHsDistinctAlarmByMostRecent(h.HsAssociationID)
var timeGap time.Duration
if database.IsEmptyTable(err) {
// No values so mark it
needsUpdate = true
} else if err == nil {
if lastAlarm.Alarm != h.Alarm {
// New value so mark it
needsUpdate = true
}
timeGap = h.Timestamp.Sub(lastAlarm.LastUpdated)
} else {
logrus.Errorf("Failed to get most recent distinct alarm: %s", err)
}
if needsUpdate {
// Create new alarm
newAlarm := hsdistinctalarms.HsDistinctAlarm{
HsAssociationID: h.HsAssociationID,
Timestamp: h.Timestamp,
Alarm: h.Alarm,
LastUpdated: h.Timestamp,
}
err = hsdistinctalarms.CreateHsDistinctAlarm(&newAlarm)
if err != nil {
logrus.Errorf("Failed to create hs distinct alarm: %s", err)
}
} else {
// Update timestamp
lastAlarm.LastUpdated = h.Timestamp
err = hsdistinctalarms.UpdateHsDistinctAlarm(&lastAlarm)
if err != nil {
logrus.Errorf("Failed to update hs distinct alarm: %s", err)
}
}
return nil
}
// CreateHsDatum creates the resource.
// It modifies the parameter in place and returns error information.
func CreateHsDatum(h *HsDatum) error {
return database.GetDB().Insert(h)
}

Option 1:
use pointers
e.g.
Alarm *int `sql:",notnull"`
...
Alarm: new(int) // init with zero value
or
Alarm: &alarm // set a pointer to the alarm variable (should be int)
Ugly? Yes.
Option 2:
use sql.NullInt32
e.g.
Alarm sql.NullInt32 `sql:",notnull"`
...
var a sql.NullInt32
if err := a.Scan(alarm); err != nil {
// handle error
}
...
Alarm: a
...

Related

How can I compare Firestore time with Go time.Now()?

I use Go to build my game server. I am going to compare a time from time.Now() and one from a Firestore field. How can I do that in Go?
playerDataSnap, err := Instace.FireStoreClient.Collection("PlayerData").Doc(playerUID).Get(Instace.Context)
if err != nil {
log.Printf("EventModeFee Get PlayerData Fail: %v", err)
return
}
playerData := playerDataSnap.Data()
if value, exist := playerData["EventPlayTimes"]; exist {
eventPlayTimes = value.(int64)
}
if discount_Subscribe > 0 {
if value, exist := playerData["SubscriptionExpiredDate"]; exist { //Get Subscribe expired time
var expireTimeStamp = value //<-------------timestamp from firestore field
if time.Now().Before(expireTimeStamp) {//<---------------expireTimeStamp is not a valid type, but How can I convert it to a valid type
isSubscribed = true
}
}
}
expireTimeStamp is not a valid type, but how can I convert it to a valid one?
It turns out that I can directly assert the timestamp from Firestore field to time.Time with the following code:
if time.Now().Before(expireTimeStamp.(time.Time)) {
isSubscribed = true
}

[]bigquery.Value operation

I want to access bigquery array structure with golang.
GCP billing standard table query
q := client.Query(
SELECT billing_account_id,credits FROM +
"project.dataset.gcp_billing_export_xxxx" +
WHERE DATE(_PARTITIONTIME) = '2021-11-24' and array_length(credits) > 0 LIMIT 1)
and explore data
for {
var row []bigquery.Value
err := it.Next(&row)
if err == iterator.Done {
break
}
if err != nil {
return err
}
fmt.Fprintln(w, row[1] )
}
row[1] output :
[
[GCP Enhanced Support customers receive a 50% promotional discount that ends on Dec 31st 2021. -0.03 GCP Enhanced Support customers receive a 50% promotional discount that ends on Dec 31st 2021. DISCOUNT]
[Discount on Total Spend -0.001 Discount on Total Spend RESELLER_MARGIN]
]
row[1] looks like an arrary structure,but I can't access its element through row[1][0]
here is the error message:
invalid operation: row[1][0] (type "cloud.google.com/go/bigquery".Value does not support indexing
Any advices?
Regards,
Steven
Try to use Struct of Struct. credits is described here.
type creditsStruct struct {
id string
full_name string
type string
name string
amount float64
}
type myResultsStruct struct {
billing_account_id string
credits creditsStruct
}
for {
var c myResultsStruct
err := it.Next(&c)
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
fmt.Println(c)
}
You can use “func (*RowIterator) Next“.
Next loads the next row into dst. Its return value is iterator.Done if there are no more results. Once Next returns iterator.Done, all subsequent calls will return iterator.Done.
Each BigQuery column type corresponds to one or more Go types; a matching struct field must be of the correct type. The correspondences are:
STRING string
BOOL bool
INTEGER int, int8, int16, int32, int64, uint8, uint16, uint32
FLOAT float32, float64
BYTES []byte
TIMESTAMP time.Time
DATE civil.Date
TIME civil.Time
DATETIME civil.DateTime
You can see more information.
You can see this example:
package main
import (
"context"
"fmt"
"cloud.google.com/go/bigquery"
"google.golang.org/api/iterator"
)
func main() {
ctx := context.Background()
client, err := bigquery.NewClient(ctx, "project-id")
if err != nil {
// TODO: Handle error.
}
type score struct {
Name string
Num int
}
q := client.Query("select name, num from t1")
it, err := q.Read(ctx)
if err != nil {
// TODO: Handle error.
}
for {
var s score
err := it.Next(&s)
if err == iterator.Done {
break
}
if err != nil {
// TODO: Handle error.
}
fmt.Println(s)
}
}

Fatal error when retrieving multiple rows with gorm Find()

I'm stuck with an obvious operation: retrieving multiple rows using gorm.Find() method.
(resolver.go)
package resolver
type Root struct {
DB *gorm.DB
}
func (r *Root) Users(ctx context.Context) (*[]*UserResolver, error) {
var userRxs []*UserResolver
var users []model.User
// debug-start
// This is to prove r.DB is allocated and working
// It will print {2 alice#mail.com} in the console
var user model.User
r.DB.Find(&user)
log.Println(user)
// debug-end
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
for _, user := range users {
userRxs = append(userRxs, &UserResolver{user})
log.Println(user)
}
return &userRxs, nil
}
(model.go)
package model
type User struct {
ID graphql.ID `gorm:"primary_key"`
Email string `gorm:"unique;not null"`
}
The mysql table is filled with 2 values. Here is the content in json style:
{
{ Email: bob#mail.com },
{ Email: alice#mail.com },
}
This is the result when I run the program:
2020/05/13 12:23:17 Listening for requests on :8000
2020/05/13 12:23:22 {2 alice#mail.com}
2020/05/13 12:23:22 &{{{0 0} 0 0 0 0} 0xc0004cee40 <nil> 2 0xc00031e3c0 false 0 {0xc00035bea0} 0xc0004b3080 {{0 0} {<nil>} map[] 0} 0xc000408340 <nil> 0xc0004cee60 false <nil>}
What is wrong with my code? It seems from all the tuto/so/etc.. sources that I'm correctly defining a slice var and passing it to the Find() function..
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
Probably you forgot to mention Error property and returned object in this case is not nil for sure (please mention that Find returns not error interface in this case)
Please try something like that
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}
Hope it helps
You need to use a slice of pointers:
users := make([]*model.User, 0, 2)
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}

Problem parsing values of PostgreSQL TIMESTAMP type

In PostgreSQL, I have table called surveys.
CREATE TABLE SURVEYS(
SURVEY_ID UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
SURVEY_NAME VARCHAR NOT NULL,
SURVEY_DESCRIPTION TEXT,
START_PERIOD TIMESTAMP,
END_PERIOD TIMESTAMP
);
As you can see only SURVEY_ID and SURVEY_NAME columns are NOT NULL.
In Go, I want to create new entry in that table by POST request. I send JSON object like this:
{
"survey_name": "NAME",
"survey_description": "DESCRIPTION",
"start_period": "2019-01-01 00:00:00",
"end_period": "2019-02-28 23:59:59"
}
Unfortunatly it raise strange ERROR:
parsing time ""2019-01-01 00:00:00"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 00:00:00"" as "T"
Where I make mistake and how to fix my problem?
models/surveys.go:
import (
"database/sql"
"time"
)
type NullTime struct {
time.Time
Valid bool
}
type Survey struct {
ID int `json:"survey_id"`
Name string `json:"survey_name"`
Description sql.NullString `json:"survey_description"`
StartPeriod NullTime `json:"start_period"`
EndPeriod NullTime `json:"end_period"`
}
controllers/surveys.go:
var CreateSurvey = func(responseWriter http.ResponseWriter, request *http.Request) {
// Initialize variables.
survey := models.Survey{}
var err error
// The decoder introduces its own buffering and may read data from argument beyond the JSON values requested.
err = json.NewDecoder(request.Body).Decode(&survey)
if err != nil {
log.Println(err)
utils.ResponseWithError(responseWriter, http.StatusInternalServerError, err.Error())
return
}
defer request.Body.Close()
// Execute INSERT SQL statement.
_, err = database.DB.Exec("INSERT INTO surveys (survey_name, survey_description, start_period, end_period) VALUES ($1, $2, $3, $4);", survey.Name, survey.Description, survey.StartPeriod, survey.EndPeriod)
// Shape the response depending on the result of the previous command.
if err != nil {
log.Println(err)
utils.ResponseWithError(responseWriter, http.StatusInternalServerError, err.Error())
return
}
utils.ResponseWithSuccess(responseWriter, http.StatusCreated, "The new entry successfully created.")
}
The error already says what is wrong:
parsing time ""2019-01-01 00:00:00"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 00:00:00"" as "T"
You are passing "2019-01-01 00:00:00" while it expects a different time format, namely RFC3339 (UnmarshalJSON's default).
To solve this, you either want to pass the time in the expected format "2019-01-01T00:00:00Z00:00" or define your own type CustomTime like this:
const timeFormat = "2006-01-02 15:04:05"
type CustomTime time.Time
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
newTime, err := time.Parse(timeFormat, strings.Trim(string(data), "\""))
if err != nil {
return err
}
*ct = CustomTime(newTime)
return nil
}
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%q", time.Time(*ct).Format(timeFormat))), nil
}
Careful, you might also need to implement the Valuer and the Scanner interfaces for the time to be parsed in and out of the database, something like the following:
func (ct CustomTime) Value() (driver.Value, error) {
return time.Time(ct), nil
}
func (ct *CustomTime) Scan(src interface{}) error {
if val, ok := src.(time.Time); ok {
*ct = CustomTime(val)
} else {
return errors.New("time Scanner passed a non-time object")
}
return nil
}
Go Playground example.

How to access struct with a variable?

I'm new to Go and I'm facing issues in accessing a struct with a variable
I have this function decodeUser. My task is to check whether the keys are present in the request. So this is what I did. I've added a comment where I got the error.
func decodeUser(r *http.Request) (root.User, []string, error) {
var u root.User
if r.Body == nil {
return u, []string{}, errors.New("no request body")
}
decoder := json.NewDecoder(r.Body)
checks := []string{
"UserName",
"FirstName",
"LastName",
"Email",
}
emptyFields := []string{}
for _, check := range checks {
// i'm having problem over here `u[check]` it's showing (invalid
operation: u[check] (type root.User does not support
indexing))
if u[check] == nil {
emptyFields = append(emptyFields, check)
}
}
err := decoder.Decode(&u)
return u, emptyFields, err
}
Just in case I added root.User here's structure for it
type User struct {
ID string
Username string
Password string
FirstName string
LastName string
Email string
PhoneNumber string
PhoneNumberExtension string
DOB time.Time
AboutMe string
}
The problem occurs as it doesn't allow me to access struct by a variable and I can't use this method which is u.check. So basically how should I make u[check] work?
I would suggest you manually check for zero values since it seems that you already know the fields that needs to be non-zero at compile time. However, if that is not the case, here is a simple function (using reflection) that will check for zero values in a struct.
func zeroFields(v interface{}, fields ...string) []string {
val := reflect.Indirect(reflect.ValueOf(v))
if val.Kind() != reflect.Struct {
return nil
}
var zeroes []string
for _, name := range fields {
field := val.FieldByName(name)
if !field.IsValid() {
continue
}
zero := reflect.Zero(field.Type())
// check for zero value
if reflect.DeepEqual(zero.Interface(), field.Interface()) {
zeroes = append(zeroes, name)
}
}
return zeroes
}
func main() {
x := User{
Email: "not#nil",
}
fmt.Println(zeroFields(&x, "ID", "Username", "Email", "Something", "DOB"))
}
Which would output:
[ID Username DOB]
Playground
This is what worked for me
for _, check := range checks {
temp := reflect.Indirect(reflect.ValueOf(&u))
fieldValue := temp.FieldByName(string(check))
if (fieldValue.Type().String() == "string" && fieldValue.Len() == 0) || (fieldValue.Type().String() != "string" && fieldValue.IsNil()) {
fmt.Println("EMPTY->", check)
emptyFields = append(emptyFields, check)
}
}

Resources