PHP code example of thomas-0816 / pdo-duckdb-php

1. Go to this page and download the library: Download thomas-0816/pdo-duckdb-php library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

thomas-0816 / pdo-duckdb-php example snippets


$duckDb = new PDO('duckdb::memory:', null, null, [PDO::DUCKDB_ATTR_CONFIG => ['TimeZone' => 'Europe/Berlin']]);
$duckDb->exec('CREATE TABLE table1 (id INTEGER, amount DECIMAL(10, 2), description VARCHAR)');

$statement = $duckDb->prepare('INSERT INTO table1 VALUES (?, ?, ?)');
$statement->execute([1, 42.21, 'Hello DuckDB! 🐘 💓 🦆']);

$statement = $duckDb->query('SELECT * FROM table1');
print_r($statement->fetchAll(PDO::FETCH_ASSOC));

# Array
#     [0] => Array
#         [id] => 1
#         [amount] => 42.21
#         [description] => Hello DuckDB! 🐘 💓 🦆

$db = new PDO('duckdb::memory:'); // open in-memory database

$db = new PDO('duckdb:/tmp/test.db'); // open database file from disk

// open database file as read-only
$db = new PDO('duckdb:/tmp/test.db', null, null, [
    PDO::DUCKDB_ATTR_CONFIG => ['access_mode' => 'read_only']
]);

$db = new PDO('duckdb::memory:');
$db->exec("CREATE TABLE table1 (id INTEGER, text VARCHAR USING COMPRESSION zstd, data JSON)");

$statement = $db->prepare('INSERT INTO table1 VALUES (?, ?, ?)');
$statement->execute([1, 'Hello DuckDB 🦆', ['foo' => 'bar', 'baz' => 42]]);

$db->exec("COPY (SELECT * FROM table1) TO '/tmp/table1.parquet' (COMPRESSION zstd)");

foreach ($db->query("SELECT * FROM '/tmp/table1.parquet'", PDO::FETCH_ASSOC) as $row) {
    print_r($row);
}

# Array
#     [id] => 1
#     [text] => Hello DuckDB 🦆
#     [data] => Array
#         [foo] => bar
#         [baz] => 42

$list = [
    ['aaa', 'bbb', 'ccc'],
    ['123', '456', '789'],
    ['aaa', 'bbb', 'ccc']
];
$fp = fopen('/tmp/test.csv', 'w');
foreach ($list as $fields) {
    fputcsv($fp, $fields, ',', '"', "");
}
fclose($fp);

$db = new PDO('duckdb::memory:');
$statement = $db->query("SELECT * FROM '/tmp/test.csv'");
print_r($statement->fetchAll(PDO::FETCH_ASSOC));

# Array
#     [0] => Array
#         [aaa] => 123
#         [bbb] => 456
#         [ccc] => 789
#     [1] => Array
#         [aaa] => aaa
#         [bbb] => bbb
#         [ccc] => ccc

$list = [
    ['aaa', 'bbb'],
    ['123', '456'],
    ['aaa', 'bbb']
];
$fp = fopen('/tmp/test.csv', 'w');
foreach ($list as $fields) {
    fputcsv($fp, $fields, ',', '"', "");
}
fclose($fp);

$db = new PDO('duckdb::memory:');
$db->exec("CREATE TABLE test_csv AS SELECT * FROM '/tmp/test.csv'"); // schema + data import
$db->exec("INSERT INTO test_csv SELECT * FROM '/tmp/test.csv'"); // only import data

print_r($db->query('SHOW test_csv')->fetchAll(PDO::FETCH_ASSOC));

# Array
#     [0] => Array
#         [column_name] => aaa
#         [column_type] => VARCHAR
#         [null] => YES
#     [1] => Array
#         [column_name] => bbb
#         [column_type] => VARCHAR
#         [null] => YES

file_put_contents('/tmp/logs.json', json_encode(['log' => 'log text']) . PHP_EOL, FILE_APPEND);
file_put_contents('/tmp/logs.json', json_encode(['log' => 'log text 2']) . PHP_EOL, FILE_APPEND);

$db = new PDO('duckdb::memory:');
$statement = $db->query("SELECT * FROM '/tmp/logs.json'");
print_r($statement->fetchAll(PDO::FETCH_ASSOC));

# Array
#     [0] => Array
#         [log] => log text
#     [1] => Array
#         [log] => log text 2

$db->exec("COPY (SELECT * FROM '/tmp/logs.json') TO '/tmp/logs_json.parquet' (COMPRESSION zstd)");

$db = new PDO('duckdb::memory:');
$db->exec('INSTALL excel; LOAD excel');
$db->exec('CREATE TABLE table1 (id INTEGER, text VARCHAR, amount DECIMAL(10, 2))');

$statement = $db->prepare('INSERT INTO table1 VALUES (?, ?, ?)');
$statement->execute([1, 'Hello Excel 🦆', 42.21]);

$db->exec("COPY (SELECT * FROM table1) TO '/tmp/table1.xlsx'");

$statement = $db->query("SELECT * FROM '/tmp/table1.xlsx'");
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [A1] => 1
#     [B1] => Hello Excel 🦆
#     [C1] => 42.21

// s is array{v: string, i: int, a: string[], d: float}

$db = new PDO('duckdb::memory:');
$db->exec('CREATE TABLE table1 (s STRUCT(v VARCHAR, i INTEGER, a VARCHAR[], d DECIMAL))');

$statement = $db->prepare('INSERT INTO table1 VALUES (?)');
$statement->execute([['v' => 'foo', 'i' => 21, 'a' => ['b', 'c'], 'd' => 42.21]]);

$statement = $db->query('SELECT * FROM table1');
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [s] => Array
#         [v] => foo
#         [i] => 21
#         [a] => Array
#             [0] => b
#             [1] => c
#         [d] => 42.21

$db = new PDO('duckdb::memory:');
$db->exec('INSTALL vss; LOAD vss');

$db->exec('CREATE TABLE table1 (id int primary key, embeddings float[3])');
$db->exec("CREATE INDEX table1_hnsw ON table1 USING HNSW (embeddings) WITH (metric = 'cosine')");

$db->exec('INSERT INTO table1 VALUES (1, [1, 2, 3])');
$db->exec('INSERT INTO table1 VALUES (2, [4, 5, 6])');

$statement = $db->query('
    SELECT id, array_cosine_distance(embeddings, [1.1, 2.1, 3.1]::FLOAT[3]) as distance
    FROM table1 WHERE distance <= 0.01 ORDER BY distance
');
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [id] => 1
#     [distance] => 0.0001407862

$db = new PDO('duckdb::memory:');
$db->exec('CREATE TABLE table1 (id INTEGER, text VARCHAR, amount DECIMAL(10, 2))');
$db->exec("INSERT INTO table1 VALUES (1, 'foo', 42.21)");
$db->exec('CREATE VIEW view1 as SELECT * FROM table1');

$statement = $db->query('SELECT * FROM view1');
print_r($statement->fetchAll(PDO::FETCH_ASSOC));

$list = [
    ['aaa', 'bbb', 'ccc'],
    ['123', '456', '789'],
    ['ddd', 'eee', 'fff'],
];
$fp = fopen('/tmp/test.csv', 'w');
foreach ($list as $fields) {
    fputcsv($fp, $fields, ',', '"', "");
}
fclose($fp);

$db = new PDO('duckdb::memory:');
$db->beginTransaction();
$db->exec("CREATE TABLE test_csv AS SELECT * FROM '/tmp/test.csv'");
$db->exec("INSERT INTO test_csv SELECT * FROM '/tmp/test.csv'");
$db->commit();

$db = new PDO('duckdb::memory:');
$db->exec('CREATE TABLE table1 (v VARCHAR[])');
$db->exec("INSERT INTO table1 VALUES (['a', 'b'])");

$statement = $db->query('SELECT v FROM table1');
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [v] => Array
#         [0] => a
#         [1] => b

$statement = $db->query('SELECT v::json::varchar as v FROM table1');
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [v] => ["a","b"]

$db = new PDO('duckdb::memory:');
$db->exec('CREATE SEQUENCE table1_id');
$db->exec("CREATE TABLE table1 (id INTEGER PRIMARY KEY DEFAULT nextval('table1_id'))");
$statement = $db->query('INSERT INTO table1 VALUES (default) RETURNING *');
print_r($statement->fetch(PDO::FETCH_ASSOC));

# Array
#     [id] => 1

$db = new PDO('duckdb::memory:');
$statement = $db->query("SELECT
    0/0, 1/0, -1/0,
    nullif(0/0, 'NAN'), nullif(1/0, 'INF'), nullif(-1/0, '-INF')");
var_export($statement->fetch(PDO::FETCH_NUM));

# array (
#     0 => NAN, // MySQL,MariaDB: NULL
#     1 => INF, // MySQL,MariaDB: NULL
#     2 => -INF, // MySQL,MariaDB: NULL
#     3 => NULL,
#     4 => NULL,
#     5 => NULL,
# )

$db = new PDO('duckdb::memory:');
$db->exec('INSTALL mysql');
$db->exec("ATTACH 'host=127.0.0.1 port=3306 user=root password=secret database=testdb' AS testdb (TYPE mysql)");
$db->exec("COPY (select * from testdb.orders) TO '/tmp/orders.parquet' (FORMAT parquet)");

$rows = $db->query("SELECT * from '/tmp/orders.parquet'")->fetchAll(PDO::FETCH_ASSOC);
print_r($rows);

# Array
#     [0] => Array
#         [id] => 1
#         [customerId] => 42
#         [amount] => 123.42
#         [origin] => shop
#     [1] => Array
#         [id] => 2
#         [customerId] => 21
#         [amount] => 12.21
#         [origin] => offline

$db = new PDO('duckdb::memory:');
$db->exec('INSTALL postgres');
$db->exec("ATTACH 'host=127.0.0.1 port=5432 user=postgres password=secret' AS testdb (TYPE postgres)");
$db->exec("COPY (select * from testdb.orders) TO '/tmp/orders.parquet' (FORMAT parquet)");

$rows = $db->query("SELECT * from '/tmp/orders.parquet'")->fetchAll(PDO::FETCH_ASSOC);
print_r($rows);

# Array
#     [0] => Array
#         [id] => 1
#         [customerId] => 42
#         [amount] => 123.42
#         [origin] => shop
#     [1] => Array
#         [id] => 2
#         [customerId] => 21
#         [amount] => 12.21
#         [origin] => offline

$db = new PDO('duckdb::memory:');

$url = 'https://bulk.meteostat.net/v2/stations/lite.json.gz';
$rows = $db->query("select id, name.en from read_json('{$url}') WHERE name.en like '%Berlin%' limit 2");
echo json_encode($rows->fetchAll(PDO::FETCH_ASSOC)), PHP_EOL;

$url = 'https://data.meteostat.net/hourly/2026/10381.csv.gz';
$rows = $db->query("select hour, temp from read_csv('{$url}') where year = 2026 and month = 7 and day = 25 and hour > 9 limit 4");
echo json_encode($rows->fetchAll(PDO::FETCH_ASSOC)), PHP_EOL;

# [{"id":"10381","en":"Berlin \/ Dahlem"},{"id":"10382","en":"Berlin \/ Tegel"}]
# [{"hour":10,"temp":24.1},{"hour":11,"temp":25.5},{"hour":12,"temp":26.4},{"hour":13,"temp":27.4}]

# wget https://huggingface.co/datasets/piebro/deutsche-bahn-data/resolve/main/monthly_processed_data/data-2026-07.parquet

$db = new PDO('duckdb::memory:');
$rows = $db->query("
    SELECT train_type, train_number, round(avg(delay_in_min)) as delay_avg, count(*) as count
    FROM 'data-2026-07.parquet' WHERE train_type = 'ICE'
    GROUP BY train_number, train_type
    ORDER BY delay_avg DESC
    LIMIT 10
");
print_r(array_map('json_encode', $rows->fetchAll(PDO::FETCH_ASSOC)));

$rows = $db->query("
    SELECT train_number, station_name, delay_in_min, hour(time) as hour, departure_is_canceled
    FROM 'data-2026-07.parquet'
    WHERE train_number = 647 AND time::date = '2026-07-11'
");
print_r(array_map('json_encode', $rows->fetchAll(PDO::FETCH_ASSOC)));

# Array
#     {"train_type":"ICE","train_number":"647","delay_avg":70,"count":35}
#     {"train_type":"ICE","train_number":"1541","delay_avg":66,"count":198}
#     {"train_type":"ICE","train_number":"79152","delay_avg":44,"count":2}
#     {"train_type":"ICE","train_number":"2587","delay_avg":44,"count":72}
#     {"train_type":"ICE","train_number":"2214","delay_avg":43,"count":159}
#     {"train_type":"ICE","train_number":"953","delay_avg":42,"count":79}
#     {"train_type":"ICE","train_number":"2311","delay_avg":42,"count":289}
#     {"train_type":"ICE","train_number":"859","delay_avg":41,"count":80}
#     {"train_type":"ICE","train_number":"526","delay_avg":41,"count":337}
#     {"train_type":"ICE","train_number":"2512","delay_avg":39,"count":28}
# Array
#     {"train_number":"647","station_name":"Dortmund Hbf","delay_in_min":82,"hour":0,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Hamm (Westf) Hbf","delay_in_min":120,"hour":1,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Bielefeld Hbf","delay_in_min":120,"hour":1,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Minden (Westf)","delay_in_min":123,"hour":2,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Hannover Hbf","delay_in_min":138,"hour":2,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Wolfsburg Hbf","delay_in_min":135,"hour":3,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Berlin Hauptbahnhof","delay_in_min":121,"hour":4,"departure_is_canceled":true}
#     {"train_number":"647","station_name":"Berlin S\u00fcdkreuz","delay_in_min":120,"hour":4,"departure_is_canceled":false}
#     {"train_number":"647","station_name":"Berlin-Spandau","delay_in_min":146,"hour":4,"departure_is_canceled":false}

$url = 'https://httpbin.org/headers';

$db = new PDO('duckdb::memory:');

$db->exec("CREATE SECRET http_auth (TYPE http, SCOPE '{$url}', BEARER_TOKEN 'some secret')");

$rows = $db->query("SELECT * FROM read_json('{$url}')");
print_r($rows->fetchAll(PDO::FETCH_ASSOC));

# Array
#     Array
#         [headers] => Array
#             [Accept] => */*
#             [Authorization] => Bearer some secret
#             [Host] => httpbin.org
#             [User-Agent] => duckdb/v1.5.5(linux_amd64) capi d8cdaa33fd
#             [X-Amzn-Trace-Id] => Root=1-6aab0271-2c027dfa07bc6def14ce4e72

# ./llama-server -hf JetBrains/Mellum2-12B-A2.5B-Thinking-GGUF-Q4_K_M --parallel 1 --ctx-size 16384 --temp 0.6 --top-k 20 --reasoning off

$db = new PDO('duckdb::memory:');
$db->exec('INSTALL open_prompt FROM community');
$db->exec('LOAD open_prompt');
$db->exec("SET VARIABLE openprompt_api_url = 'http://127.0.0.1:8080/v1/chat/completions'");
$db->exec('create table customers (id integer primary key, first_name varchar, last_name varchar, birth_date date)');
$result = $db->query("
    SELECT open_prompt('write duckdb sql, no markdown, find customers older than 30, schema: ' || group_concat(sql))
    FROM duckdb_tables()")->fetch(PDO::FETCH_COLUMN);
echo $result, PHP_EOL;

# SELECT * FROM customers WHERE age(birth_date) > 30;

$db = new PDO('duckdb::memory:', null, null, [PDO::DUCKDB_ATTR_INIT_COMMAND => "SET threads = 2; SET memory_limit = '4GB';"]);

ini_set('swoole.aio_thread_num', 8); // default: max. number of CPU cores
// server workers: $server->set(['worker_num' => 8]);
$start = microtime(true);
Swoole\Coroutine\run(function() {
    $waitGroup = new Swoole\Coroutine\WaitGroup();
    for ($i = 0; $i < 8; $i++) {
        Swoole\Coroutine::create(function () use ($waitGroup) {
            $waitGroup->add();
            $pdo = new PDO('duckdb::memory:');
            // $pdo = new PDO('duckdb:/tmp/test.db', null, null, [PDO::DUCKDB_ATTR_CONFIG => ['access_mode' => 'read_only']]);
            $pdo->exec("select sleep_ms(1000)");
            echo '.';
            $waitGroup->done();
        });
    }
    $waitGroup->wait(10);
});
echo microtime(true) - $start, PHP_EOL; // 1 second
bash
pie install thomas-0816/pdo-duckdb-php
bash
pie install --skip-enable-extension thomas-0816/pdo-duckdb-php
php -d extension=pdo_duckdb some_script.php
bash
sudo curl -s https://pkg.henderkes.com/api/packages/85/debian/repository.key -o /etc/apt/keyrings/static-php85.asc
echo "deb [signed-by=/etc/apt/keyrings/static-php85.asc] https://pkg.henderkes.com/api/packages/85/debian php-zts main" | \
    sudo tee -a /etc/apt/sources.list.d/static-php85.list
sudo apt-get update
sudo apt-get install php-zts-cli php-zts-pdo frankenphp pie-zts
sudo pie-zts install thomas-0816/pdo-duckdb-php

# test
frankenphp php-cli -r 'print_r((new PDO("duckdb::memory:"))->query("SELECT 42 as n")->fetch(PDO::FETCH_ASSOC));'

FROM php:8.5-cli
RUN <<EOF
    apt-get -y update && apt-get -y --no-install-recommends install unzip
    curl -fsSL -o /tmp/pie https://github.com/php/pie/releases/latest/download/pie.phar
    php /tmp/pie install --no-build-tools-check -v thomas-0816/pdo-duckdb-php
    php -r 'print_r((new PDO("duckdb::memory:"))->query("SELECT 42 as n")->fetch(PDO::FETCH_ASSOC));'
EOF
bash
echo "deb https://packages.sury.org/php/ noble main" >/etc/apt/sources.list.d/ondrej-php.list
curl -s https://packages.sury.org/php/apt.gpg >/etc/apt/trusted.gpg.d/php.gpg
sudo apt-get -y update
sudo apt-get -y --no-install-recommends install php8.5-cli php8.5-swoole
curl -fsSL -o /tmp/pie https://github.com/php/pie/releases/latest/download/pie.phar
sudo php /tmp/pie install thomas-0816/pdo-duckdb-php
# test
php -r 'print_r((new PDO("duckdb::memory:"))->query("SELECT 42 as n")->fetch(PDO::FETCH_ASSOC));'
php test_swoole.php