Primary key is the term for a column, or group of columns, whose values uniquely identify each row in a relational table. In standard SQL the values must be both unique and not null, and a table can have at most one primary key. Other tables point at it with foreign keys, which is how rows get linked. PostgreSQL backs it with a unique B-tree index.
Declaring PRIMARY KEY adds two rules. Two rows cannot hold the same key value, and the key columns cannot be NULL. PostgreSQL documents this as equivalent to UNIQUE plus NOT NULL, and it also creates a unique B-tree index on the key columns automatically. A key that spans several columns is a composite key, and uniqueness applies to the combination.
This run uses SQLite 3.45.1 through Python's sqlite3 module:
c.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE)")
c.execute("INSERT INTO users(email) VALUES ('a@example.com')")
c.execute("INSERT INTO users(email) VALUES ('b@example.com')")
# [(1, 'a@example.com'), (2, 'b@example.com')]
c.execute("INSERT INTO users(id,email) VALUES (1,'c@example.com')")
# IntegrityError: UNIQUE constraint failed: users.id
c.execute("DELETE FROM users WHERE id=2")
c.execute("INSERT INTO users(email) VALUES ('d@example.com')")
# [(1, 'a@example.com'), (2, 'd@example.com')]
No in the SQL standard, but SQLite is a documented exception. SQLite allows NULL in a PRIMARY KEY column unless the column is INTEGER PRIMARY KEY, the table is WITHOUT ROWID or STRICT, or you add NOT NULL. The SQLite docs call this a bug in early versions that was kept for backward compatibility. In a test with code TEXT PRIMARY KEY, two rows with a NULL key were both accepted, while the same insert into a STRICT table failed with NOT NULL constraint failed: s.code.
NOT NULL explicitly, or use STRICT tables, so NULL keys are rejected.(1, 10) and (1, 11) are distinct, but a second (1, 10) fails with UNIQUE constraint failed: enr.student_id, enr.course_id.