drizzle-orm: [BUG]: Support MySQL UNSIGNED

What version of drizzle-orm are you using?

0.21.1

Describe the Bug

When using the following schema definition:

import { mysqlTable, serial, bigint } from "drizzle-orm/mysql-core";

export const users = mysqlTable("users", {
  id: serial("id"),
});

export const pages = mysqlTable("pages", {
  id: serial("id"),
  authorId: bigint("author_id", { mode: "bigint" })
    .notNull()
    .references(() => users.id, { onUpdate: "cascade", onDelete: "cascade" }),
});

Running drizzle-kit generate:mysql generates the following invalid schema:

CREATE TABLE `pages` (
  `id` serial AUTO_INCREMENT,
  `author_id` bigint NOT NULL
);

CREATE TABLE `users` (
  `id` serial AUTO_INCREMENT
);

ALTER TABLE pages 
  ADD CONSTRAINT pages_author_id_users_id_fk 
  FOREIGN KEY (`author_id`) 
  REFERENCES users(`id`) 
    ON DELETE cascade 
    ON UPDATE cascade;

This schema results in the following MySQL-Error:

Referencing column 'author_id' and referenced column 'id' in foreign key constraint 'pages_author_id_users_id_fk' are incompatible.

The issues with this schema are:

  • SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE in MySQL so AUTO_INCREMENT is not needed here.
  • SERIAL alias to an BIGINT UNSIGNED wich is not a valid FOREIGN KEY for author_id since this compiles to SIGNED.

Proposal

To fix this issue we need to be able to mark columns as UNSIGNED afaik. Something like:

interface MySqlBigIntConfig<T extends 'number' | 'bigint' = 'number' | 'bigint'> {
    mode: T;
++  unsigned: boolean
}

Additional Version Info

  • MySQL: 8.0.26
  • drizzle-kit: 0.17.0

Docs

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 17
  • Comments: 16 (1 by maintainers)

Most upvoted comments

Any news on this? This is a major issue making drizzle almost unusable and there’s no real fix:

/ccing the team to get awareness, would be happy if you guys could focus on this before allocating resources to new features/tools, e.g. Studio, thanks for your understanding @dankochetov @AndriiSherman @AlexBlokh

the only work-around is to not use drizzle’s ts declaration but rather other tools like Atlas and declare in SQL and let drizzle only introspect which brings you quite far at the cost of a so-so DX

it’s really sad since drizzle is the only lib with a sane architecture and approach

looking fwd to your feedback 🙂

@HaNdTriX I don’t know if it helps, but I’m currently creating a custom type as a workaround:

import { customType, mysqlTable, serial } from "drizzle-orm/mysql-core";

const unsignedBigint = customType<{ data: number }>({
    dataType() {
        return "bigint UNSIGNED";
    }
});

export const users = mysqlTable("users", {
    id: serial("id"),
});

export const pages = mysqlTable("pages", {
    id: serial("id"),
    authorId: unsignedBigint("author_id")
        .notNull()
        .references(() => users.id, { onUpdate: "cascade", onDelete: "cascade" }),
});

If you want, you can also create a custom id column type for any type of integer like this:

import { customType } from "drizzle-orm/mysql-core";

type IdType = "tinyint" | "smallint" | "mediumint" | "int" | "bigint";
interface UIntConfig {
    type?: IdType;
}

export const unsignedIntAutoIncrement = customType<{ data: number; config: UIntConfig; primaryKey: true; default: true }>({
    dataType: (config) => {
        return `${config?.type ?? "int"} UNSIGNED AUTO_INCREMENT`;
    }
});


export function id(dbName: string, config?: UIntConfig) {
    return unsignedIntAutoIncrement(dbName, config).primaryKey()
};

@HaNdTriX thanks so much!

We had same problem with unsigned on db introspect command. Drizzle was generating simple int(for example) and used in a code simple int(without unsigned option). It was causing to generating new migration with changing column type. Also good suggestion on a syntax. We were thinking to place it same way

Good thing - it’s on a list to be included in one of next releases, so will update under this ticket as well

Would love to migrate to drizzle from prisma but definitely need unsigned ints

Bumping this issues as well. I’ve commented also on other threads but this seems to be the right one to track unsigned support.

It’s also important for MySQL’s clustered index means it’s very common to use unsigned bigint as primary key and not some uuid. With a unsigned bigint you’ve an infinite primary key, with signed it’s good too but yeah… why make compromises here and it should be an easy fix or no?

@AndriiSherman any update on the progress of this bug. Thanks

There’s even a bigger problem: if you use SERIAL in mysql, mysql creates an UNSIGNED column (next to BIGINT, UNIQUE and others). Now, when you want to define foregin keys you must match the exact type of SERIAL which is UNSIGNED but your don’t want to have SERIAL auto increment feature or unique because not every foregin key fields need it. But you can’t uses UNSIGNED because Drizzle has no API call for this. tldr you can’t use FK properly

Thank you for you fast response!

One addition regarding my proposal:

UNSIGNED is applicable for every number type in MySQL so it makes sense to make apply this setting to all number types.

@thisisandreww one issue we face with your workaround is that every time we run drizzle-kit push:mysql, it always alter the table:

ALTER TABLE `example` MODIFY COLUMN `another_id` bigint UNSIGNED NOT NULL;

It’s an issue when the table has data, because drizzle truncates the table even if the column is not really changing.

Any updates?

Can this be closed as complete in v0.29?

I would also like to bump this issue (w/ #670). @AndriiSherman, while this gets sorted out maybe @mymatsubara’s type could be added as an example in the custom types docs, with a note in the MySQL column types docs.