1. Omid Sayfun
  2. /
  3. Notebook
  • Home
  • About
  • Notebook
  • Token Usage
  • Whisper Usage
Tools
  • Agent Knowledge Base

PostgreSQL doesn't have ON UPDATE CURRENT_TIMESTAMP. Here's the fix

February 27, 2024 · Updated on August 09, 2026

PostgreSQL has no ON UPDATE CURRENT_TIMESTAMP clause. Schemas ported from MySQL usually keep DEFAULT CURRENT_TIMESTAMP on the updated_at column and expect the value to stay fresh, but in Postgres that default only fires on insert. It does not auto update the row on later writes. Here's the MySQL setup people port over:

CREATE TABLE Users (
	...
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

In MySQL that second column re-stamps itself on every UPDATE. In PostgreSQL the same column keeps whatever value it got at insert time.

I write short, practical notes like this one. Get the next one by email:

Unsubscribe anytime.

Use a trigger to auto update the timestamp

A trigger is a function Postgres runs automatically when an event happens on a table, such as an insert, an update, or a delete. Triggers are how you enforce business rules, keep audit columns current, or maintain data integrity. They're also the replacement for MySQL's ON UPDATE CURRENT_TIMESTAMP.

Start with the function itself. It sets the updated_at field on the row Postgres is about to write, then returns that row:

CREATE OR REPLACE FUNCTION trigger_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Attach the function to the table with BEFORE UPDATE

The function does nothing until a trigger calls it. Attach it to the Users table so it runs before each row is written. The full syntax is in the PostgreSQL CREATE TRIGGER documentation:

CREATE TRIGGER update_users_updated_on
    BEFORE UPDATE
    ON
        Users
    FOR EACH ROW
EXECUTE PROCEDURE trigger_updated_at();

Now the updated_at column is automatically set to the current timestamp every time a row is modified. Inserts still work as before, because the column default handles those.

Drizzle ORM writes it back in application code

Drizzle has no way to declare a Postgres trigger in the schema. Its $onUpdate helper runs in the client and stamps the column on every update the ORM issues:

updatedAt: timestamp('updated_at').defaultNow().$onUpdate(() => new Date()),

That covers writes going through Drizzle. Writes from psql, a migration, or another service still need the trigger.

tl;dr

To auto-update a timestamp in PostgreSQL like MySQL's ON UPDATE CURRENT_TIMESTAMP, use a trigger. Create a trigger function to set updated_at to NOW() on updates, then activate it on your table with a BEFORE UPDATE trigger.

Join My Newsletter

Occasional notes on software, tools, and things I learn. No spam.

Unsubscribe anytime.

Continue Reading

  • Embeddings rot too. Running pgvector in production.Aug 28, 2026
  • Stop shipping retrieval changes on vibesAug 25, 2026
  • Your RAG is confidently wrong without hybrid searchAug 21, 2026
  • Stop tuning everything. pgvector has three knobs that matter.Aug 18, 2026
  • Your agent's knowledge base is lying to you. Run these 14 checks.Aug 18, 2026