Nadia audits the Cartwheel schema one column at a time, and four of them are wrong. A price in a float, a timestamp with no time zone, JSON hidden inside a text column, and a primary key two thirds of the way to its ceiling. Six topics fix what can be fixed here, name the cost of the one that cannot, and explain the Postgres types that make a whole category of application code unnecessary.
6 topics
A column type is a promise the database makes to every future reader of the data. Cartwheel's schema was drawn to get a prototype shipped, and it has been carrying four broken promises since. products.price is a double precision, so a day's revenue depends on the order the rows were added up in. orders.placed_at is a bare timestamp, digits with no clock attached. products.attributes is a text column holding JSON that the Python service parses on every read, which means no index, no constraint and no statistics. And orders.id is a 4-byte integer whose sequence has already issued 1.35 billion values against a hard ceiling of 2,147,483,647.
None of these produce an error message today. That is the shape of a type mistake: it costs nothing on the day it is made, it is invisible in code review, and it becomes expensive exactly in proportion to how much data has accumulated on top of it. Three of the four are fixed in this chapter, on the real schema, with the real tradeoffs stated. The fourth — widening orders.id — needs a full rewrite of a 40-million-row table under an ACCESS EXCLUSIVE lock, so this chapter names the deadline and the price, and Chapter 3 does the migration without taking checkout down.
The other half of the chapter is the part a MySQL or Oracle background does not prepare you for. Postgres has arrays, ranges, multiranges, enums and domains as first-class types, and an exclusion constraint that can make double-booking a courier physically impossible rather than merely forbidden by the application. courier_shifts.shift becomes a tstzrange here, and the four-way overlap comparison — the one Cartwheel's dispatch service got wrong — disappears with it.
Four broken promises, none of which errors today
products.pricedouble precision
A day's revenue depends on the order the rows were added up in.
orders.placed_attimestamp
Digits with no clock attached.
products.attributestext
JSON the Python service parses on every read: no index, no constraint, no statistics.
orders.id4-byte integer
1.35 billion values already issued against a hard ceiling of 2,147,483,647. The one this chapter prices rather than fixes.