Diesel migration force String instead of Uuid in schema.rs - postgresql

I have a problem with the diesel migration. I need to implement Uuid as primary key for a model. I got a lot of issues with the Uuid integration (feature uuidv07, uuid crate,..) but when I specify the type uuid in the migration, diesel generate a "Varchar" field in the migration, so I can't use Uuid as a type of field in my model.
users.sql
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR NOT NULL,
name VARCHAR NOT NULL,
password VARCHAR NOT NULL,
id_role INT,
CONSTRAINT fk_role
FOREIGN KEY(id_role)
REFERENCES roles(id)
)
schema.rs
table! {
users (id) {
id -> Varchar,
email -> Varchar,
name -> Varchar,
password -> Varchar,
id_role -> Nullable<Int4>,
}
}
Is this normal to use Varchar and not Uuid ?
uuid = { version = "0.8.2", features = ["serde", "v4"] }
diesel = { version = "1.4.5", features = ["postgres", "r2d2", "uuidv07"] }
Thanks.

Diesel does not force anything onto you. It just reads the types from your database system. So if it outputs a Varchar as type for a specific column that means your database system recorded Varchar (for whatever reason) as type for this column. Or to word it differently: The problem here is not diesel, but likely an interaction between your migration, your existing database schema and your database. As neither information about your pre-existing database schema nor about your database system is provided as part of your question there is not much I can add here to point you into the right direction.

Related

F# SqlProvider: System.Exception: Error - you cannot update an entity that does not have a primary key. (public.users)

I'm using the SQLProvider type-provider project: https://fsprojects.github.io/SQLProvider/
As the documentation states on CRUD usage, I'm trying to update a row like this:
let UpdateGpsLocation(gpsLocationDetails: UpdateGpsLocation) =
let ctx = SQL.GetDataContext()
let maybeFoundUser =
query {
for user in ctx.Public.Users do
where(user.UserId = gpsLocationDetails.UserId)
select(Some user)
exactlyOneOrDefault
}
match maybeFoundUser with
| Some user ->
user.GpsLatitude <- Some gpsLocationDetails.Latitude
user.GpsLongitude <- Some gpsLocationDetails.Longitude
ctx.SubmitUpdates()
| None -> failwithf "User %i not found" gpsLocationDetails.UserId
But it is failing with this:
System.Exception: Error - you cannot update an entity that does not have a primary key. (public.users)
at FSharp.Data.Sql.Providers.PostgresqlProvider.createUpdateCommand(IDbConnection con, StringBuilder sb, SqlEntity entity, FSharpList`1 changedColumns)
at <StartupCode$FSharp-Data-SqlProvider>.$Providers.Postgresql.FSharp-Data-Sql-Common-ISqlProvider-ProcessUpdates#1205-8.Invoke(SqlEntity e)
at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source) in D:\a\_work\1\s\src\fsharp\FSharp.Core\seq.fs:line 497
at FSharp.Data.Sql.Providers.PostgresqlProvider.FSharp-Data-Sql-Common-ISqlProvider-ProcessUpdates(IDbConnection con, ConcurrentDictionary`2 entities, TransactionOptions transactionOptions, FSharpOption`1 timeout)
at <StartupCode$FSharp-Data-SqlProvider>.$SqlRuntime.DataContext.FSharp-Data-Sql-Common-ISqlDataContext-SubmitPendingChanges#110.Invoke(Unit unitVar0)
at FSharp.Data.Sql.Runtime.SqlDataContext.FSharp-Data-Sql-Common-ISqlDataContext-SubmitPendingChanges()
at BackendDataLayer.Access.UpdateGpsLocation(UpdateGpsLocation gpsLocationDetails) in C:\Users\knocte\Documents\Code\RunIntoMeMASTER\src\BackendDataLayer\Access.fs:line 34
But the table has a primary key! In particular, it's using the SERIAL feature of PostgreSQL, this way:
CREATE TABLE users (
user_id SERIAL PRIMARY KEY
, gps_latitude FLOAT8
, gps_longitude FLOAT8
);
Is this a bug in SqlProvider project? Or maybe just in its PostgreSQL component?
I was able to reproduce this problem, there seems to be an issue with using user_id as the column name here.
This is just a workaround but changing the column name to "UserId" (double-quotes included!) fixed the issue for me.
See the new schema:
CREATE TABLE "Users" (
"UserId" SERIAL PRIMARY KEY
, "GpsLatitude" FLOAT8
, "GpsLongitude" FLOAT8
);
(If you don't use the double-quotes, it will convert UserId into Userid and your F# code won't compile.)

Struggling to manually create relations with gorm / migrate

I have an sql structure like so:
CREATE TABLE resources (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
deleted_at TIMESTAMPTZ
);
CREATE TABLE tags (
name TEXT PRIMARY KEY
);
What sql do I need to write, how can I tell gorm that I want a Resource to have many Tags? This is what I have currently go-wise:
package models
import (
"github.com/jinzhu/gorm"
)
type Tag struct {
Name string `gorm:"PRIMARY_KEY"`
}
type Resource struct {
gorm.Model
Title string
Tags []Tag `gorm:""`
}
Note that I explicitly do not want to auto-migrate via gorm. I am using the migrate tool to handle migrations and want to specifically handle them manually, not with go.
To define a has many relationship, a foreign key must exist.
So for your case Tag should be:
type Tag struct {
gorm.Model
Name string `gorm:"PRIMARY_KEY"`
ResourceID int
}
and Resource:
type Resource struct {
gorm.Model
Title string
Tags []Tag `gorm:"foreignkey:ResourceID"`
}
And your sql structure should have that foreign key ResourceID column.
Not sure if you have already checked this but it contains more details in case you need them: https://gorm.io/docs/has_many.html#Foreign-Key

Grails doesn't create foreign key column in PostgreSQL

I am having trouble generating my tables in PostgreSQL from Grails. I have simple Email and EmailAttachment domain classes with a hasMany and belongsTo relationship. This setup worked well on our production server (AS400 DB2), but when I try to run my program on PostgreSQL (the new dev environment), the Email class does not have the attachment_id column.
Email.groovy:
class Email {
static hasMany = [attachments:EmailAttachment]
Integer id
Integer version = 0
String subject
String recipients
String sender
Date sentDate
String plainTextMessage
Set attachments
static mapping = {
datasources(['DEFAULT'])
table name:'error_email', schema: Appointment.schema
sort sentDate:'desc'
}
static constraints = {
subject nullable:true
version nullable:true
recipients nullable:true
sender nullable:true
sentDate nullable:true
plainTextMessage nullable:true
attachments nullable:true
}
def String toString(){
return subject
}
}
EmailAttachment.groovy:
class EmailAttachment {
static belongsTo = [email:ErrorEmail]
ErrorEmail email
String filename
byte[] content
static mapping = {
datasources(['DEFAULT'])
table name:'error_email_attachment', schema: Appointment.schema
}
static constraints = {
filename nullable:true
content nullable:true
}
}
Also, here are the relevant lines from schema-export:
alter table program.email_attachment drop constraint FK2E592AFD1D80E229;
drop table program.email cascade;
drop table program.email_attachment cascade;
drop sequence hibernate_sequence;
create table program.email (id int4 not null, version int4, plain_text_message varchar(255), recipients varchar(255), sender varchar(255), sent_date timestamp, subject varchar(255), primary key (id));
create table program.email_attachment (id int8 not null, version int8 not null, content bytea, email_id int4 not null, filename varchar(255), primary key (id));
alter table program.email_attachment add constraint FK2E592AFD1D80E229 foreign key (email_id) references program.error_email;
create sequence hibernate_sequence;
I've tried specifying joinTable: attachments joinTable:[name: 'email_table', column: 'attachment_id', key: 'id'] to no avail, as well as leaving it out, and trying other collection types for attachment. Thanks in advance for your time and brain cells.
The email doesn't have an attachment_id column because it's the one side of the one-to-many. The many side, attachment in this case, has a reference to its owning email in the email_id int4 not null column. Given an email with id 42 you (and Grails/GORM/Hibernate) can find all of the attachments by querying for all rows in that table with email_id=42.

Deal with foreign keys

I found a way of defining a primary key in Play:
case class User(id: Pk[Long], name: String)
But I didn't find any way to deal with foreign keys. Is there any one or do I have to use it as a normal field?
You can treat it as a normal field.
By the way you can check sample Play! framework applications. You can find them within the Play! distribution in folder "samples". For example, check computer-database project. There are foreign keys in database, but in the code they are treated like a normal fields.
Evolution:
create table company (
id bigint not null,
name varchar(255) not null,
constraint pk_company primary key (id))
;
create table computer (
id bigint not null,
name varchar(255) not null,
introduced timestamp,
discontinued timestamp,
company_id bigint,
constraint pk_computer primary key (id))
;
alter table computer add constraint fk_computer_company_1 foreign key (company_id) references company (id) on delete restrict on update restrict;
Code:
case class Computer(id: Pk[Long] = NotAssigned, name: String, introduced: Option[Date], discontinued: Option[Date], companyId: Option[Long])

"Can't adapt type" error on a Foreign key attribute with SQLAlchemy/PostGreSQL

I got a problem with PostGreSQL 8.4 and tables reflection. My metadata object seems to be ok (it has foreign keys, primary keys, every columns and tables). But when I try to associate an object to an another one through a Foreign key, I get : "sqlalchemy.exc.ProgrammingError: (ProgrammingError) can't adapt type 'EventParameters' 'INSERT INTO event (".
I'm using SQLAlchemy 0.6.3 (psycopg2 2.2.1) and PostGreSQL 8.4.5
Here, my code :
#! /usr/bin/env python
from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import mapper, sessionmaker
class EventParameters(object):
pass
class Event(object):
pass
engine = create_engine('postgresql://postgres:toto#127.0.0.1:5432/renass')
metadata = MetaData()
metadata.reflect(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
mapper(EventParameters, metadata.tables['eventparameters'])
mapper(Event, metadata.tables['event'])
ep = EventParameters()
ep.publicid = 'test'
e = Event()
e.publicid = 'test'
e.eventparametersid=ep
session.add(e)
session.commit()
and my database :
CREATE TABLE EventParameters (
id SERIAL PRIMARY KEY,
publicID varchar(255) UNIQUE NOT NULL,
description varchar(255),
creationInfo_agencyID varchar(64),
creationInfo_agencyURI varchar(255),
creationInfo_author varchar(128),
creationInfo_authorURI varchar(255),
creationInfo_creationTime time,
creationInfo_version varchar(64)
);
CREATE TABLE Event (
id SERIAL PRIMARY KEY,
eventParametersID integer NOT NULL REFERENCES EventParameters(id),
publicID varchar(255) UNIQUE NOT NULL,
preferredOriginID integer,
preferredMagnitudeID integer,
preferredFocalMechanismID integer,
type EventType,
typeCertainty EventTypeCertainty,
creationInfo_agencyID varchar(64),
creationInfo_agencyURI varchar(255),
creationInfo_author varchar(128),
creationInfo_authorURI varchar(255),
creationInfo_creationTime time,
creationInfo_version varchar(64)
);
It seems that SQLAlchemy don't recognize the attribute "eventparametersid" as a relationship ...
Thank you in advance
Fabien
To make your code working you should change the mapper invocation for the Event class to include the mapping between the two classes
mapper(Event, metadata.tables['event'], properties={
'eventparameters': relation(EventParameters)
})
then use that column to assign the EventParameters instance
ep = EventParameters()
ep.publicid = 'test'
e = Event()
e.publicid = 'test'
e.eventparameters = ep