Extension:ODBC
Release status: stable |
|
|---|---|
| Implementation | Parser function, Special page |
| Description | Generic ODBC database connectivity for MediaWiki. Query any ODBC-accessible database directly from wiki pages using five parser functions, with prepared statement support, SQL injection protection, an admin interface for connection testing and table browsing, optional result caching, and seamless integration with the External Data extension. |
| Author(s) | Paul Vodrazka |
| Latest version | 1.5.0 (2026-03-03) |
| MediaWiki | >= 1.39.0 |
| PHP | >= 7.4.0 |
| Database changes | No |
|
|
|
|
|
|
| Licence | GNU General Public License 2.0 or later |
| Download | GitHub:
Note: README CHANGELOG |
The ODBC extension provides generic ODBC database connectivity for MediaWiki. It enables wiki editors to query any database that has an ODBC driver — including Microsoft SQL Server, MySQL, PostgreSQL, Oracle, IBM DB2, Microsoft Access, SQLite, SAP HANA, Snowflake, and Amazon Redshift — directly from wiki pages using parser functions.
All queries are executed read-only, with multi-layered SQL injection protection. Results are stored in page-scoped variables and rendered using inline wikitext templates or wiki templates.
Why ODBC?
[edit]- Universal Database Access: One extension connects MediaWiki to any ODBC-accessible database — no vendor-specific extensions required.
- Secure by Default: Prepared statements, SQL pattern blocklists, identifier validation, and read-only enforcement prevent SQL injection by design.
- Flexible: Five parser functions cover single-value lookups, tabular display, and template-driven rendering.
- External Data Compatible: Registers an
odbc_genericconnector for the External Data extension, extending its ecosystem to ODBC sources.
Use Cases
[edit]| Use Case | Example | Features Used |
|---|---|---|
| Business Dashboards | Display KPIs, sales figures, or inventory counts from a SQL Server data warehouse | #odbc_query with prepared statements, #odbc_value for single metrics
|
| Employee Directories | Searchable staff listings from an HR database | Prepared statements with parameters, #display_odbc_table with a wiki template
|
| Product Catalogues | Render product tables from a MySQL database | Composed queries, #for_odbc_table for inline wikitable rows
|
| Report Archives | Surface historical data from a legacy Access or DB2 database | DSN-based connections, query caching for performance |
| IT Asset Tracking | List servers, licenses, or configurations from a CMDB | Special:ODBCAdmin for ad-hoc exploration, prepared statements for wiki pages
|
| Data Integration | Combine ODBC data with other External Data sources (CSV, JSON, LDAP) | odbc_generic connector with the External Data extension
|
Installation
[edit]- Download and place the file(s) in a directory called
ODBCin yourextensions/folder. - Add the following code at the bottom of your LocalSettings.php file:
wfLoadExtension( 'ODBC' );
- Ensure the PHP ODBC extension (
ext-odbc) is installed and enabled:- Windows: Usually included; enable
extension=php_odbc.dllinphp.iniif needed. - Linux (Debian/Ubuntu):
sudo apt install php-odbc - Linux (RHEL/CentOS):
sudo yum install php-odbc
- Windows: Usually included; enable
- Install an ODBC driver for your target database (e.g., Microsoft ODBC Driver for SQL Server, MySQL Connector/ODBC, PostgreSQL ODBC).
- Configure data sources in
LocalSettings.php(see Configuration below). - Developers running tests should run
composer installwithin theODBCdirectory.
Done – Navigate to Special:Versionon your wiki to verify that the extension is successfully installed.
Configuration
[edit]Data Sources ($wgODBCSources)
[edit]Each data source gets a unique ID key in $wgODBCSources. Three connection modes are supported:
Mode 1 — System/User DSN
[edit]$wgODBCSources['my-access-db'] = [
'dsn' => 'MyAccessDSN',
'user' => '',
'password' => '',
];
Mode 2 — Driver-based connection
[edit]$wgODBCSources['sql-server'] = [
'driver' => 'ODBC Driver 17 for SQL Server',
'server' => 'localhost,1433',
'database' => 'MyDatabase',
'user' => 'sa',
'password' => 'YourPassword123',
'trust_certificate' => true,
];
Mode 3 — Raw connection string
[edit]$wgODBCSources['oracle-db'] = [
'connection_string' => 'Driver={Oracle in OraDB19Home1};DBQ=myserver:1521/myservice;',
'user' => 'myuser',
'password' => 'mypass',
];
Prepared Statements (recommended)
[edit]$wgODBCSources['employees'] = [
'driver' => 'ODBC Driver 17 for SQL Server',
'server' => 'hr-server.local,1433',
'database' => 'HumanResources',
'user' => 'wiki_reader',
'password' => 'ReadOnly123',
'prepared' => [
'get_employee' => 'SELECT FirstName, LastName, Department FROM Employees WHERE EmployeeID = ?',
'dept_list' => 'SELECT DISTINCT Department FROM Employees ORDER BY Department',
'search' => 'SELECT FirstName, LastName, Title FROM Employees WHERE Department = ? AND Title LIKE ?',
],
];
Per-Source Options
[edit]| Option | Type | Description |
|---|---|---|
timeout |
integer | Per-source query timeout in seconds (overrides $wgODBCQueryTimeout).
|
allow_queries |
boolean | Allow composed (ad-hoc) queries for this source even when $wgODBCAllowArbitraryQueries is false. Use sparingly.
|
charset |
string | Explicit encoding (e.g. 'ISO-8859-1'). When set, encoding auto-detection is skipped for this source.
|
dsn_params |
array | Extra key=value pairs appended to the driver connection string (e.g. ['Encrypt' => 'yes']).
|
trust_certificate |
boolean | Add TrustServerCertificate=yes for SQL Server with self-signed certificates.
|
host |
string | Progress OpenEdge only. Alternative to server.
|
db |
string | Progress OpenEdge only. Alternative to database.
|
prepared |
array | Associative array of named prepared SQL statements: 'name' => 'SQL with ? placeholders'.
|
Global Parameters
[edit]| Variable | Type | Default | Description |
|---|---|---|---|
$wgODBCSources |
array | {} |
Associative array of ODBC data source configurations. Each key is a source ID. |
$wgODBCAllowArbitraryQueries |
boolean | false |
If true, wiki editors with odbc-query permission can pass composed (ad-hoc) SQL. Keep false in production.
|
$wgODBCMaxRows |
integer | 1000 |
Maximum number of rows returned by any single ODBC query. |
$wgODBCQueryTimeout |
integer | 30 |
Query timeout in seconds. |
$wgODBCCacheExpiry |
integer | 0 |
Number of seconds to cache query results. 0 disables caching. Cached per unique SQL+parameters+maxRows combination.
|
$wgODBCExternalDataIntegration |
boolean | true |
Register as an odbc_generic connector for External Data. Must be set before wfLoadExtension( 'ODBC' ).
|
$wgODBCMaxQueriesPerPage |
integer | 0 |
Maximum #odbc_query calls per page render. 0 = unlimited.
|
$wgODBCSlowQueryThreshold |
float | 0 |
Execution time in seconds above which a query is logged to the odbc-slow log channel. 0 = disabled.
|
$wgODBCMaxConnections |
integer | 10 |
Maximum cached connections per PHP worker process. In PHP-FPM, total system connections = this × active workers. |
Permissions
[edit]| Right | Description | Default Groups |
|---|---|---|
odbc-query |
Use ODBC parser functions (#odbc_query, etc.) on wiki pages |
sysop
|
odbc-admin |
Access Special:ODBCAdmin for connection testing, table browsing, and test queries |
sysop
|
Grant to additional groups as needed:
$wgGroupPermissions['user']['odbc-query'] = true;
Parser Functions
[edit]The extension provides five parser functions that work as a pipeline: fetch data with #odbc_query, display with #odbc_value, #for_odbc_table, or #display_odbc_table, and optionally reset with #odbc_clear.
All parser function names are case-insensitive — {{#odbc_query:}}, {{#ODBC_QUERY:}}, and {{#Odbc_Query:}} are all equivalent.
#odbc_query — Fetch Data
[edit]Executes a query and stores results in page-scoped variables.
Prepared statement mode (recommended):
{{#odbc_query: source=employees
| query=get_employee
| parameters=12345
| data=first=FirstName,last=LastName,dept=Department
}}
Composed query mode (requires allow_queries or $wgODBCAllowArbitraryQueries):
{{#odbc_query: source=my-source
| from=Products
| data=name=ProductName,price=UnitPrice
| where=Active=1
| order by=ProductName ASC
| limit=50
}}
#odbc_value — Display a Single Value
[edit]{{#odbc_value: name}}
{{#odbc_value: dept | (Unassigned) }}
{{#odbc_value: name | N/A | 2 }}
{{#odbc_value: name | N/A | last }}
#for_odbc_table — Loop with Inline Wikitext
[edit]{| class="wikitable"
! Name !! Department
{{#for_odbc_table:
{{!}}-
{{!}} {{{name}}} {{!}}{{!}} {{{dept}}}
}}
|}
#display_odbc_table — Loop with Wiki Template
[edit]{{#display_odbc_table: template=EmployeeRow }}
Calls {{EmployeeRow|name=...|dept=...|email=...}} for each row.
#odbc_clear — Clear Stored Data
[edit]{{#odbc_clear:}}
{{#odbc_clear: name,dept}}
For complete syntax, parameters, and worked examples, see ODBC Guide.
External Data Integration
[edit]When External Data is installed and $wgODBCExternalDataIntegration is true (the default), ODBC registers an odbc_generic connector. This allows External Data's parser functions to query ODBC sources:
$wgExternalDataSources['my-odbc'] = [
'type' => 'odbc_generic',
'driver' => 'MySQL ODBC 8.0 Unicode Driver',
'server' => 'mysql-server.local',
'name' => 'production_db',
'user' => 'readonly',
'password' => 'secret',
];
{{#get_db_data: db=my-odbc
| from=users
| data=username=user_name,email=user_email
| where=active=1
}}
$wgODBCExternalDataIntegration = false; before wfLoadExtension( 'ODBC' ); in LocalSettings.php.Admin Interface
[edit]Special:ODBCAdmin (requires odbc-admin permission) provides:
- Connection overview — View all configured sources with driver, server, and database details
- Connection testing — Verify ODBC connectivity to each source
- Table browser — List all tables available in a source
- Column inspector — Inspect table schema and column types
- Query runner — Execute SELECT queries and view results (non-SELECT blocked; SQL sanitization enforced)
Supported Databases
[edit]Any database with an ODBC driver should work. Tested and known-compatible drivers:
| Database | Common ODBC Driver |
|---|---|
| Microsoft SQL Server | ODBC Driver 17 for SQL Server / ODBC Driver 18 for SQL Server
|
| MySQL / MariaDB | MySQL ODBC 8.0 Unicode Driver
|
| PostgreSQL | PostgreSQL Unicode
|
| Oracle | Oracle in OraDB19Home1
|
| Microsoft Access | Microsoft Access Driver (*.mdb, *.accdb)
|
| IBM DB2 | IBM DB2 ODBC DRIVER
|
| SQLite | SQLite3 ODBC Driver
|
| SAP HANA | HDBODBC
|
| Snowflake | SnowflakeDSIIDriver
|
| Amazon Redshift | Amazon Redshift (x64)
|
| Progress OpenEdge | Progress OpenEdge (uses host/db keys)
|
Security
[edit]Design Principles
[edit]- Read-only by design — Only SELECT queries are permitted. DROP, DELETE, INSERT, UPDATE, EXEC, and 40+ other dangerous patterns are blocked by a multi-layered sanitizer.
- Prepared statements — The recommended query mode; prevents SQL injection by design.
- Identifier validation — Table and column names are validated against a strict regex (alphanumeric, underscores, and up to 3 dot-separated segments only).
- Error sanitization — Credentials are stripped from ODBC error messages before display.
- CSRF protection — All state-changing actions in
Special:ODBCAdminrequire a valid session token. - Connection pooling limits — Configurable per-worker connection cap with LRU eviction prevents resource exhaustion.
Recommendations
[edit]- Use prepared statements wherever possible
- Keep
$wgODBCAllowArbitraryQueries = falsein production - Use a read-only database account (SELECT-only privileges)
- Restrict
odbc-queryto trusted user groups - Secure
LocalSettings.phpfile permissions (contains plain-text credentials)
Troubleshooting
[edit]- "ODBC extension not found"
- Install
ext-odbc:apt install php-odbc(Debian/Ubuntu) or enableextension=php_odbc.dll(Windows). Restart the web server.
- "Could not connect"
- Verify the ODBC driver is installed (
odbcinst -q -don Linux, ODBC Data Source Administrator on Windows). Test the DSN outside of MediaWiki withisql. Check firewall rules, credentials, and server hostname.
- "Arbitrary SQL not allowed"
- Either define prepared statements (recommended), set
'allow_queries' => trueon the specific source, or set$wgODBCAllowArbitraryQueries = trueglobally.
- "Invalid identifier" / "Illegal SQL pattern"
- The query contains blocked characters or keywords. Use prepared statements, or ensure composed queries use only allowed patterns.
- Query returns no results
- Check column-name case sensitivity, verify
data=mappings, reviewwhere=conditions, and useSpecial:ODBCAdminto browse the table structure.
- Performance issues
- Enable caching (
$wgODBCCacheExpiry), add database indexes, uselimit=, and consider slow-query logging ($wgODBCSlowQueryThreshold).
Technical Details
[edit]- Architecture: Modular PHP classes —
ODBCConnectionManager(connection pooling, DSN construction, LRU eviction),ODBCQueryRunner(query execution, SQL sanitization),ODBCParserFunctions(parser function implementations),EDConnectorOdbcGeneric(External Data bridge),SpecialODBCAdmin(admin interface). - Stability: 242 PHPUnit tests with PHPStan level 3 static analysis and MediaWiki code style enforcement via PHP_CodeSniffer.
- CI: GitHub Actions pipeline with PHP syntax lint (7.4–8.4), PHPUnit, PHPStan, PHPCS, and release-readiness checks.
See also
[edit]- Extension:ODBC/Guide — Detailed usage guide with complete syntax reference and examples
- Full Changelog
- Project Wiki
- Extension:External Data — Complementary extension for CSV, JSON, LDAP, and other data sources
