question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Disable automatically included RETURNING clause in INSERT statements

See original GitHub issue

Problem

When I create a row using prisma: await prisma.organization.create({ ... }), I see prisma automatically includes a RETURNING id clause:

INSERT INTO "public"."Organization" ("id","name","createdAt","updatedAt") VALUES ($1,$2,$3,$4) RETURNING "public"."Organization"."id"

However, we are using Postgres RLS, and we have some policies that allow the creation of "Organization"s but the policies do not allow to view those created rows(at least not in the same query).

We don’t have a need for that ID, we simply ignore the result, i.e. await prisma...; with no assignment to a variable.

Suggested solution

I suggest a way, so we can explicitly disable the RETURNING clause, maybe like: prisma.organization.create({ data, select: { id: false }}) or prisma.organization.create({ data, select: false}).

Alternatives

Additional context

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:8 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
eduhenkecommented, Oct 21, 2021

the RETURNING statement happened when we did the prisma.model.create({...}), and that violated our RLS policies. However, as we now are using the prismaWithoutOrganization, that was created by the “superuser”, the RLS policies aren’t being applied(because when the owner of the tables queries, the RLS policies can be bypassed, see BYPASSRLS on Postgres), so the RETURNING statement does not break the query, because we have “super” permissions.


We didn’t have any issue with connection pooling overriding the SET, because as commented in the other issue(https://github.com/prisma/prisma/issues/5128#issuecomment-921179713), we are using the SET LOCAL, which sets for the duration of the current transaction, and as we are doing the SET in the same transaction as the query(i.e. prisma.$transaction([setVar, prismaQuery]), the variables are indeed local to that current transaction in that connection.

We did have an issue(with more infos here: https://www.postgresql.org/message-id/flat/56842412.5000005@joeconway.com) with the SET LOCAL command. It impacted our RLS policies, because sometimes we wanted to check if a certain variable was NULL(i.e. was not set), however because of the aforementioned problem, we had to change our policies to not check if it was NULL, but to check if it was the empty string, or some other “nully” value we considered.

e.g. we have a policy that checks if there is a user or not(in case of service accounts that can access all reservations we represent them with “-1”, or single users that can only access their only reservations we represent them with their ID):

ALTER TABLE "Reservation" ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS reservation_policy ON "Reservation";
CREATE POLICY reservation_policy ON "Reservation" USING (
  ((current_setting('app.current_user_id', true)::int = -1) OR ("userId" = current_setting('app.current_user_id', true)::int))
);
1reaction
eduhenkecommented, Oct 21, 2021

@hyusetiawan we did a workaround that was viable for us. In our case, the problem that RLS was solving was mostly reading the data, i.e. we need 100% certainty that the user is READing the correct data, we didn’t need any validation when INSERTing new records. So we created two instances of PrismaClient:


const prisma = new PrismaClient({
  datasources: {
    // we gave this one a normal user, which means that RLS is applied to this user
    db: { url: 'postgres://normaluser:password@host/db' },
  },
});
const prismaWithoutSecurity = new PrismaClient({
  datasources: {
    // we gave this one a "superuser", which means that he is the owner of all tables, so RLS is not applied to this user
    db: { url: 'postgres://superuser@host/db' },
  },
});

// we only apply the rls middleware to the "normal"/unprivileged PrismaClient
prisma.$use(createRlsMiddleware(prisma));
  • In our background processes that need 100% control of the database, we feed those modules the prismaWithoutSecurity.
  • In modules that serve the end-user data, that are required to have the policies applied, we feed those modules the normal prisma.
  • In modules that only insert new data coming from the end-user, where we don’t need any validation(from Postgres RLS at least), we use the prismaWithoutSecurity:
    await prismaWithoutSecurity.organization.create({ ... });

(We actually do validate in another place if the user has an auth scope that allows him to create organizations, but we don’t need that validation in the Postgres RLS)

Read more comments on GitHub >

github_iconTop Results From Across the Web

how flask-sqlalchemy turn off returning id while insert data
Set this to False to disable the automatic usage of RETURNING. Since version 2.4 Flask-SQLAlchemy provides a SQLALCHEMY_ENGINE_OPTIONS ...
Read more >
Script: Use RETURNING Clause to Avoid ... - Oracle Live SQL
Description The RETURNING clause allows you to retrieve values of columns (and expressions based on columns) that were modified by an insert, ...
Read more >
RETURNING - SQLite
The RETURNING clause only returns rows that are directly modified by the DELETE, INSERT, or UPDATE statement. The RETURNING clause does not ...
Read more >
Executing Insert/Update/Delete Statements with a Returning ...
Your application must execute the Insert, Update, or Delete statement with the Returning clause using a CallableStatement object. In addition, your application ...
Read more >
OUTPUT Clause (Transact-SQL) - SQL Server - Microsoft Learn
An UPDATE, INSERT, or DELETE statement that has an OUTPUT clause will return rows to the client even if the statement encounters errors...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found