Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [Drizzle] Convert MySQL timestamps to datetimes #1676

Open
Wundero opened this issue Nov 23, 2023 · 3 comments
Open

feat: [Drizzle] Convert MySQL timestamps to datetimes #1676

Wundero opened this issue Nov 23, 2023 · 3 comments
Labels
🌟 enhancement New feature or request

Comments

@Wundero
Copy link

Wundero commented Nov 23, 2023

Is your feature request related to a problem? Please describe.

For date + time related fields, MySQL's timestamp field is only 32 bits, and will run into issues once 2038 comes around. This isn't a giant issue per se, and users can certainly correct this on their own, but IMO there really is no harm in switching to datetime columns instead, since they can hold dates from 1000-01-01 00:00:00 to 9999-12-31 23:59:59, which will last a lot longer.

Describe the solution you'd like to see

Prisma already stores datetimes like this (reference), and switching drizzle is as simple as:

// Example model schema from the Drizzle docs
// https://orm.drizzle.team/docs/sql-schema-declaration

import { sql } from "drizzle-orm";
import {
  bigint,
  index,
  mysqlTableCreator,
-  timestamp,
+  datetime,
  varchar,
} from "drizzle-orm/mysql-core";

/**
 * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
 * database instance for multiple projects.
 *
 * @see https://orm.drizzle.team/docs/goodies#multi-project-schema
 */
export const mysqlTable = mysqlTableCreator((name) => `project1_${name}`);

export const posts = mysqlTable(
  "post",
  {
    id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
    name: varchar("name", { length: 256 }),
-   createdAt: timestamp("created_at")
-     .default(sql`CURRENT_TIMESTAMP`)
+   createdAt: datetime("created_at", { fsp: 3 })
+     .defaultCurrentTimestamp()
      .notNull(),
-    updatedAt: timestamp("updatedAt").onUpdateNow(),
+    updatedAt: datetime("updatedAt", { fsp: 3 }).onUpdateCurrentTimestamp(),
  },
  (example) => ({
    nameIndex: index("name_idx").on(example.name),
  })
);

This is using a sub-second precision of 3 points, which is in-line with what Prisma does, just for consistency.

Describe alternate solutions

N/A

Additional information

The syntax for onUpdateCurrentTimestamp requires drizzle-team/drizzle-orm#1114 to be merged, and I don't know that Drizzle currently supports the syntax necessary for auto-updates directly, though I believe you can just insert it into the default SQL like so:

updatedAt: datetime("updatedAt", { fsp: 3 }).default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`),
@Wundero Wundero added the 🌟 enhancement New feature or request label Nov 23, 2023
@c-ehrlich
Copy link
Member

seems reasonable. do you want to open a pr for this?

@Wundero
Copy link
Author

Wundero commented Dec 21, 2023

seems reasonable. do you want to open a pr for this?

I'm watching the Drizzle issue which will introduce the change for the default timestamp, since I don't want to rely on semantics that are kind of undefined behavior (the on update logic being in the default statement) in code that will likely ship to production applications. Once that goes through, I will probably make a PR if no one else has.

@statusunknown418
Copy link

statusunknown418 commented Jan 17, 2024

hey @Wundero this line

updatedAt: datetime("updatedAt", { fsp: 3 }).default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`),

throws this error when trying db:push

    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ER_INVALID_DEFAULT',
  errno: 1067,
  sql: 'CREATE TABLE `post` (\n' +
    '\t`id` bigint AUTO_INCREMENT NOT NULL,\n' +
    '\t`name` varchar(256),\n' +
    '\t`createdById` varchar(255) NOT NULL,\n' +
    '\t`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n' +
    '\t`updatedAt` datetime(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n' +
    '\tCONSTRAINT `post_id` PRIMARY KEY(`id`)\n' +
    ');\n',
  sqlState: '42000',
  sqlMessage: 'target: pathways.-.primary: vttablet: rpc error: code = InvalidArgument desc = Invalid default value for \'updatedAt\' (errno 1067) (sqlstate 42000) (CallerID: mi3x34888e3ekca4weoi): Sql: "create table post (\\n\\tid bigint not null auto_increment,\\n\\t`name` varchar(256),\\n\\tcreatedById varchar(255) not null,\\n\\tcreated_at timestamp not null default current_timestamp(),\\n\\tupdatedAt datetime(3) default current_timestamp() on update current_timestamp(),\\n\\tconstraint post_id primary key (id)\\n)", BindVars: {REDACTED}'
}

[EDIT]: It does work, I was using timestamp instead of datetime as the column type lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌟 enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants