Skip to content

Stop forcing *increments() columns to be the table primary key#7

Merged
eaguad1337 merged 2 commits into
3.xfrom
circulon/increments-primary
Jun 7, 2026
Merged

Stop forcing *increments() columns to be the table primary key#7
eaguad1337 merged 2 commits into
3.xfrom
circulon/increments-primary

Conversation

@eaguad1337

Copy link
Copy Markdown
Contributor

Migrated from MasoniteFramework/orm#950 (original author: @circulon) as part of the move to the masonitedev organization. Ported, evaluated, and hardened for 3.x.

Closes #4

What this does

increments(), tiny_increments() and big_increments() no longer implicitly mark the column as the table's primary key, aligning them with every other datatype usable as a primary key. This unblocks tables that pair a non-integer primary key (e.g. uuid) with an auto-numbered column — previously that raised QueryException: table has more than one primary key:

with self.schema.create("invoice") as table:
    table.uuid("id").primary()
    table.big_increments("reference")   # now works
    ...
  • .primary() on an *increments() column compiles the PRIMARY KEY inline through dedicated *_increments_primary type mappings.
  • id() keeps the previous convenience behavior (auto-incrementing big-integer primary key).
  • The internal migrations table schema declares .primary() explicitly.
  • Schema now honors explicitly provided connection_details instead of always reloading them from the config file (same pattern #948 introduced for the QueryBuilder).

⚠️ BREAKING

Migrations relying on *increments() implying the primary key must add .primary():

table.increments("id")            # 3.0.x: primary key — now: plain auto-increment (raises on SQLite)
table.increments("id").primary()  # previous behavior
table.id()                        # unchanged convenience

Platform-SQL fixes added on top of the original PR

The original mappings compiled to invalid SQL on two platforms:

Platform Original PR This PR Why
SQLite BIGINT PRIMARY KEY AUTOINCREMENT INTEGER PRIMARY KEY AUTOINCREMENT AUTOINCREMENT is only valid on INTEGER PRIMARY KEY; the tiny/big variants were syntax errors. The rowid alias is 64-bit, so big_increments loses no range.
MSSQL INT PRIMARY KEY IDENTITY INT IDENTITY NOT NULL PRIMARY KEY T-SQL grammar puts IDENTITY before column constraints; PRIMARY KEY is now emitted via the constraint slot after nullability.
MySQL (non-primary) INT UNSIGNED AUTO_INCREMENT INT UNSIGNED AUTO_INCREMENT UNIQUE MySQL requires an AUTO_INCREMENT column to be defined as a key (error 1075).
Postgres (non-primary) SERIAL SERIAL UNIQUE Keeps the UNIQUE the old SERIAL UNIQUE mapping carried; consistent with MySQL.

SQLite still raises a descriptive QueryException for non-primary *increments() (SQLite cannot autoincrement a non-PK column).

Tests

  • Split the SQLite unsupported-types test so each variant actually asserts (blocks 2 and 3 were dead code inside the first assertRaises).
  • Added compile assertions for id() and big_increments().primary() on SQLite plus an executed id() table creation against the real test database, so invalid AUTOINCREMENT DDL can't slip through string-only tests again.
  • Changelog entry moved to an Unreleased section (3.0.0 already shipped without this change).
  • Full suite: 1037 passed, 1 pre-existing failure (requires a live Postgres server — fails identically on 3.x).

circulon and others added 2 commits June 7, 2026 09:53
Ported from MasoniteFramework/orm#950.

increments(), tiny_increments() and big_increments() no longer implicitly
mark the column as the table's primary key. This aligns them with every
other datatype usable as a primary key and unblocks tables that pair a
non-integer primary key (e.g. uuid) with an auto-numbered column.

- .primary() on an *increments() column compiles the PRIMARY KEY inline
  through dedicated *_increments_primary type mappings.
- id() keeps the previous convenience behavior (big_increments + primary).
- The migrations table schema uses increments().primary() explicitly.
- SQLite raises QueryException for non-primary *increments() columns since
  SQLite only supports AUTOINCREMENT on the INTEGER PRIMARY KEY.
- Schema honors explicitly provided connection_details instead of always
  reloading them from the config file.

BREAKING: migrations relying on increments() implying the primary key must
add .primary() to the column definition to keep the previous behavior.

Closes #4
- SQLite: AUTOINCREMENT is only valid on INTEGER PRIMARY KEY, so all
  *_increments_primary variants compile to INTEGER PRIMARY KEY
  AUTOINCREMENT (the rowid alias is 64-bit, so big_increments loses no
  range). The original mapping produced invalid SQL for the tiny and big
  variants. The unreachable list-form fallback in columnize() now routes
  through the same mapping instead of appending its own constraint.
- MSSQL: T-SQL expects IDENTITY before column constraints, so the
  PRIMARY KEY is emitted after the nullability clause
  ([id] INT IDENTITY NOT NULL PRIMARY KEY) instead of the undocumented
  INT PRIMARY KEY IDENTITY order.
- MySQL: an AUTO_INCREMENT column must be defined as a key (error 1075),
  so non-primary *_increments columns now carry UNIQUE.
- Postgres: non-primary serial columns keep the UNIQUE constraint the
  previous SERIAL UNIQUE mapping carried, staying consistent with MySQL.
- Split the SQLite unsupported-types test so each variant actually
  asserts (the second and third blocks were dead code inside the first
  assertRaises), add compile assertions for id() and
  big_increments().primary() on SQLite, and execute an id() table
  creation against the real test database.
- Move the changelog entry to an Unreleased section: 3.0.0 already
  shipped without this change.
@eaguad1337 eaguad1337 merged commit 36c1a7a into 3.x Jun 7, 2026
14 checks passed
@eaguad1337 eaguad1337 deleted the circulon/increments-primary branch June 7, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

increments() and *_increments() force the column to be the primary key

2 participants