1

Trying to fix this problem i'm having with my api im building.

db:

DROP TABLE IF EXISTS contacts CASCADE;
CREATE TABLE IF NOT EXISTS contacts (
        uuid UUID UNIQUE PRIMARY KEY,
        first_name varchar(150),
);



DROP TABLE IF EXISTS workorders CASCADE;
CREATE TABLE IF NOT EXISTS workorders (
        uuid UUID UNIQUE PRIMARY KEY,
        work_date timestamp WITH time zone,
        requested_by UUID REFERENCES contacts (uuid) ON UPDATE CASCADE ON DELETE CASCADE,

);

struct:

https://gopkg.in/guregu/null.v3

type WorkorderNew struct {
    UUID               string      `json:"uuid"`
    WorkDate           null.Time   `json:"work_date"`
    RequestedBy        null.String `json:"requested_by"`
}

api code:

workorder := &models.WorkorderNew{}
if err := json.NewDecoder(r.Body).Decode(workorder); err != nil {
    log.Println("decoding fail", err)
}
// fmt.Println(NewUUID())
u2, err := uuid.NewV4()
if err != nil {
    log.Fatalf("failed to generate UUID: %v", err)
}

q := `
            INSERT
            INTO workorders
                (uuid,
                work_date,
                requested_by
                )
            VALUES
                ($1,$2,$3)
            RETURNING uuid;`

statement, err := global.DB.Prepare(q)
global.CheckDbErr(err)

fmt.Println("requested by", workorder.RequestedBy)

lastInsertID := ""
err = statement.QueryRow(
    u2,
    workorder.WorkDate,
    workorder.RequestedBy,
).Scan(&lastInsertID)
global.CheckDbErr(err)

json.NewEncoder(w).Encode(lastInsertID)

When I send an API request with null as the value it works as expected but when i try to send a "" as the value for the null.String or the null.Time it fails

works:

{  
   "work_date":"2016-12-16T19:00:00Z",
   "requested_by":null
}

not working:

{  
   "work_date":"2016-12-16T19:00:00Z",
   "requested_by":""
}

Basically when i call the QueryRow and save to database the workorder.RequestedBy value should be a null and not the "" value im getting thanks

3
  • What does "it fails" or "not working" mean? Are you getting an error? If so, please include that error in your question. Are you getting a result different from what you expect? If so, please include the expected and the actual result in the question. Commented Aug 27, 2019 at 7:06
  • @mkopriva i'm expecting when i do the queryrow it to save RequestedBy as a null in the database and not to save it as "" ( which fails with my schema) Commented Aug 27, 2019 at 7:08
  • 1
    Then do not use null.String but implement your own custom type. Since in json an empty string "" is not the same as null, the behaviour of null.String is correct. So if your requirements are different from what null.String supports then simply do not use it. Commented Aug 27, 2019 at 7:11

1 Answer 1

3

If you want to treat empty strings as nulls you have at least two options.

"Extend" null.String:

type MyNullString struct {
    null.String
}

func (ns *MyNullString) UnmarshalJSON(data []byte) error {
    if string(data) == `""` {
        ns.Valid = false
        return nil
    }
    ns.String.UnmarshalJSON(data)
}

Or use NULLIF in the query:

INSERT INTO workorders (
    uuid
    , work_date
    , requested_by
) VALUES (
    $1
    , $2
    , NULLIF($3, '')
)
RETURNING uuid

Update:

To extend the null.Time you have to understand that the type of null.Time.Time is a struct. The builtin len function works on slices, arrays, pointers to arrays, maps, channels, and strings. Not structs. So in this case you can check the data argument, which is a byte slice, by converting it to a string and comparing it to a string that contains an empty string, i.e. it has two double quotes and nothing else.

type MyNullTime struct {
    null.Time
}

func (ns *MyNullTime) UnmarshalJSON(data []byte) error {
    if string(data) == `""` {
        ns.Valid = false
        return nil
    }
    return ns.Time.UnmarshalJSON(data)
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thankyou for first solution was perfect. I've tried to implement it for null.time but im a bit lost on that too could you possible tell me how to do that one too? func (ns *MyNullTime) UnmarshalJSON(data []byte) error { if err := ns.Time.UnmarshalJSON(data); err != nil { return err } ns.Valid = len(ns.Time.Time) > 0 // if empty, set as valid=false return nil } dosent seem to work
@Trentfrompunchbowl1 I've added an update to the answer, try it and let me know if it works because I haven't tested it or anything.
@mkoprive works like a charm and thanks again for your help.... my brain has melted all day from trying to figure out what i was doing wrong

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.