/// Provides utilities around sql keywords, like optional escaping etc.
@Deprecated('Drift is no longer using this library and will remove it in the '
    'next breaking release')
library drift.sqlite_keywords;

import 'package:drift/drift.dart';

/// A set of SQL keywords.
///
/// Drift will escape column names and identifiers that appear in this set.
const baseKeywords = {
  'ADD',
  'ABORT',
  'ACTION',
  'AFTER',
  'ALL',
  'ALTER',
  'ALWAYS',
  'ANALYZE',
  'AND',
  'AS',
  'ASC',
  'ATTACH',
  'AUTOINCREMENT',
  'BEFORE',
  'BEGIN',
  'BETWEEN',
  'BY',
  'CASCADE',
  'CASE',
  'CAST',
  'CHECK',
  'COLLATE',
  'COLUMN',
  'COMMIT',
  'CONFLICT',
  'CONSTRAINT',
  'CREATE',
  'CROSS',
  'CURRENT',
  'CURRENT_DATE',
  'CURRENT_TIME',
  'CURRENT_TIMESTAMP',
  'DATABASE',
  'DEFAULT',
  'DEFERRABLE',
  'DEFERRED',
  'DELETE',
  'DESC',
  'DETACH',
  'DISTINCT',
  'DO',
  'DROP',
  'EACH',
  'ELSE',
  'END',
  'ESCAPE',
  'EXCEPT',
  'EXCLUDE',
  'EXCLUSIVE',
  'EXISTS',
  'EXPLAIN',
  'FAIL',
  'FALSE',
  'FILTER',
  'FIRST',
  'FOLLOWING',
  'FOR',
  'FOREIGN',
  'FROM',
  'FULL',
  'GENERATED',
  'GLOB',
  'GROUP',
  'GROUPS',
  'HAVING',
  'IF',
  'IGNORE',
  'IMMEDIATE',
  'IN',
  'INDEX',
  'INDEXED',
  'INITIALLY',
  'INNER',
  'INSERT',
  'INSTEAD',
  'INTERSECT',
  'INTO',
  'IS',
  'ISNULL',
  'JOIN',
  'KEY',
  'LAST',
  'LEFT',
  'LIKE',
  'LIMIT',
  'MATCH',
  'NATURAL',
  'NO',
  'NOT',
  'NOTHING',
  'NOTNULL',
  'NULL',
  'NULLS',
  'OF',
  'OFFSET',
  'ON',
  'OR',
  'ORDER',
  'OTHERS',
  'OUTER',
  'OVER',
  'PARTITION',
  'PLAN',
  'PRAGMA',
  'PRECEDING',
  'PRIMARY',
  'QUERY',
  'RAISE',
  'RANGE',
  'RECURSIVE',
  'REFERENCES',
  'REGEXP',
  'REINDEX',
  'RELEASE',
  'RENAME',
  'REPLACE',
  'RIGHT',
  'RESTRICT',
  'ROLLBACK',
  'ROW',
  'ROWID',
  'ROWS',
  'SAVEPOINT',
  'SELECT',
  'SET',
  'TABLE',
  'TEMP',
  'TEMPORARY',
  'THEN',
  'TIES',
  'TO',
  'TRANSACTION',
  'TRIGGER',
  'TRUE',
  'UNBOUNDED',
  'UNION',
  'UNIQUE',
  'UPDATE',
  'USING',
  'VACUUM',
  'VALUES',
  'VIEW',
  'VIRTUAL',
  'WHEN',
  'WHERE',
  'WINDOW',
  'WITH',
  'WITHOUT',
};

/// Contains a set of all sqlite keywords, according to
/// https://www.sqlite.org/lang_keywords.html. Drift will use this list to
/// escape keywords.
const sqliteKeywords = baseKeywords;

/// A set of keywords that need to be escaped on sqlite and aren't contained
/// in [baseKeywords].
const additionalSqliteKeywords = <String>{};

/// A set of keywords that need to be escaped on postgres and aren't contained
/// in [baseKeywords].
const additionalPostgresKeywords = <String>{
  'ANY',
  'ARRAY',
  'ASYMMETRIC',
  'BINARY',
  'BOTH',
  'CURRENT_USER',
  'ILIKE',
  'LEADING',
  'LOCALTIME',
  'LOCALTIMESTAMP',
  'GRANT',
  'ONLY',
  'OVERLAPS',
  'PLACING',
  'SESSION_USER',
  'SIMILAR',
  'SOME',
  'SYMMETRIC',
  'TRAILING',
  'USER',
};

/// A set of keywords that need to be escaped on mariadb and aren't contained
/// in [baseKeywords].
const additionalMariaDBKeywords = <String>{
  'ACCESSIBLE',
  'ASENSITIVE',
  'AUTO_INCREMENT',
  'BIGINT',
  'BINARY',
  'BLOB',
  'BOTH',
  'CALL',
  'CHANGE',
  'CHAR',
  'CHARACTER',
  'CONDITION',
  'CONTINUE',
  'CONVERT',
  'CURRENT_ROLE',
  'CURRENT_USER',
  'CURSOR',
  'DATABASES',
  'DAY_HOUR',
  'DAY_MICROSECOND',
  'DAY_MINUTE',
  'DAY_SECOND',
  'DEC',
  'DECIMAL',
  'DECLARE',
  'DELAYED',
  'DELETE_DOMAIN_ID',
  'DESCRIBE',
  'DETERMINISTIC',
  'DISTINCTROW',
  'DIV',
  'DO_DOMAIN_IDS',
  'DOUBLE',
  'DUAL',
  'ELSEIF',
  'ENCLOSED',
  'ESCAPED',
  'EXIT',
  'FETCH',
  'FLOAT',
  'FLOAT4',
  'FLOAT8',
  'FORCE',
  'FULLTEXT',
  'GENERAL',
  'GRANT',
  'HIGH_PRIORITY',
  'HOUR_MICROSECOND',
  'HOUR_MINUTE',
  'HOUR_SECOND',
  'IGNORE_DOMAIN_IDS',
  'IGNORE_SERVER_IDS',
  'INFILE',
  'INOUT',
  'INSENSITIVE',
  'INT',
  'INT1',
  'INT2',
  'INT3',
  'INT4',
  'INT8',
  'INTEGER',
  'INTERVAL',
  'ITERATE',
  'KEYS',
  'KILL',
  'LEADING',
  'LEAVE',
  'LINEAR',
  'LINES',
  'LOAD',
  'LOCALTIME',
  'LOCALTIMESTAMP',
  'LOCK',
  'LONG',
  'LONGBLOB',
  'LONGTEXT',
  'LOOP',
  'LOW_PRIORITY',
  'MASTER_HEARTBEAT_PERIOD',
  'MASTER_SSL_VERIFY_SERVER_CERT',
  'MAXVALUE',
  'MEDIUMBLOB',
  'MEDIUMINT',
  'MEDIUMTEXT',
  'MIDDLEINT',
  'MINUTE_MICROSECOND',
  'MINUTE_SECOND',
  'MOD',
  'MODIFIES',
  'NO_WRITE_TO_BINLOG',
  'NUMERIC',
  'OPTIMIZE',
  'OPTION',
  'OPTIONALLY',
  'OUT',
  'OUTFILE',
  'PAGE_CHECKSUM',
  'PARSE_VCOL_EXPR',
  'POSITION',
  'PRECISION',
  'PROCEDURE',
  'PURGE',
  'READ',
  'READS',
  'READ_WRITE',
  'REAL',
  'REF_SYSTEM_ID',
  'REPEAT',
  'REQUIRE',
  'RESIGNAL',
  'RETURN',
  'RETURNING',
  'REVOKE',
  'RLIKE',
  'ROW_NUMBER',
  'SCHEMA',
  'SCHEMAS',
  'SECOND_MICROSECOND',
  'SENSITIVE',
  'SEPARATOR',
  'SHOW',
  'SIGNAL',
  'SLOW',
  'SMALLINT',
  'SPATIAL',
  'SPECIFIC',
  'SQL',
  'SQLEXCEPTION',
  'SQLSTATE',
  'SQLWARNING',
  'SQL_BIG_RESULT',
  'SQL_CALC_FOUND_ROWS',
  'SQL_SMALL_RESULT',
  'SSL',
  'STARTING',
  'STATS_AUTO_RECALC',
  'STATS_PERSISTENT',
  'STATS_SAMPLE_PAGES',
  'STRAIGHT_JOIN',
  'TERMINATED',
  'TINYBLOB',
  'TINYINT',
  'TINYTEXT',
  'TRAILING',
  'TYPE',
  'UNDO',
  'UNLOCK',
  'UNSIGNED',
  'USAGE',
  'USE',
  'UTC_DATE',
  'UTC_TIME',
  'UTC_TIMESTAMP',
  'VARBINARY',
  'VARCHAR',
  'VARCHARACTER',
  'VARYING',
  'WHILE',
  'WRITE',
  'XOR',
  'YEAR_MONTH',
  'ZEROFILL',
  'BODY',
  'ELSIF',
  'GOTO',
  'HISTORY',
  'MINUS',
  'PACKAGE',
  'PERIOD',
  'ROWNUM',
  'ROWTYPE',
  'SYSDATE',
  'SYSTEM',
  'SYSTEM_TIME',
  'VERSIONING',
};

/// Returns whether [s] is an sql keyword by comparing it to the
/// [sqliteKeywords].
bool isSqliteKeyword(String s) => sqliteKeywords.contains(s.toUpperCase());

final _notInKeyword = RegExp('[^A-Za-z_0-9]');

/// Escapes [s] by wrapping it in backticks if it's an sqlite keyword.
String escapeIfNeeded(String s, [SqlDialect dialect = SqlDialect.sqlite]) {
  final inUpperCase = s.toUpperCase();
  var isKeyword = baseKeywords.contains(inUpperCase);

  if (dialect == SqlDialect.postgres) {
    isKeyword |= additionalPostgresKeywords.contains(inUpperCase);
  }

  if (dialect == SqlDialect.mariadb) {
    isKeyword |= additionalMariaDBKeywords.contains(inUpperCase);
  }

  if (isKeyword || _notInKeyword.hasMatch(s)) return dialect.escape(s);

  return s;
}
