2023-02-20 10:42:21 +00:00
|
|
|
package pkg
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgtype"
|
|
|
|
"github.com/jackc/pgx/v4"
|
|
|
|
)
|
|
|
|
|
|
|
|
type NewInvoiceProductArray []*invoiceProductForm
|
|
|
|
|
|
|
|
func (src NewInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
|
|
|
typeName := "new_invoice_product[]"
|
|
|
|
dt, ok := ci.DataTypeForName(typeName)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
|
|
|
}
|
|
|
|
var values [][]interface{}
|
|
|
|
for _, form := range src {
|
2023-04-24 18:40:10 +00:00
|
|
|
var productId interface{} = form.ProductId.Val
|
|
|
|
if form.ProductId.Val == "" {
|
|
|
|
productId = nil
|
|
|
|
}
|
2023-02-20 10:42:21 +00:00
|
|
|
values = append(values, []interface{}{
|
2023-04-24 18:40:10 +00:00
|
|
|
productId,
|
2023-02-20 10:42:21 +00:00
|
|
|
form.Name.Val,
|
|
|
|
form.Description.Val,
|
|
|
|
form.Price.Val,
|
|
|
|
form.Quantity.Val,
|
|
|
|
form.Discount.Float64() / 100.0,
|
|
|
|
form.Tax.Selected,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
array := pgtype.NewValue(dt.Value).(pgtype.ValueTranscoder)
|
|
|
|
if err := array.Set(values); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return array.EncodeBinary(ci, buf)
|
|
|
|
}
|
|
|
|
|
2023-03-13 14:00:35 +00:00
|
|
|
type EditedInvoiceProductArray []*invoiceProductForm
|
|
|
|
|
|
|
|
func (src EditedInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
|
|
|
typeName := "edited_invoice_product[]"
|
|
|
|
dt, ok := ci.DataTypeForName(typeName)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
|
|
|
}
|
|
|
|
var values [][]interface{}
|
|
|
|
for _, form := range src {
|
|
|
|
values = append(values, []interface{}{
|
2023-05-22 09:06:06 +00:00
|
|
|
form.InvoiceProductId.IntegerOrNil(),
|
|
|
|
form.ProductId.IntegerOrNil(),
|
2023-03-13 14:00:35 +00:00
|
|
|
form.Name.Val,
|
|
|
|
form.Description.Val,
|
|
|
|
form.Price.Val,
|
|
|
|
form.Quantity.Val,
|
|
|
|
form.Discount.Float64() / 100.0,
|
|
|
|
form.Tax.Selected,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
array := pgtype.NewValue(dt.Value).(pgtype.ValueTranscoder)
|
|
|
|
if err := array.Set(values); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return array.EncodeBinary(ci, buf)
|
|
|
|
}
|
|
|
|
|
2023-02-20 10:42:21 +00:00
|
|
|
func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
|
2023-02-24 12:17:37 +00:00
|
|
|
if _, err := conn.Exec(ctx, "set role to admin"); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
Replace tag relations with array attributes
It all started when i wanted to try to filter invoices by multiple tags
using an “AND”, instead of “OR” as it was doing until now. But
something felt off and seemed to me that i was doing thing much more
complex than needed, all to be able to list the tags as a suggestion
in the input field—which i am not doing yet.
I found this article series[0] exploring different approaches for
tagging, which includes the one i was using, and comparing their
performance. I have not actually tested it, but it seems that i have
chosen the worst option, in both query time and storage.
I attempted to try using an array attribute to each table, which is more
or less the same they did in the articles but without using a separate
relation for tags, and i found out that all the queries were way easier
to write, and needed two joins less, so it was a no-brainer.
[0]: http://www.databasesoup.com/2015/01/tag-all-things.html
2023-04-07 19:31:35 +00:00
|
|
|
tagNameOID, err := registerPgType(ctx, conn, &pgtype.Text{}, "tag_name")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tagNameArray := pgtype.NewArrayType("tag_name[]", tagNameOID, func() pgtype.ValueTranscoder {
|
|
|
|
return &pgtype.Text{}
|
|
|
|
})
|
|
|
|
if _, err := registerPgType(ctx, conn, tagNameArray, tagNameArray.TypeName()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-20 10:42:21 +00:00
|
|
|
discountRateOID, err := registerPgType(ctx, conn, &pgtype.Numeric{}, "discount_rate")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newInvoiceProduct, err := pgtype.NewCompositeType(
|
|
|
|
"new_invoice_product",
|
|
|
|
[]pgtype.CompositeTypeField{
|
|
|
|
{"product_id", pgtype.Int4OID},
|
|
|
|
{"name", pgtype.TextOID},
|
|
|
|
{"description", pgtype.TextOID},
|
|
|
|
{"price", pgtype.TextOID},
|
|
|
|
{"quantity", pgtype.Int4OID},
|
|
|
|
{"discount_rate", discountRateOID},
|
|
|
|
{"tax", pgtype.Int4ArrayOID},
|
|
|
|
},
|
|
|
|
conn.ConnInfo(),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newInvoiceProductOID, err := registerPgType(ctx, conn, newInvoiceProduct, newInvoiceProduct.TypeName())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newInvoiceProductArray := pgtype.NewArrayType("new_invoice_product[]", newInvoiceProductOID, func() pgtype.ValueTranscoder {
|
|
|
|
value := newInvoiceProduct.NewTypeValue()
|
|
|
|
return value.(pgtype.ValueTranscoder)
|
|
|
|
})
|
|
|
|
_, err = registerPgType(ctx, conn, newInvoiceProductArray, newInvoiceProductArray.TypeName())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-02-24 12:17:37 +00:00
|
|
|
|
2023-03-13 14:00:35 +00:00
|
|
|
editedInvoiceProduct, err := pgtype.NewCompositeType(
|
|
|
|
"edited_invoice_product",
|
|
|
|
[]pgtype.CompositeTypeField{
|
|
|
|
{"invoice_product_id", pgtype.Int4OID},
|
|
|
|
{"product_id", pgtype.Int4OID},
|
|
|
|
{"name", pgtype.TextOID},
|
|
|
|
{"description", pgtype.TextOID},
|
|
|
|
{"price", pgtype.TextOID},
|
|
|
|
{"quantity", pgtype.Int4OID},
|
|
|
|
{"discount_rate", discountRateOID},
|
|
|
|
{"tax", pgtype.Int4ArrayOID},
|
|
|
|
},
|
|
|
|
conn.ConnInfo(),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
editedInvoiceProductOID, err := registerPgType(ctx, conn, editedInvoiceProduct, editedInvoiceProduct.TypeName())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
editedInvoiceProductArray := pgtype.NewArrayType("edited_invoice_product[]", editedInvoiceProductOID, func() pgtype.ValueTranscoder {
|
|
|
|
value := editedInvoiceProduct.NewTypeValue()
|
|
|
|
return value.(pgtype.ValueTranscoder)
|
|
|
|
})
|
|
|
|
_, err = registerPgType(ctx, conn, editedInvoiceProductArray, editedInvoiceProductArray.TypeName())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-02-24 12:17:37 +00:00
|
|
|
_, err = conn.Exec(ctx, "reset role")
|
|
|
|
return err
|
2023-02-20 10:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func registerPgType(ctx context.Context, conn *pgx.Conn, value pgtype.Value, name string) (oid uint32, err error) {
|
|
|
|
if err = conn.QueryRow(ctx, "select $1::regtype::oid", name).Scan(&oid); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
conn.ConnInfo().RegisterDataType(pgtype.DataType{Value: value, Name: name, OID: oid})
|
|
|
|
return
|
|
|
|
}
|