douglasshuang

douglasshuang

High Performance PostgreSQL for Rails: `NOT NULL` constraint not part of domain as stated (page 90)

The book states the following:

The main difference for domains compared with enums is that the NOT NULL constraint portion is part of the domain.

This is incorrect. The domain has a CHECK constraint for valid non-NULL values, but the NOT NULL constraint is part of the column declaration and can be removed independently of the domain, as follows:

owner@localhost:5432 rideshare_development# ALTER TABLE vehicles ALTER COLUMN status DROP NOT NULL;
ALTER TABLE
owner@localhost:5432 rideshare_development# UPDATE vehicles SET status = NULL;
UPDATE 4
owner@localhost:5432 rideshare_development# SELECT DISTINCT(status) from vehicles;
 status
--------

(1 row)

owner@localhost:5432 rideshare_development# \d vehicles
                                        Table "rideshare.vehicles"
   Column   |              Type              | Collation | Nullable |               Default
------------+--------------------------------+-----------+----------+--------------------------------------
 id         | bigint                         |           | not null | nextval('vehicles_id_seq'::regclass)
 name       | character varying              |           | not null |
 created_at | timestamp(6) without time zone |           | not null |
 updated_at | timestamp(6) without time zone |           | not null |
 status     | vehicle_statuses               |           |          | 'draft'::text
Indexes:
    "vehicles_pkey" PRIMARY KEY, btree (id)
    "index_vehicles_on_name" UNIQUE, btree (name)
Referenced by:
    TABLE "vehicle_reservations" CONSTRAINT "fk_rails_7edc8e666a" FOREIGN KEY (vehicle_id) REFERENCES vehicles(id)

owner@localhost:5432 rideshare_development# \dD vehicle_statuses
                                                           List of domains
  Schema   |       Name       | Type | Collation | Nullable | Default |                             Check
-----------+------------------+------+-----------+----------+---------+---------------------------------------------------------------
 rideshare | vehicle_statuses | text |           |          |         | CHECK (VALUE = ANY (ARRAY['draft'::text, 'published'::text]))
(1 row)

Marked As Solved

andatki

andatki

Author of High Performance PostgreSQL for Rails

Hi @douglasshuang. You’re bringing up a great point here. I want to make a correction to the book text based on this to help readers.

Here’s the domain definition in the book:

-- This is the existing domain in the book.
CREATE DOMAIN vehicle_statuses AS TEXT
CONSTRAINT valid_vehicle_statuses
CHECK (VALUE IN ('draft', 'published') );

This omits a NOT NULL constraint in the domain definition. If we were to add a NOT NULL to the domain like this:

-- Create a new domain called vehicle_statuses_not_null and include a NOT NULL
CREATE DOMAIN vehicle_statuses_not_null AS TEXT
CONSTRAINT valid_vehicle_statuses
CHECK (VALUE IN ('draft', 'published') )
NOT NULL;

Then the domain will enforce the not null.

Here’s an example:

-- Add column “status_new_domain”  that uses the domain above with NOT NULL.
-- Empty out the table rows so we can try inserting
ALTER TABLE vehicles
ADD COLUMN status_new_domain vehicle_statuses_not_null;

-- Describe the domains. Notice the nullable property has the NOT NULL constraint.
owner@[local]:5432 rideshare_development# \dD
                                                               List of domains
 Schema   |           Name            | Type | Collation | Nullable | Default |                             Check
-----------+---------------------------+------+-----------+----------+---------+---------------------------------------------------------------
rideshare | vehicle_statuses_not_null | text |           | not null |         | CHECK (VALUE = ANY (ARRAY['draft'::text, 'published'::text]))


-- Describe the vehicles table. Only showing the “status_new_domain” column.
-- Notice here that status_new_domain shows as nullable
-- i.e. we don't see the NOT NULL from the domain, confusing!
\d vehicles
owner@[local]:5432 rideshare_development# \d vehicles
                                           Table "rideshare.vehicles"
     Column       |              Type              | Collation | Nullable |               Default
-------------------+--------------------------------+-----------+----------+--------------------------------------
status_new_domain | vehicle_statuses_not_null      |           |          |

owner@[local]:5432 rideshare_development# insert into vehicles (name) values ('draft');
ERROR:  domain vehicle_statuses_not_null does not allow null values


-- Try to insert a null value in status_new_domain
owner@[local]:5432 rideshare_development# insert into vehicles (name, status_new_domain) values ('draft', null);
ERROR:  domain vehicle_statuses_not_null does not allow null values

If we add an additional NOT NULL to the column on the table, that’s more clear. Then describing the table shows it as NOT NULL.

After considering this more since you’ve brought it up, using not null constraints in Domains to illustrate the difference between an Enum and a Domain type feels like a poor choice for learning materials. It’s useful but more like an edge case.

I would like to expand on this in a better example as a blog post, or possibly in a future iteration of the book. I think focusing on the base data type for a Domain for example, and the behavior and the functionality it brings over an Enum, would be a better example for learning the differences. We could note this gotcha with NOT NULL constraints as the documentation does.

To make a targeted correction in the book, I think we’ll add a NOT NULL constraint to the domain definition though, and a warning about the implicit behavior, to keep the changes minimal.

Docs: PostgreSQL: Documentation: 17: CREATE DOMAIN

What do you think?

Thanks again for noticing this and spending the time to write up this issue!

Where Next?

Popular Pragmatic Bookshelf topics Top

jimschubert
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
New
Alexandr
Hi everyone! There is an error on the page 71 in the book “Programming machine learning from coding to depp learning” P. Perrotta. You c...
New
mikecargal
Title: Hands-on Rust: question about get_component (page 295) (feel free to respond. “You dug you’re own hole… good luck”) I have somet...
New
cro
I am working on the “Your Turn” for chapter one and building out the restart button talked about on page 27. It recommends looking into ...
New
gilesdotcodes
In case this helps anyone, I’ve had issues setting up the rails source code. Here were the solutions: In Gemfile, change gem 'rails' t...
New
brian-m-ops
#book-python-testing-with-pytest-second-edition Hi. Thanks for writing the book. I am just learning so this might just of been an issue ...
New
hazardco
On page 78 the following code appears: <%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New
ggerico
I got this error when executing the plot files on macOS Ventura 13.0.1 with Python 3.10.8 and matplotlib 3.6.1: programming_ML/code/03_...
New
redconfetti
Docker-Machine became part of the Docker Toolbox, which was deprecated in 2020, long after Docker Desktop supported Docker Engine nativel...
New

Other popular topics Top

Devtalk
Hello Devtalk World! Please let us know a little about who you are and where you’re from :nerd_face:
New
PragmaticBookshelf
Learn from the award-winning programming series that inspired the Elixir language, and go on a step-by-step journey through the most impo...
New
PragmaticBookshelf
Ruby, Io, Prolog, Scala, Erlang, Clojure, Haskell. With Seven Languages in Seven Weeks, by Bruce A. Tate, you’ll go beyond the syntax—and...
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
New
AstonJ
I’ve been hearing quite a lot of comments relating to the sound of a keyboard, with one of the most desirable of these called ‘thock’, he...
New
Exadra37
Oh just spent so much time on this to discover now that RancherOS is in end of life but Rancher is refusing to mark the Github repo as su...
New
AstonJ
We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New
PragmaticBookshelf
Author Spotlight Rebecca Skinner @RebeccaSkinner Welcome to our latest author spotlight, where we sit down with Rebecca Skinner, auth...
New
AnfaengerAlex
Hello, I’m a beginner in Android development and I’m facing an issue with my project setup. In my build.gradle.kts file, I have the foll...
New
PragmaticBookshelf
A concise guide to MySQL 9 database administration, covering fundamental concepts, techniques, and best practices. Neil Smyth MySQL...
New

Sub Categories: