How to avoid that 0 (zero) int turns into Postgres "null" value and violates "not null" constraint? - postgresql

In Go, I am unmarshalling/decoding JSON into a struct with an ID field of type int. Then I try to insert this struct into a PostgreSQL database using go-pg with the ID column as the primary key (which has a not-null constraint). The first entry has a 0 as its ID. In the Postgres documentation, it states that 0 is ok as a value of a primary key. However, I keep getting an error message:
"ERROR #23502 null value in column "number" violates not-null constraint".
It looks like the 0 turns into a Go "zero value" when it is unmarshalled into the int value. Then it is inserted as null value into Postgres. Any tips on how I might be able to avoid this would be greatly appreciated.
type Account struct {
Number int `sql:"type:smallint, pk"`
Name string
}
[...]
account := Account{}
err := json.NewDecoder(r.Body).Decode(&account)
[...]
insertErr := pgLayer.db.Insert(&account)
if insertErr != nil {
log.Printf("Error while inserting new item")
return "n/a", insertErr
}

While it's not immediately obvious with go-pg you can use the struct tag sql:",notnull" to show that Go empty values ("", 0, [] etc.) are allowed and should not be treated as SQL NULL.
You can see it in the Features list.
In your case I would change this to:
type Account struct {
Number int `sql:"type:smallint,pk,notnull"`
Name string
}

I think the easiest solution to your problem is to make your ID column of type SERIAL and let Postgres deal with setting and auto-incrementing the value for you. If you need the value within your application directly after inserting it, you can always use a RETURNING psql clause, like such:
INSERT INTO shows(
user_id, name, description, created, modified
) VALUES(
:user_id, :name, :created, :modified
) RETURNING id;
And capture the response within your code.

Related

Querying string array with gorm

I'm trying to select items of a table with a column of type text[].
The result of the field brandIds always return null
my struct:
type User struct {
Email string `json:"email"`
Name string `json:"name"`
BrandIds pq.StringArray `gorm:"type:text[]" json:"brandIds"`
}
the code:
var users []dtos.User
db.Table("user").Find(&users)
data, _ := json.Marshal(users)
Name and emails returns fine... But the brandIds always return null.
Type of brandIds column is text[]
Postgres have some troubles with camelCase column names. When I named BrandIds gorm was looking for column bran_id
I needed to specify the column name so I have added the gorm:"column:brandId" to tell gorm whats the name of the column.

Postgresql Has-Many relationship insert same id error

I have 2 structs in has-many relationship like this:
type Domain struct {
ID uint `gorm:"primaryKey;type:serial"`
Emails []Email `gorm:"foreignKey:ID" "column:emails"`
}
type Email struct {
ID uint `gorm:"primaryKey;type:serial"`
Address string `gorm:"column:address"`
}
I follow document of gorm here: https://gorm.io/docs/associations.html#Association-Mode
As far as I understand, when we insert a Domain record, gorm had insert array email ([]Email) into database first, I met a problem like this when try to insert into database:
var domain models.Domain
var emails []models.Email
for key, _ := range Output {
emails = append(emails, models.Email{Address: key})
}
domain = models.Domain{
Emails: emails,
}
dataAccess.Domain.Insert(domain) // db.Create(&domain)
And this is output in my console:
2020/09/10 10:37:46 /Users/tho/go/src/txxxx/exxxx/dataAccess/domains.go:18 ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time (SQLSTATE 21000)
[362.162ms] [rows:0] INSERT INTO "emails" ("address","id") VALUES ('info#example.com',2),('d.apple#example.com',2) ON CONFLICT ("id") DO UPDATE SET "id"="excluded"."id" RETURNING "id"
2020/09/10 10:37:46 /Users/tho/go/src/txxxx/exxxx/dataAccess/domains.go:18 ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time (SQLSTATE 21000)
[1078.499ms] [rows:1] INSERT INTO "domains" DEFAULT VALUES RETURNING "id"
ERRO[0050] Err: ERROR: ON CONFLICT DO UPDATE command cannot affect row a second time (SQLSTATE 21000)
Gorm insert 2 emails with the same id so the postresql error show like above.
How can i avoid same id because it is primaryKey and already serial (autoIncrement).
You need change
Emails []Email `gorm:"foreignKey:ID" "column:emails"`
to
Emails []Email `gorm:"foreignKey:DomainId" "column:domain_id"`
And add
DomainId uint `gorm:"column:domain_id"`
in type Email struct
Try:
type Domain struct {
ID uint `gorm:"primaryKey;type:serial"`
Domain string `gorm:"column:domain"`
Emails []Email `gorm:"foreignKey:DomainId" "column:domain_id"`
}
type Email struct {
ID uint `gorm:"primaryKey;type:serial"`
Address string `gorm:"column:address"`
DomainId uint `gorm:"column:domain_id"`
}

Cannot read "one to many" Relation with go-pg

I am trying to implement a little state machine with go and store my states in a postgres db.
i created my database like this:
CREATE TABLE state_machines
(
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
initial_state TEXT NOT NULL,
"name" TEXT NOT NULL
);
CREATE TABLE state_machine_states
(
state_machine_id uuid NOT NULL REFERENCES state_machines(id) ON DELETE CASCADE,
"name" TEXT NOT NULL,
PRIMARY KEY(state_machine_id, "name")
);
// StateMachine is the DB schema
type StateMachine struct {
ID *uuid.UUID `pg:"id,pk,type:uuid,default:uuid_generate_v4()"`
Name string `pg:"name"`
InitialState string `pg:"initial_state"`
States []*StateMachineState `pg:"fk:state_machine_id"`
}
// StateMachineState is the DB schema
type StateMachineState struct {
StateMachineID uuid.UUID `pg:"state_machine_id,fk"`
Name string `pg:"name"`
}
I am using go-pg 9.2 and i am trying to load a state machine and a list of its states from the "States" relation.
My function to load the state machines looks like this:
func (cep *repository) GetStateMachines() ([]*StateMachine, error) {
stateMachines := []*StateMachine{}
err := cep.db.Model(&stateMachines).
Relation("States").
Relation("Transitions").
Select()
return stateMachines, err
}
If I execute it, I always get the error message Error reading state machine: model=StateMachine does not have relation="States"
I have done similar relations before and they worked and now, I cannot get it to work again :(
Try upgrading to v10 which fully supports relations: https://pg.uptrace.dev/orm/has-many-relation/
See if there is .Debug() function in go-pg for a query.
I use https://gorm.io/ , and there is a Debug funcion, that returns all of the SQL queries.
When i see what queries are send, i log in postgresql and try them manually to see a more detailed error.

How do I make a column DEFAULT(NULL) in SQLite.swift

I'm trying to define my database structure, but I can't figure out how to set the default value of a column to NULL. This is what I want it to produce:
CREATE TABLE test (
content text DEFAULT(NULL),
);
I have the following code
let content = Expression<String?>("content")
try self.db.run(test.create { t in
t.column(content, defaultValue: nil)
})
But defaultValue: nil obviously does not work. What do I set instead?
Unless you create the column in the table with the NOT NULL qualifier, the column's default value is NULL. You don't need to do anything special to get the behavior you want.
CREATE TABLE test {
content TEXT
};
Create the table that way. When you insert rows into that table, if you don't provide a specific value for the content column, the row will be added with no value (NULL) in that column.

Pass nothing (not null) to the server if the argument is None

I have a model:
case class MyModel(
id: Pk[Long] = NotAssigned,
startsAt: Option[DateTime] = None,
addedAt: Option[DateTime] = None
)
object MyModel {
// .....................
SQL("""
INSERT INTO my_table(starts_at)
VALUES ({startsAt})
"""
).on('startsAt -> newItem.startsAt).executeInsert()
}
Both startst_at and added_at have a default value of now() in Postgresql and don't allow null values in them. It doesn't cause any error for addedAt (because I never pass it to the server from the client) but it does cause the error for startsAt if it's not specified at newItem.startsAt and, thus, is equal to None and, thus, it's being passed as null.
org.postgresql.util.PSQLException: ERROR: null value in column "starts_at" violates not-null constraint
What I want is be able to specify startsAt whenever I want it and pass it to the server, meaning if I specify it then that value should be passed to the server, if not - nothing should be passed and the server should use its default value now(). I don't want to specify the default value at the client because it's already set at the server at the db level.
How about this SQL fix:
insert into my_table(starts_at)
values (COALESCE({startsAt}, now())
Updated: requirement is to use the default value of the column
The only way that I know of to get the server to use the default value of a column in an insert, is not to mention that column in the columns list. For example (not tested):
startsAt.map { date =>
SQL("""insert into my_table(starts_at) values({startsAt})""")
.on('startsAt -> date)
.execute()
}.orElse {
SQL("""insert into my_table() values()""")
.execute()
}