Source code for squabble.rules.require_primary_key

import pglast
from pglast.enums import ConstrType

import squabble.rule
from squabble.message import Message
from squabble.rules import BaseRule


[docs]class RequirePrimaryKey(BaseRule): """ Require that all new tables specify a ``PRIMARY KEY`` constraint. Configuration: :: { "RequirePrimaryKey": {} } """
[docs] class MissingPrimaryKey(Message): """ When creating a new table, it's usually a good idea to define a primary key, as it can guarantee a unique, fast lookup into the table. If no single column will uniquely identify a row, creating a composite primary key is also possible. .. code-block:: sql CREATE TABLE users (email VARCHAR(128) PRIMARY KEY); -- Also valid CREATE TABLE users ( email VARCHAR(128), -- ... PRIMARY KEY(email) ); """ CODE = 1002 TEMPLATE = 'table "{tbl}" does not name a primary key'
def enable(self, ctx, _config): ctx.register('CreateStmt', self._create_table()) @squabble.rule.node_visitor def _create_table(self, ctx, node): seen_pk = False def _check_constraint(_ctx, constraint): nonlocal seen_pk if constraint.contype == ConstrType.CONSTR_PRIMARY: seen_pk = True def _check_column(_ctx, col): if col.constraints == pglast.Missing: return for c in col.constraints: _check_constraint(_ctx, c) ctx.register('ColumnDef', _check_column) ctx.register('Constraint', _check_constraint) ctx.register_exit(lambda c: self._check_pk(c, seen_pk, node)) def _check_pk(self, ctx, seen_pk, table_node): """ Make sure we've seen a primary key constraint by the time the `CREATE TABLE` statement is finished. """ if seen_pk: return # Use the table's name as the reported error's location node = table_node.relation ctx.report(self.MissingPrimaryKey(tbl=node.relname.value), node=node)