supabase / postgrest-js

Isomorphic JavaScript client for PostgREST.

Home Page:https://supabase.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bulk upsert not working (typescript)

dejoma opened this issue · comments

commented

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

When upserting data

let bugExample = [ {id: 1, title: "Hello" }, { id: 2, title: "WOrld" } ];
 const { error } = await supabase
        .from("modules")
        .upsert(bugExample);
// 'record "new" has no field "updated_at"' , code: 42703

bugExample = bugExample.map((elem) => ({...elem, updated_at: Date.now()}));
....
// table modules has no column updated_at

These errors come through. Records exist, and actually all data is supplied again.

System information

  • OS: macOS
  • Version of supabase-js: 2.10.0
  • Version of Node.js: 18.15.0

'record "new" has no field "updated_at"' , code: 42703

That sounds like an outdated trigger in your table which contains an updated_at column

commented

What I tried to do for further debugging:

  • Create new table and one by one add columns
  • Decouple my "foreign key" table (e.g. modules_books where foreign keys of both tables were mentioned)
  • Update "new table" until it was exactly the same as the old table

And then it suddenly worked. I compared my MR's afterwards: the supabase generated typescript DB file was exactly the same.

the supabase generated typescript DB file was exactly the same.

The typescript types won't detect errors inside triggers. The CLI would have to parse the internal SQL function to do that. I don't think any ORM does it.

@soedirgo Do you think the above would be possible? Or if it should be done?

@soedirgo Nvm, I think this should be fixed at the database side. I'll open an issue on supabase/postgres.


Problem

This is a pure SQL repro. Using the example here:

CREATE TABLE employees(
   id INT GENERATED ALWAYS AS IDENTITY,
   first_name VARCHAR(40) NOT NULL,
   last_name VARCHAR(40) NOT NULL,
   PRIMARY KEY(id)
);

CREATE TABLE employee_audits (
   id INT GENERATED ALWAYS AS IDENTITY,
   employee_id INT NOT NULL,
   last_name VARCHAR(40) NOT NULL,
   changed_on TIMESTAMP(6) NOT NULL
);

CREATE OR REPLACE FUNCTION log_last_name_changes()
  RETURNS TRIGGER
  LANGUAGE PLPGSQL
  AS
$$
BEGIN
	IF NEW.last_name <> OLD.last_name THEN
		 INSERT INTO employee_audits(employee_id,last_name,changed_on)
		 VALUES(OLD.id,OLD.last_name,now());
	END IF;

	RETURN NEW;
END;
$$;

We'll break the trigger with this:

alter table employees rename COLUMN last_name TO lastname;

An UPDATE should now give a similar error to OP's:

UPDATE employees SET lastname = 'Brown' WHERE ID = 2;

ERROR:  record "new" has no field "last_name"
CONTEXT:  PL/pgSQL function log_last_name_changes() line 3 at IF

Solution

This can be detected with plpgsql_check, which is already in Supabase:

create extension plpgsql_check ;

select * from plpgsql_check_function('log_last_name_changes', 'employees');
                  plpgsql_check_function
----------------------------------------------------------
 error:42703:3:IF:record "new" has no field "last_name"
 Context: SQL expression "NEW.last_name <> OLD.last_name"

We could try enabling an event trigger that uses plgpsql_check for every trigger.

Notes

BEGIN ATOMIC already checks dependencies in functions, but it only works on pure SQL functions. Triggers do not work with SQL: "ERROR: SQL functions cannot return type trigger".

commented

Thanks for looking into this 👍🏻