fix catastrophic backtracking in sqlite inline unique reflection regex
Fixes: #13419
### Description
While reflecting a SQLite table, `get_unique_constraints` scans the stored
`CREATE TABLE` text (taken verbatim from `sqlite_master.sql`) with an
`INLINE_UNIQUE_PATTERN` to spot inline `UNIQUE` columns. The tail of that
pattern is `[\t ]+[a-z0-9_ ]+?[\t ]+UNIQUE`, where the lazy middle class
itself contains a space, so all three quantifiers can lay claim to the same
space character. When a column definition contains a run of whitespace that
isn't followed by `UNIQUE`, the engine tries every way of splitting that run
across the three quantifiers, which is cubic in the length of the run.
SQLite keeps the original whitespace of a `CREATE TABLE` statement in
`sqlite_master`, so any account that can create a table can leave a long gap
in a column definition and make later reflection of that schema hang.
A small reproduction:
```python
from sqlalchemy import create_engine, inspect
e = create_engine("sqlite://")
with e.begin() as c:
c.exec_driver_sql(
"CREATE TABLE t (x INTEGER" + " " * 1000 + "NOT NULL, "
"y INTEGER NOT NULL UNIQUE)"
)
inspect(e).get_unique_constraints("t") # ~11s before, instant after
```
The fix tokenises the inter-keyword whitespace with disjoint classes,
`[\t ]+[a-z0-9_]+(?:[\t ]+[a-z0-9_]+)*?[\t ]+UNIQUE`, so a space only ever
belongs to a separator and never to a token. Valid inline `UNIQUE` columns
reflect exactly as before; I have added a regression test that reflects a
table carrying a long whitespace run and runs in linear time on the new
pattern.
### Checklist
This pull request is:
- [x] A short code fix
Closes: #13398
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/13398
Pull-request-sha:
e398806f1243897f25c6ef0697200a49df464243
Change-Id: I7e394357162c9d3aad6c7a72d9276321d404ca64