0

I am trying to save incoming request's remote IP address into Postgres. I am using chi and sqlc.

Here's the migration:

CREATE TABLE IF NOT EXISTS ips (
    id bigserial PRIMARY KEY,
    ip INET
);

And this is the insert query:

-- name: CreateIp :one
INSERT INTO ips (
    ip
) VALUES (
    $1
) RETURNING *;

This is the struct for params:

type CreateIpParams struct {
    Ip        net.IP         `json:"ip"`
}

Get the IP and parse it:

...

    // Get IP from RemoteAddr
    ip, _, _ := net.SplitHostPort(r.RemoteAddr)
    log.Println(" >> ip:", reflect.ValueOf(ip)) // ::1
    log.Println(" >> ip.TypeOf:", reflect.TypeOf(ip)) // string

    if netIp := net.ParseIP(ip); netIp != nil {
        params.Ip = netIp
        log.Println(" >> netIp:", reflect.ValueOf(netIp)) // ::1
        log.Println(" >> netIp.TypeOf:", reflect.TypeOf(netIp)) // net.IP
    }

...

When I made a POST request, it returns:

    "error": "pq: invalid byte sequence for encoding \"UTF8\": 0x00"

If I removed the that code block without saving ip at all:

    "error": "pq: invalid input syntax for type inet: \"\""

I tried trimming the ip with strings.TrimSpace(ip) but doesn't help.

Code with sqlc save.

...
    params := &db.CreateIpParams{}

    // Get IP from RemoteAddr
    ip, _, _ := net.SplitHostPort(r.RemoteAddr)
    if netIp := net.ParseIP(ip); netIp != nil {
        params.Ip = netIp
    }

    if _, err := db.NewPg(pg).CreateIp(context.Background(), *params); err != nil {
        render.Render(w, r, ErrRender(err))
        return
    }

CreateIp code and Ip struct:

// db/user.sql.go

func (q *Queries) CreateIp(ctx context.Context, arg CreateIpParams) (Ip, error) {
    row := q.queryRow(ctx, q.createIpStmt, createIp,
        arg.Ip,
    )
    var i Ip
    err := row.Scan(
        &i.Ip,
    )
    return i, err
}

// db/models.go

type Ip struct {
    ID         int64          `json:"id"`
    Ip         net.IP         `json:"ip"`
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Swix
  • 1,883
  • 7
  • 33
  • 50
  • Sorry about that, I have updated with the code that save with `sqlc`. – Swix Oct 06 '20 at 13:16
  • Can you show how `CreateIp` looks like, regardless of whether it's generated or handwritten? – mkopriva Oct 06 '20 at 13:19
  • Sure thing, I've added. – Swix Oct 06 '20 at 13:26
  • Looks ok, one more thing: Is the parameter parsing and saving happening in one and the same function? or is it done by separate functions? If it's done by separate function are you certain you're passing a pointer to that function? Try adding a log/print statement to the generated `CreateIp` to see if the `arg` has the `Ip` field set or not, if not then that would mean that you're passing around copies of params somewhere where you should be passing pointers instead. – mkopriva Oct 06 '20 at 13:28
  • You need to focus your question a bit more. You are asking how to save the IP, but in code, you are giving QueryRow() which is not for saving but retrieving data from QW. – Mayank Patel Oct 06 '20 at 13:32
  • @MayankPatel that's generated code, and it's correct for INSERTs that have the RETURNING clause. – mkopriva Oct 06 '20 at 13:33
  • @MayankPatel I added "generated" to explain it, but Flimzy edited that out for some reason. – Swix Oct 06 '20 at 13:44
  • @mkopriva Yes, parameters parsing and saving are in one and the same function in one file under one package `auth`. – Swix Oct 06 '20 at 13:59
  • @Rario and have you tried adding the print statement to the generated `CreateIp` method? – mkopriva Oct 06 '20 at 14:01
  • @mkopriva Yes, I just did. I added `log.Println(" >> IP here:", arg.Ip)` right above `query.Row()`, it prints `2020/10/06 20:39:42 >> IP here: ::1` – Swix Oct 06 '20 at 14:10
  • @Rario then this is gonna be an issue with how the driver handles `net.IP`, or maybe `sqlc` should not use `net.IP` for `inet` types. You probably want to use [this](https://github.com/kyleconroy/sqlc#type-overrides) and map `inet` to plain `string` and then change your parsing code accordingly. While `::1` is a valid value for `inet` that's not what is actually being sent to the database, it is the raw bytes that are being sent (try printing `fmt.Printf("%q\n", []byte(arg.Ip))` and you'll see what I mean. And btw the reason you're seeing `::1` in your prints is because `net.IP` implements ... – mkopriva Oct 06 '20 at 14:21
  • ... the `Stringer` interface, however that interface is not used when sending data to the db. – mkopriva Oct 06 '20 at 14:21
  • 1
    @mkopriva Yep, I see what you mean now. Thanks for the help, brother/sister. :) – Swix Oct 06 '20 at 15:14
  • so where is the answer to his question? was really hoping to see an accepted answer @mkopriva mind posting an answer? – uberrebu Jul 17 '22 at 06:39
  • @uberrebu I didn't provide an answer because I don't use `sqlc` nor `chi` and therefore I do not know what the correct solution to the problem is. However I'm familiar with Go and PostgreSQL so I thought I may at least be able to provide some suggestions, which I did, in the comments, as you can see. If you would like to know how Rario ended up solving the issue you should probably ask them to provide the answer. If you have a similar Go+PostgreSQL+IP question but unrelated to sqlc then prehaps you should open a new question, I'd definitely take a look at it and help if I can. – mkopriva Jul 17 '22 at 06:49

0 Answers0