Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative #1470

Closed
antonprokopovich opened this issue Jan 15, 2023 · 7 comments
Closed

Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative #1470

antonprokopovich opened this issue Jan 15, 2023 · 7 comments

Comments

@antonprokopovich
Copy link

antonprokopovich commented Jan 15, 2023

I'm trying to migrate from pgx/v4 to pgx/v5 and in particular I need to rewrite code that uses CIDR type from jackc/pgtype to it's v5 alternative. I see v5/pgtype has the InetCodec type but I can't figure out how to use it instead of CIDR from v4.

Here the code I need to rewrite:

package main

import (
	"net"

	pgtypeV4 "github.com/jackc/pgtype" // TODO: Migrate to "github.com/jackc/pgx/v5/pgtype"
)

func toPgCIDRs(a []*net.IPNet) pgtypeV4.CIDRArray {
	elements := make([]pgtypeV4.CIDR, len(a))
	for i, e := range a {
		elements[i] = pgtypeV4.CIDR(pgtypeV4.Inet{IPNet: e, Status: pgtypeV4.Present})
	}

	return pgtypeV4.CIDRArray{
		Status:     pgtypeV4.Present,
		Elements:   elements,
		Dimensions: []pgtypeV4.ArrayDimension{{Length: int32(len(a)), LowerBound: 0}},
	}
}

Any kind of help would be greatly appreciated!

@antonprokopovich antonprokopovich changed the title Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Jan 15, 2023
@antonprokopovich antonprokopovich changed the title Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Jan 15, 2023
@antonprokopovich antonprokopovich changed the title Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Jan 15, 2023
@antonprokopovich antonprokopovich changed the title Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Migrate jackc/pgtype CIDR to its pgx/v5/pgtype alternative Jan 15, 2023
@jackc
Copy link
Owner

jackc commented Jan 17, 2023

You should be able to use the core Go types directly now. There isn't a pgx CIDR type because it shouldn't be necessary anymore. You probably want https://pkg.go.dev/net/netip#Prefix.

@antonprokopovich
Copy link
Author

antonprokopovich commented Jan 17, 2023

@jackc Thank you for your reply! I’ll try it out.

May I ask just one more questing regarding pgtype V5 vs V4?

I have the code that queries a jsonb field from BD into a *string field of a struct. It worked fine with V4 but after migrating to V5 I get scan row into struct fields: can't scan into dest[7]: json: cannot unmarshal object into Go value of type string.

I understand that it tries to umarshal the json and if I use map[string]interface instead of *string then error goes away. But I would like to keep using *string type as before and can’t figure out how to fix it.

@jackc
Copy link
Owner

jackc commented Jan 17, 2023

Can you make an example program that demonstrates this?

@antonprokopovich
Copy link
Author

antonprokopovich commented Jan 17, 2023

@jackc Sorry, I'm short on time at the moment to make a reproducible example. But here's the part of the code where the error is coming from (operator_data field is the one of that causes it):

package main

import (
   "context"
   "fmt"
   "time"

   "[github.com/google/uuid](http://github.com/google/uuid)"
   "[github.com/jackc/pgx/v5](http://github.com/jackc/pgx/v5)"
   "[github.com/jackc/pgx/v5/pgtype](http://github.com/jackc/pgx/v5/pgtype)"
   "[github.com/georgysavva/scany/v2/pgxscan](http://github.com/georgysavva/scany/v2/pgxscan)"
)

type ResourceStatus string

type ResourceState struct {
   Resource     uuid.UUID
   StatusTS     time.Time
   Status       ResourceStatus
   ResourceCI   *string
   OperatorData *string    `db:"operator_data"`
   ErrorText    *string    `db:"error_text"`
   OperatorTS   *time.Time `db:"operator_ts"`
   OperatorUUID *uuid.UUID `db:"operator_uuid"`
   ErrorCount   *int32     `db:"error_count"`
}

func toPgUUIds(a []uuid.UUID) (res pgtype.Array[pgtype.UUID]) {
   elements := make([]pgtype.UUID, len(a))
   for i, e := range a {
      elements[i] = pgtype.UUID{Bytes: e, Valid: true}
   }

   res.Dims = []pgtype.ArrayDimension{{Length: int32(len(a)), LowerBound: 0}}
   res.Elements = elements
   res.Valid = true

   return res
}

func GetResourcesStatus(ctx context.Context, tx pgx.Tx, all bool, resourceUUID ...uuid.UUID) (map[uuid.UUID]*ResourceState, error) {
   rsm := make(map[uuid.UUID]*ResourceState)

   var res []*ResourceState

   if err := pgxscan.Select(ctx, tx, &res, `
   SELECT     
      c.resource_uid,
      c.number,
      c.cluster_uid,
      c.parent_uid,
      c.type,
      r.operator_ts ,
      r.operator_uuid ,
      r.operator_data ,
      COALESCE(r.status_ts,c.ts) status_ts,
      COALESCE(r.status,'Pending') status,
      r.error_text,
      r.error_count,
      i.ci resource_ci
   FROM state.changed c
   LEFT JOIN  state.resource r  ON c.resource_uid = r.uid
   LEFT JOIN  state.resource_ci i  ON c.resource_uid = i.resource_id 
   WHERE c.resource_uid =  ANY($1) OR $2`, toPgUUIds(resourceUUID), all); err != nil {
      return nil, fmt.Errorf("can't get resource status: %w", err)
   }

   for _, v := range res {
      rsm[v.Resource] = v
   }

   return rsm, nil
}

can't get resource status: scanning all: scanning: scanning: doing scan: scanFn: scany: scan row into struct fields: can't scan into dest[7]: json: cannot unmarshal object into Go value of type string

If you could tell what might be wrong by looking at this snippet?

Btw, thanks for all the excellent work with the library!

@antonprokopovich
Copy link
Author

antonprokopovich commented Jan 17, 2023

@jackc I went down the source code and I see that **string typed value is passed as target to JSONCodec.PlanScan instead of *string. So it switches to default scanPlanJSONToJSONUnmarshal{} case instead of the desirable scanPlanAnyToString{} which causes the error.

Though I don't understand why double pointer is passed instead of a pointer.

@antonprokopovich
Copy link
Author

Fix it on the SQL-query level by casting json to varchar like so: CAST(r.operator_data AS VARCHAR). It looks more like a workaround since it's supposed to scan PG jsonb into Go *string without any casting. It's probably something else in my code that breaks that functionality so I'll look into it later.

jackc added a commit that referenced this issue Jan 21, 2023
@jackc
Copy link
Owner

jackc commented Jan 21, 2023

@antonprokopovich Glad you got it working. I think the **string case should have worked. Fixed in e48e7a7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants