A PHP C extension that provides the mysqli API backed by PostgreSQL via libpq. Drop-in replacement that lets existing PHP applications written for MySQL/mysqli run against PostgreSQL with zero code changes.
PHP userland (mysqli_* calls) → mysqlipg C extension → SQL translator (C) → libpq → PostgreSQL
PHP 8.0+ with development headers (php-devel / php-dev)
PostgreSQL client library (libpq-devel / libpq-dev)
C compiler (gcc/clang)
# Install dependencies (Fedora/RHEL)
dnf install php-devel libpq-devel
# Install dependencies (Debian/Ubuntu)
apt install php-dev libpq-dev
# Build the extension
cd php-mysqlipg
phpize
./configure --with-mysqlipg
make
make test # runs 34 tests
# Install system-wide (optional)
make install
# Load the extension
echo " extension=mysqlipg.so" >> $( php-config --ini-dir) /mysqlipg.ini
# Or load per-invocation
php -d extension=mysqlipg.so your_script.php
php -d extension=modules/mysqlipg.so -m | grep mysqlipg
# → mysqlipg
php -d extension=modules/mysqlipg.so -r " phpinfo();" | grep mysqlipg
# → mysqlipg support => enabled
// Connect to PostgreSQL using the mysqli-style API
$ conn = mysqlipg_connect ('localhost ' , 'postgres ' , 'password ' , 'mydb ' , 5432 );
// Queries are automatically translated from MySQL to PostgreSQL
$ result = mysqlipg_query ($ conn , "SHOW TABLES " );
while ($ row = mysqlipg_fetch_assoc ($ result )) {
print_r ($ row );
}
// MySQL functions are translated transparently
$ result = mysqlipg_query ($ conn , "SELECT IFNULL(name, 'unknown'), RAND() FROM users LIMIT 10,5 " );
// Becomes: SELECT COALESCE(name, 'unknown'), RANDOM() FROM users LIMIT 5 OFFSET 10
// Backtick-quoted identifiers work
$ result = mysqlipg_query ($ conn , "SELECT `user`.`name` FROM `user` WHERE `id` = 1 " );
// Prepared statements
$ stmt = mysqlipg_prepare ($ conn , "SELECT * FROM users WHERE age > ? AND city = ? " );
mysqlipg_stmt_bind_param ($ stmt , "is " , $ age , $ city );
mysqlipg_stmt_execute ($ stmt );
$ result = mysqlipg_stmt_get_result ($ stmt );
while ($ row = mysqlipg_fetch_assoc ($ result )) {
print_r ($ row );
}
mysqlipg_stmt_close ($ stmt );
mysqlipg_close ($ conn );
$ conn = new MysqliPg ('localhost ' , 'postgres ' , 'password ' , 'mydb ' , 5432 );
$ result = $ conn ->query ("SELECT DATABASE() AS db " );
$ row = $ result ->fetch_assoc ();
echo $ row ['db ' ]; // current PostgreSQL database name
// Transactions
$ conn ->begin_transaction ();
$ conn ->query ("INSERT INTO orders (product, qty) VALUES ('Widget', 5) " );
$ conn ->query ("UPDATE inventory SET stock = stock - 5 WHERE product = 'Widget' " );
$ conn ->commit ();
// Prepared statements
$ stmt = $ conn ->prepare ("INSERT INTO logs (msg, level) VALUES (?, ?) " );
$ stmt ->bind_param ("si " , $ message , $ level );
$ stmt ->execute ();
echo $ conn ->insert_id ;
$ stmt ->close ();
$ conn ->close ();
The extension automatically translates MySQL SQL to PostgreSQL. 80+ patterns are handled:
MySQL
PostgreSQL
`table`.`column`
"table"."column"
MySQL
PostgreSQL
RAND()
RANDOM()
IFNULL(a, b)
COALESCE(a, b)
DATABASE()
current_database()
CURDATE()
CURRENT_DATE
SYSDATE()
CLOCK_TIMESTAMP()
LAST_INSERT_ID()
lastval()
IF(cond, a, b)
CASE WHEN cond THEN a ELSE b END
ISNULL(x)
(x IS NULL)
DATEDIFF(a, b)
(a::date - b::date)
DATE_ADD(d, INTERVAL)
(d + INTERVAL)
DATE_FORMAT(d, '%Y-%m-%d')
TO_CHAR(d, 'YYYY-MM-DD')
STR_TO_DATE(s, fmt)
TO_TIMESTAMP(s, pg_fmt)
YEAR(d) / MONTH(d) / DAY(d)
EXTRACT(YEAR FROM d)
LAST_DAY(d)
(DATE_TRUNC('month',d)+INTERVAL '1 month'-INTERVAL '1 day')::date
GROUP_CONCAT(col SEPARATOR ',')
STRING_AGG(col::text, ',')
FIELD(v, a, b, c)
ARRAY_POSITION(ARRAY[a,b,c], v)
ELT(n, a, b, c)
(ARRAY[a,b,c])[n]
FIND_IN_SET(v, 'a,b,c')
v = ANY(string_to_array('a,b,c',','))
STRCMP(a, b)
CASE WHEN a<b THEN -1 WHEN a>b THEN 1 ELSE 0 END
LOG(x)
LN(x)
LOG2(x) / LOG10(x)
LOG(2,x) / LOG(10,x)
TRUNCATE(n, d)
TRUNC(n, d)
FROM_UNIXTIME(n)
TO_TIMESTAMP(n)
UNIX_TIMESTAMP()
EXTRACT(EPOCH FROM NOW())::bigint
CONVERT(x, UNSIGNED)
CAST(x AS BIGINT)
VERSION()
version()
DATE(col) / TIME(col)
(col)::date / (col)::time
MySQL
PostgreSQL
SHOW DATABASES
pg_database query
SHOW TABLES
information_schema.tables query
SHOW FULL TABLES
includes Table_type column
SHOW COLUMNS FROM t
information_schema.columns query
SHOW INDEX FROM t
pg_class/pg_index query
DESC table
information_schema.columns with type mapping
SHOW TABLE STATUS
information_schema.tables query
SHOW VARIABLES
pg_settings query
SHOW PROCESSLIST
pg_stat_activity query
SHOW GRANTS
role_table_grants query
SHOW WARNINGS
empty result
SHOW ENGINES
returns PostgreSQL
MySQL
PostgreSQL
INSERT ... SET col=val
INSERT ... (col) VALUES (val)
INSERT IGNORE INTO ...
INSERT ... ON CONFLICT DO NOTHING
INSERT LOW_PRIORITY ...
modifier stripped
TRUNCATE TABLE t
TRUNCATE TABLE t RESTART IDENTITY
LIMIT 10, 5
LIMIT 5 OFFSET 10
'0000-00-00 00:00:00'
NULL
MySQL
PostgreSQL
ALTER TABLE t MODIFY col TYPE
ALTER TABLE t ALTER COLUMN col TYPE
ALTER TABLE t CHANGE old new TYPE
RENAME COLUMN + ALTER TYPE
ALTER TABLE t ADD INDEX idx (col)
CREATE INDEX idx ON t (col)
ALTER TABLE t DROP INDEX idx
DROP INDEX idx
RENAME TABLE old TO new
ALTER TABLE old RENAME TO new
CREATE DATABASE [IF NOT EXISTS] db
CREATE DATABASE db
MySQL
PostgreSQL
<=>
IS NOT DISTINCT FROM
REGEXP / RLIKE
~*
NOT REGEXP
!~*
FROM DUAL
stripped
STRAIGHT_JOIN
JOIN
SQL_CALC_FOUND_ROWS
stripped
SQL_NO_CACHE
stripped
LOCK IN SHARE MODE
FOR SHARE
USE INDEX(...)
stripped
CAST(x AS UNSIGNED)
CAST(x AS BIGINT)
WITH ROLLUP
stripped
These MySQL statements are accepted and silently handled:
SET NAMES utf8mb4 → SET client_encoding = 'UTF8'
SET FOREIGN_KEY_CHECKS = 0/1 → SET session_replication_role
SET sql_mode, SET CHARACTER_SET_*, SET @@session.* → no-op
LOCK TABLES / UNLOCK TABLES → no-op
ALTER TABLE ... DISABLE/ENABLE KEYS → no-op
FLUSH PRIVILEGES → no-op
DO expr → SELECT expr
KILL pid → SELECT pg_terminate_backend(pid)
Procedural
OOP
Description
mysqlipg_connect(host, user, pass, db, port)
new MysqliPg(...)
Connect to PostgreSQL
mysqlipg_close($conn)
$conn->close()
Close connection
mysqlipg_query($conn, $sql)
$conn->query($sql)
Execute query
mysqlipg_real_escape_string($conn, $str)
$conn->real_escape_string($str)
Escape string
mysqlipg_select_db($conn, $db)
$conn->select_db($db)
Change database
mysqlipg_multi_query($conn, $sql)
$conn->multi_query($sql)
Execute multiple queries
mysqlipg_more_results($conn)
$conn->more_results()
Check for more results
mysqlipg_next_result($conn)
$conn->next_result()
Move to next result
mysqlipg_store_result($conn)
$conn->store_result()
Get result from multi-query
mysqlipg_ping($conn)
$conn->ping()
Check connection
mysqlipg_set_charset($conn, $cs)
$conn->set_charset($cs)
Set character set
mysqlipg_character_set_name($conn)
$conn->character_set_name()
Get character set
mysqlipg_stat($conn)
$conn->stat()
Server status
mysqlipg_thread_id($conn)
—
Backend PID
Procedural
OOP
Description
mysqlipg_autocommit($conn, $bool)
$conn->autocommit($bool)
Toggle autocommit
mysqlipg_begin_transaction($conn)
$conn->begin_transaction()
Start transaction
mysqlipg_commit($conn)
$conn->commit()
Commit
mysqlipg_rollback($conn)
$conn->rollback()
Rollback
mysqlipg_savepoint($conn, $name)
$conn->savepoint($name)
Create savepoint
mysqlipg_release_savepoint($conn, $name)
—
Release savepoint
Procedural
OOP Property
Description
mysqlipg_affected_rows($conn)
$conn->affected_rows
Affected row count
mysqlipg_insert_id($conn)
$conn->insert_id
Last insert ID
mysqlipg_error($conn)
$conn->error
Last error message
mysqlipg_errno($conn)
$conn->errno
Last error number
mysqlipg_sqlstate($conn)
$conn->sqlstate
SQLSTATE code
mysqlipg_field_count($conn)
$conn->field_count
Field count
—
$conn->warning_count
Warning count
—
$conn->host_info
Host info string
—
$conn->server_info
Server version string
—
$conn->server_version
Server version int
Procedural
OOP
Description
mysqlipg_fetch_assoc($result)
$result->fetch_assoc()
Fetch as assoc array
mysqlipg_fetch_array($result, $mode)
$result->fetch_array($mode)
Fetch with mode
mysqlipg_fetch_row($result)
$result->fetch_row()
Fetch as numeric array
mysqlipg_fetch_all($result, $mode)
$result->fetch_all($mode)
Fetch all rows
mysqlipg_fetch_column($result, $col)
$result->fetch_column($col)
Fetch single column
mysqlipg_num_rows($result)
$result->num_rows
Row count
mysqlipg_num_fields($result)
$result->num_fields
Field count
mysqlipg_fetch_field($result)
$result->fetch_field()
Field metadata
mysqlipg_fetch_fields($result)
$result->fetch_fields()
All field metadata
mysqlipg_data_seek($result, $offset)
$result->data_seek($offset)
Seek to row
mysqlipg_free_result($result)
$result->free()
Free result
Prepared Statement Functions
Procedural
OOP
Description
mysqlipg_prepare($conn, $query)
$conn->prepare($query)
Prepare statement
mysqlipg_stmt_bind_param($stmt, $types, ...)
$stmt->bind_param($types, ...)
Bind parameters
mysqlipg_stmt_execute($stmt)
$stmt->execute()
Execute statement
mysqlipg_stmt_get_result($stmt)
$stmt->get_result()
Get result set
mysqlipg_stmt_close($stmt)
$stmt->close()
Close statement
mysqlipg_stmt_affected_rows($stmt)
—
Affected rows
mysqlipg_stmt_num_rows($stmt)
—
Row count
mysqlipg_stmt_error($stmt)
—
Error message
mysqlipg_stmt_errno($stmt)
—
Error number
mysqlipg_stmt_insert_id($stmt)
—
Insert ID
MYSQLIPG_ASSOC, MYSQLIPG_NUM, MYSQLIPG_BOTH — fetch modes
MYSQLIPG_TYPE_* — field type constants (TINY, SHORT, LONG, FLOAT, DOUBLE, BLOB, VAR_STRING, DATE, TIME, DATETIME, etc.)
MYSQLIPG_*_FLAG — field flags (NOT_NULL, PRI_KEY, AUTO_INCREMENT, etc.)
MYSQLIPG_STORE_RESULT, MYSQLIPG_USE_RESULT
MYSQLIPG_REPORT_OFF, MYSQLIPG_REPORT_ERROR, MYSQLIPG_REPORT_STRICT, MYSQLIPG_REPORT_ALL
# Run all tests
make test
# Run specific test
make test TESTS=tests/mysqlipg_prepared_stmt.phpt
# Set custom PG connection for tests
export MYSQLIPG_TEST_HOST=localhost
export MYSQLIPG_TEST_USER=postgres
export MYSQLIPG_TEST_PASS=secret
export MYSQLIPG_TEST_DB=testdb
export MYSQLIPG_TEST_PORT=5432
make test
┌──────────────────────────────────────────┐
│ PHP Application (uses mysqli_* calls) │
├──────────────────────────────────────���───┤
│ mysqlipg C Extension │
│ ├─ mysqlipg.c Module init │
│ ├─ mysqlipg_connection.c Connect/query │
│ ├─ mysqlipg_result.c Fetch/fields │
│ ├─ mysqlipg_stmt.c Prepared stmts │
│ ├─ mysqlipg_translator.c SQL translator │
│ └─ mysqlipg_props.c Property hdlrs │
├──────────────────────────────────────────┤
│ libpq (PostgreSQL client library) │
├──────────────────────────────────────────┤
│ PostgreSQL Server │
└──────────────────────────────────────────┘
mysqlpg — MySQL-compatible CLI for PostgreSQL (Python), the translation patterns were ported from this project
MIT