Prisma with psql db - incompatible types: text and uuid - psql

I am trying to learn how to use prisma with a psql database.
I'm running into an issue using references where the id is a uuid string.
I have a user model with:
model User {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
request Request?
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model Request {
id Int #id #default(autoincrement())
user User #relation(fields: [id], references: [id])
// I also tried making the relation field userId
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
When I try to migrate this, I get an error that says:
failed to apply cleanly to the shadow database. Error: db error:
ERROR: foreign key constraint "Request_userId_fkey" cannot be
implemented DETAIL: Key columns "userId" and "id" are of incompatible
types: text and uuid.
The prisma documents dont show an example using uuid.
The example they do give has a second parameter in the Profile model which has a userId as an Int. I tried adding this to my Request model (as an int, as a string and as a uuid). None of these worked.
model User {
id Int #id #default(autoincrement())
email String #unique
name String?
role Role #default(USER)
posts Post[]
profile Profile?
}
model Profile {
id Int #id #default(autoincrement())
bio String
user User #relation(fields: [userId], references: [id])
userId Int
}
How can I reference a userId when it is generated using uuid?
This segment of the prisma documentation suggests (if I have understood it correctly), that any of String or Int or enum should work to recognise a uuid:
Relational databases Corresponding database type: PRIMARY KEY
Can be annotated with a #default() value that uses functions to
auto-generate an ID:
autoincrement() cuid() uuid() Can be defined on any scalar field
(String, Int, enum)
When I try adding the pgcrypto extension to psql, I try to run the migration again and get an error that has less verbose messaging, but still similar issue:
Error parsing attribute "#relation": The type of the field id in the
model Request is not matching the type of the referenced field id
in model User.
I have seen this discussion which suggests somehow lying to prisma. I am not clever enough to understand the gist of what the lie is supposed to be or how to do it.
Someone on github suggested using this referencing syntax in the request model:
user User #relation(fields: [userId], references: [id])
userId String #unique #db.Uuid
I tried it as above, and without the #unique flag, but I still get a migration error that says that uuid and text are incompatible references. I can't find a section of the prisma documentation that addresses how to make uuid references compatible with relation models.
fyi: the migration file for the attempt above shows the following:
CREATE TABLE "Request" (
"id" SERIAL NOT NULL,
"userId" UUID NOT NULL,
CONSTRAINT "Request_pkey" PRIMARY KEY ("id")
);

You will have to use the annotation #db.Uuid on the reference column userId, read more about it here.
Example:
model Request {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
userId String #db.Uuid
...your other stuff
}

In your Request model is missing the foreign key userId with #db.Uuid this will make postgreSql use the uuid type on a column and #relation the field name must be the same as the foreign key like this #relation(fields: [userId]). The complete code should look like this:
model User {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
request Request?
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model Request {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id]) <-ADD userId here
userId String #db.Uuid <-ADD THIS
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
You can do this with other types, here are some examples: https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string

I had a similar error and it was because I did not change the type of the relation's ID from Int to String from the example.
If you update the Request model to look like this, does it work?
model Request {
id Int #id #default(autoincrement())
user User #relation(fields: [id], references: [id])
userId String // <- this was missing
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}

I think that the Felix Hagspiel answer is the solution. I could add you should remove the failed generated migration sql (by removing the folder) and retry it again after fixing the problem.

Related

how to change Grails GORM id column name and use sequence

In a Grails/GORM app, can I rename the implicit domain id column to entity_id and auto-increment using an existing sequence?
class Practitioner {
Long entityId
String providerName
static mapping = {
id name: 'entityId', column: 'entity_id', generator:'sequence', params:[sequence:'hibernate_sequence']
}
}
Attempting to save in console fails, (and yes, sequence exists in my local Postgres database)
Practitioner p = new Practitioner(providerName: 'p')
p.save(flush:true, failOnError: true)
Error
grails.validation.ValidationException: Validation Error(s) occurred during save():
- Field error in object
'com.transcendinsights.dp.measureresult.snapshot.Practitioner' on field 'entityId': rejected value [null]; codes
Super thanks for helping!

why to use select statement even though inserting values in sql server database,using entity framework MVC application

I am very new to the MVC,Entity framework application and am still learning. I have a query.
I am inserting the values in SQL server database using Entity Framework and MVC application with the stored procedure. I am using the DB first approach. I want to ask why I need to add an select statement even though I have written the procedure to insert the values. If I dont write the select statement then it throws the error.
An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: The data reader is incompatible with the specified 'EmployeeDBModel.Employee'. A member of the type, 'Id', does not have a corresponding column in the data reader with the same name.
ALTER PROCEDURE [dbo].[sp_InsertEmployees]
-- Add the parameters for the stored procedure here
#FirstName varchar(50),
#LastName varchar(50),
#Address varchar(50),
#Salary int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
Insert into Employee values(#FirstName,#LastName,#Address,#Salary)
--select scope_identity() as Id,Firstname,LastName,Address,Salary from Employee
END
If I uncomment select command it just work.
Note:- Id is identity column and auto seed is true
Model :-
public partial class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public Nullable<int> Salary { get; set; }
}
Controller:
[HttpPost]
public ActionResult Create(Employee emp)
{
using (EmployeeDBEntities db = new EmployeeDBEntities())
{
if (ModelState.IsValid)
{
db.InsertEmpDetail(emp.FirstName, emp.LastName, emp.Address, emp.Salary);
db.SaveChanges();
ViewBag.Message = "New Employee Created Successfully!!";
}
}
return View(emp);
}
I have created the function InsertEmpDetail and mapped it properly.
I think if I dont remember it wrongly , when you map you entity, the orm entity framework will need to be able to read for the entity through certaion way. (sorry cant get to my net computer, but in java it often the same).
I'm guessing that your procedure have return result thus the will be read onto the entity.
another possibililty is the commit of the stored procedure, since it cant be read after insert properly, the column seems to be missing.
(just saying this have happen to me before)
good luck,
edd

Grails Elasticsearch plugin issues

I am new to Elasticsearch and am using the revived grails plugin "elasticsearch:0.0.4.6".
I wanted to implement a search for a User by their firstName, surname or username, which returns the full domain instance.
I have a 2 domain classes:
User:
class User {
String firstName
String surname
static hasOne = [profile:Profile]
static hasMany = [friends:User]
static mappedBy = [ friends: 'friends' ]
static searchable = {
profile component:true
only = ['firstName', 'surname', 'profile']
}
...
Profile:
class Profile {
String username
String status
byte[] photo
static belongsTo = [user:User]
static searchable = true
...
}
I made the classes "searchable = true" and then created the following search:
def res = User.search("${params.friendSearch}").searchResults
This found the correct instances, but now when a user adds a photo to their profile, it suceeds but I recieve a never ending loop of the following error:
ERROR index.IndexRequestQueue - Failed bulk item: MapperParsingException[failed to parse [photo]]; nested: NumberFor
matException[For input string: the photos base64inputstring
I dont really get what is happening, but i figure it must be something to do with elasticsearch being unable to index the photo data. Can somebody provide an explanation?
I then experimented with searchables custom mapping options -
profile index:'no'
all=false
excludeFromAll = true
etc
Nothing worked. Eventually I added
only = ['username']
and it stopped the error from occuring and allowed me to find users based on the criteria i mentioned above. However, because of the "only" limit, it means that the User instances returned by the seach have a photo property equal to null, but i need this value!
What am i doing wrong? Can anyone advise me on the best course of action to take or any misunderstandings i have about Elasticsearch? Thanks
I think you might have to exclude the byte property photo from the searchable fields like so:
class Profile {
String username
String status
byte[] photo
static belongsTo = [user:User]
static searchable = {
except = ['photo']
}
This will exclude the property photo from being indexed and search. Hence the output of converting the byte format to string format will not fail.
Also maybe you might need a custom convertor to change the byte(string) to something more usable in the results?

Custom type converter doesn't appear to work in Filter

I am using the GSON type converter in a filter as follows...
def account = new Object(){
String firstName, lastName
};
if(springSecurityService.isLoggedIn()){
account.setFirstName(springSecurityService.principal.givenName);
account.setLastName(springSecurityService.principal.familyName);
}
String test = account as GSON;
But test is always null, even thought the object is populated properly. I don't like all the extra data in the normal JSON serializer. So does anyone know why this does not work?
UPDATE
I get the following when converting to JSON...
Caused by: java.lang.IllegalAccessException: Class org.codehaus.groovy.grails.web.converters.marshaller.json.GenericJavaBeanMarshaller can not access a member of class org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor with modifiers "public"

Specify Candidate Key In grails?

How can i specify "name" as primary key...by specifying through domain class groovy file.. i'm working in fedora as OS and through terminal (not using ggts or any tools) and using database as MySQL
class Department {
String name
String description
}
To change which field is your primary key, you can use the id mapping entry like so:
class Department {
String name
static mapping = {
id name: 'name', generator: 'assigned'
}
}
You can read more details about the options on the id entry in the Grails Documentation.

Resources