Column Names Like rank and key Are Reserved Words on Some SQL Engines
A leaderboard table with a column called rank works on SQLite, PostgreSQL and MariaDB. On MySQL 8 the CREATE TABLE fails, because rank became a reserved word when window functions arrived. BareSQL Portable knows 83 names like these.
Every engine keeps a list of words that can’t be names without quotes, and the lists differ. PostgreSQL reserves user, only and array; MySQL and MariaDB reserve key, index, range, rows and many more, and MySQL 8 added rank, groups, row and other words of window functions and grouping. SQLite reserves the fewest. The table lists words people often give to tables and columns, each tried as a column name on all four engines. As a column label after AS, PostgreSQL takes any word, so there only MySQL, MariaDB and SQLite matter.

What Each Engine Answered
The example
CREATE TABLE t (id INTEGER, rank INTEGER);
gave these answers:
| Engine | Answer |
|---|---|
| SQLite 3.45.1 | no rows |
| PostgreSQL 16.15 | no rows |
| MySQL 8.0.46 | error: You have an error in your SQL syntax ... near 'rank INTEGER)' at line 1 |
| MariaDB 10.11.14 | no rows |
Every Form BareSQL Checks
BareSQL Portable checks 83 forms of this, and each was run on all four engines. yes means the engine ran it and the engines that ran it agreed, no means the engine refused it, and a value is what the engine answered where they disagreed.
| Form | SQLite | PostgreSQL | MySQL | MariaDB |
|---|---|---|---|---|
USER |
yes | no | yes | yes |
KEY |
yes | yes | no | no |
KEYS |
yes | yes | no | no |
RANK |
yes | yes | no | yes |
GROUPS |
yes | yes | no | yes |
ROW |
yes | yes | no | yes |
ROWS |
yes | yes | no | no |
RANGE |
yes | yes | no | no |
FUNCTION |
yes | yes | no | yes |
SYSTEM |
yes | yes | no | yes |
SCHEMA |
yes | yes | no | yes |
DATABASE |
yes | yes | no | yes |
CHANGE |
yes | yes | no | no |
CONDITION |
yes | yes | no | no |
DESCRIBE |
yes | yes | no | no |
DIV |
yes | yes | no | no |
MOD |
yes | yes | no | no |
DUAL |
yes | yes | no | no |
INTERVAL |
yes | yes | no | no |
READ |
yes | yes | no | no |
WRITE |
yes | yes | no | no |
RELEASE |
yes | yes | no | no |
RETURN |
yes | yes | no | no |
USAGE |
yes | yes | no | no |
SIGNAL |
yes | yes | no | no |
OPTION |
yes | yes | no | yes |
LOCK |
yes | yes | no | no |
MATCH |
yes | yes | no | no |
RENAME |
yes | yes | no | no |
REPLACE |
yes | yes | no | no |
SQL |
yes | yes | no | no |
OFFSET |
yes | no | yes | no |
WINDOW |
yes | no | no | yes |
LATERAL |
yes | no | no | yes |
RETURNING |
no | no | yes | no |
VALUES |
no | yes | no | no |
INDEX |
no | yes | no | no |
SET |
no | yes | no | no |
END |
yes | no | yes | yes |
ONLY |
yes | no | yes | yes |
ARRAY |
yes | no | yes | yes |
CAST |
yes | no | yes | yes |
FULL |
yes | no | yes | yes |
LEFT |
yes | no | no | no |
RIGHT |
yes | no | no | no |
DESC |
yes | no | no | no |
ASC |
yes | no | no | no |
COLUMN |
yes | no | no | no |
BINARY |
yes | no | no | no |
ANALYZE |
yes | no | no | no |
EMPTY |
yes | yes | no | yes |
VIRTUAL |
yes | yes | no | yes |
STORED |
yes | yes | no | yes |
GENERATED |
yes | yes | no | yes |
LEAD |
yes | yes | no | yes |
LAG |
yes | yes | no | yes |
OF |
yes | yes | no | yes |
OVER |
yes | yes | no | no |
PARTITION |
yes | yes | no | no |
PRECISION |
yes | yes | no | no |
REAL |
yes | yes | no | no |
FLOAT |
yes | yes | no | no |
DOUBLE |
yes | yes | no | no |
TRIGGER |
yes | yes | no | no |
CURSOR |
yes | yes | no | no |
EXIT |
yes | yes | no | no |
LOOP |
yes | yes | no | no |
REPEAT |
yes | yes | no | no |
DO |
yes | no | yes | yes |
CUBE |
yes | yes | no | yes |
GROUPING |
yes | yes | no | yes |
RECURSIVE |
yes | yes | no | no |
SEPARATOR |
yes | yes | no | no |
LINES |
yes | yes | no | no |
LOAD |
yes | yes | no | no |
SHOW |
yes | yes | no | no |
EXPLAIN |
yes | yes | no | no |
BEFORE |
yes | yes | no | no |
EACH |
yes | yes | no | no |
CASCADE |
yes | yes | no | no |
RESTRICT |
yes | yes | no | no |
IGNORE |
yes | yes | no | no |
OUT |
yes | yes | no | no |
What to Write Instead
This form answers the same on every engine:
CREATE TABLE t (id INTEGER, rank_no INTEGER);
| Engine | Answer |
|---|---|
| SQLite 3.45.1 | no rows |
| PostgreSQL 16.15 | no rows |
| MySQL 8.0.46 | no rows |
| MariaDB 10.11.14 | no rows |
Pick another name, such as user_name, rank_no or sort_key. Quoting the name is no portable way out: double quotes name a column on SQLite and PostgreSQL and make a string on MySQL and MariaDB (see Double quotes), and backticks work on SQLite, MySQL and MariaDB only (see backticks).
What BareSQL Portable Reports
Saved as example.sql, the example gets this from baresql lint --all:
$ baresql lint --all example.sql
example.sql:1:29: error: The name rank works on SQLite, PostgreSQL and MariaDB; MySQL refuses it. [reserved-word]
rank
Pick another name, such as user_name, rank_no or sort_key. Quoting the name is no portable way out: double quotes name a column on SQLite and PostgreSQL and make a string on MySQL and MariaDB (see double-quotes), and backticks work on SQLite, MySQL and MariaDB only (see backticks).
1 problem: 1 error.
baresql rules reserved-word prints the same answers in the terminal, and the BareSQL Portable page lists every difference BareSQL knows.