Documentsragflow
Cross-Database Compatibility
Cross-Database Compatibility
Type
Topic
Status
Published
Created
Jul 9, 2026
Updated
Jul 9, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Cross-Database Compatibility#

RAGFlow supports three relational database backends — MySQL (default), PostgreSQL, and OceanBase (MySQL-compatible) — selected at runtime via the DB_TYPE environment variable . Most compatibility work is concentrated in api/db/services/connector_service.py and api/db/db_models.py.


Interval Expression Differences#

The most common SQL dialect friction is time arithmetic used in connector polling queries. MySQL and PostgreSQL use incompatible syntax for computing intervals from a column value:

BackendExpression
MySQLNOW() - INTERVAL t2.refresh_freq MINUTE
PostgreSQLNOW() AT TIME ZONE '{TIMEZONE}' - make_interval(mins => t2.refresh_freq)

Both list_sync_tasks() and _list_due_tasks_for_freq() branch on os.getenv("DB_TYPE", "mysql") at runtime and emit the appropriate SQL() literal. PostgreSQL's path additionally injects the TIMEZONE setting (see below) for timezone-aware comparisons.

The PostgreSQL syntax was introduced in PR #11398 to fix a startup failure when running against Postgres. PR #14446 later added NOW() AT TIME ZONE '{TIMEZONE}' to the Postgres path to make the comparison timezone-aware.


Timezone Configuration#

TIMEZONE is read from the TZ environment variable and defaults to "Asia/Shanghai" . It is only injected into the PostgreSQL interval expression; the MySQL expression relies on the server's session timezone. Setting TZ is required when the Postgres server's TimeZone GUC differs from the application's intended timezone.


Enum-Based Infrastructure Routing#

api/db/db_models.py uses Python enums to dispatch database-specific implementations without inline if/else chains :

  • PooledDatabase — maps MYSQLRetryingPooledMySQLDatabase, POSTGRESRetryingPooledPostgresqlDatabase
  • DatabaseMigrator — maps MYSQL/OCEANBASEMySQLMigrator, POSTGRESPostgresqlMigrator
  • DatabaseLock — maps MYSQL/OCEANBASEMysqlDatabaseLock, POSTGRESPostgresDatabaseLock
  • TextFieldType — maps MYSQL/OCEANBASELONGTEXT, POSTGRESTEXT , used by the LongTextField custom field class

The active backend is resolved once from settings.DATABASE_TYPE at startup; all enum lookups key on DATABASE_TYPE.upper().


Migration Guards#

Schema migrations also require branching. PR #13582 introduced DB_TYPE guards in api/db/db_models.py around MySQL-specific DDL (e.g., primary-key migrations that use ROW_NUMBER() OVER (ORDER BY ...) on Postgres vs. a different approach on MySQL). PostgreSQL schema introspection uses the pg_indexes system catalog; MySQL uses information_schema.statistics .


Key Files and References#

FileRelevance
api/db/services/connector_service.pyInterval expression branching in list_sync_tasks and _list_due_tasks_for_freq
api/db/db_models.pyEnum routing, LongTextField, migration guards
common/settings.pyDB_TYPEDATABASE_TYPE, TZTIMEZONE
PR #11398Initial Postgres interval fix
PR #14446Timezone-aware interval expressions
PR #13582Migration DB_TYPE guards
Cross-Database Compatibility | Dosu