Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c714ed6

Browse files
committed
initial commit
0 parents commit c714ed6

File tree

6 files changed

+297
-0
lines changed

6 files changed

+297
-0
lines changed

‎Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM php:7.4-cli-buster
2+
3+
RUN apt-get -y update \
4+
&& DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install libffi-dev \
5+
&& docker-php-ext-configure ffi --with-ffi \
6+
&& docker-php-ext-install ffi \
7+
&& apt-get clean \
8+
&& rm -rf /tmp/* /var/lib/apt/lists/* /var/cache/apt/archives/*

‎DuckDB.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
class DuckDB
4+
{
5+
private FFI $ffi;
6+
private FFI\CData $db;
7+
private FFI\CData $conn;
8+
9+
public function __construct(?string $databasePath, string $libraryPath, string $headersPath)
10+
{
11+
$this->ffi = FFI::cdef(file_get_contents($headersPath), $libraryPath);
12+
13+
$this->db = $this->ffi->new('duckdb_database');
14+
$this->conn = $this->ffi->new('duckdb_connection');
15+
16+
$error = $this->ffi->duckdb_open($databasePath, FFI::addr($this->db));
17+
if ($error) {
18+
throw new Exception('error open: ' . $databasePath);
19+
}
20+
21+
$error = $this->ffi->duckdb_connect($this->db, FFI::addr($this->conn));
22+
if ($error) {
23+
throw new Exception('error connect');
24+
}
25+
}
26+
27+
public function __destruct()
28+
{
29+
$this->ffi->duckdb_disconnect(FFI::addr($this->conn));
30+
$this->ffi->duckdb_close(FFI::addr($this->db));
31+
unset($this->ffi);
32+
}
33+
34+
public function query(string $query): array
35+
{
36+
$result = $this->ffi->new('duckdb_result');
37+
38+
$error = $this->ffi->duckdb_query($this->conn, $query, FFI::addr($result));
39+
if ($error) {
40+
$message = FFI::string($result->error_message);
41+
42+
$this->ffi->duckdb_destroy_result(FFI::addr($result));
43+
44+
throw new Exception($message);
45+
}
46+
47+
$data = [];
48+
$columns = [];
49+
50+
for ($col = 0; $col < $result->column_count; $col++) {
51+
$columns[] = FFI::string($result->columns[$col]->name);
52+
53+
for ($row = 0; $row < $result->row_count; $row++) {
54+
$value = $this->ffi->duckdb_value_varchar(FFI::addr($result), $col, $row);
55+
56+
$data[$row][$columns[$col]] = FFI::string($value);
57+
58+
FFI::free($value);
59+
}
60+
}
61+
62+
$this->ffi->duckdb_destroy_result(FFI::addr($result));
63+
64+
return $data;
65+
}
66+
}

‎duckdb.h

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// DuckDB
4+
//
5+
// duckdb.h
6+
//
7+
// Author: Mark Raasveldt
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
#pragma once
12+
13+
#include <stdbool.h>
14+
#include <stdint.h>
15+
#include <stdlib.h>
16+
17+
#ifdef __cplusplus
18+
##extern "C" {
19+
#endif
20+
21+
typedef uint64_t idx_t;
22+
23+
typedef enum DUCKDB_TYPE {
24+
DUCKDB_TYPE_INVALID = 0,
25+
// bool
26+
DUCKDB_TYPE_BOOLEAN,
27+
// int8_t
28+
DUCKDB_TYPE_TINYINT,
29+
// int16_t
30+
DUCKDB_TYPE_SMALLINT,
31+
// int32_t
32+
DUCKDB_TYPE_INTEGER,
33+
// int64_t
34+
DUCKDB_TYPE_BIGINT,
35+
// float
36+
DUCKDB_TYPE_FLOAT,
37+
// double
38+
DUCKDB_TYPE_DOUBLE,
39+
// duckdb_timestamp
40+
DUCKDB_TYPE_TIMESTAMP,
41+
// duckdb_date
42+
DUCKDB_TYPE_DATE,
43+
// duckdb_time
44+
DUCKDB_TYPE_TIME,
45+
// duckdb_interval
46+
DUCKDB_TYPE_INTERVAL,
47+
// duckdb_hugeint
48+
DUCKDB_TYPE_HUGEINT,
49+
// const char*
50+
DUCKDB_TYPE_VARCHAR
51+
} duckdb_type;
52+
53+
typedef struct {
54+
int32_t year;
55+
int8_t month;
56+
int8_t day;
57+
} duckdb_date;
58+
59+
typedef struct {
60+
int8_t hour;
61+
int8_t min;
62+
int8_t sec;
63+
int16_t msec;
64+
} duckdb_time;
65+
66+
typedef struct {
67+
duckdb_date date;
68+
duckdb_time time;
69+
} duckdb_timestamp;
70+
71+
typedef struct {
72+
int32_t months;
73+
int32_t days;
74+
int64_t msecs;
75+
} duckdb_interval;
76+
77+
typedef struct {
78+
uint64_t lower;
79+
int64_t upper;
80+
} duckdb_hugeint;
81+
82+
typedef struct {
83+
void *data;
84+
bool *nullmask;
85+
duckdb_type type;
86+
char *name;
87+
} duckdb_column;
88+
89+
typedef struct {
90+
idx_t column_count;
91+
idx_t row_count;
92+
duckdb_column *columns;
93+
char *error_message;
94+
} duckdb_result;
95+
96+
// typedef struct {
97+
// void *data;
98+
// bool *nullmask;
99+
// } duckdb_column_data;
100+
101+
// typedef struct {
102+
// int column_count;
103+
// int count;
104+
// duckdb_column_data *columns;
105+
// } duckdb_chunk;
106+
107+
typedef void *duckdb_database;
108+
typedef void *duckdb_connection;
109+
typedef void *duckdb_prepared_statement;
110+
111+
typedef enum { DuckDBSuccess = 0, DuckDBError = 1 } duckdb_state;
112+
113+
//! Opens a database file at the given path (nullptr for in-memory). Returns DuckDBSuccess on success, or DuckDBError on
114+
//! failure. [OUT: database]
115+
duckdb_state duckdb_open(const char *path, duckdb_database *out_database);
116+
//! Closes the database.
117+
void duckdb_close(duckdb_database *database);
118+
119+
//! Creates a connection to the specified database. [OUT: connection]
120+
duckdb_state duckdb_connect(duckdb_database database, duckdb_connection *out_connection);
121+
//! Closes the specified connection handle
122+
void duckdb_disconnect(duckdb_connection *connection);
123+
124+
//! Executes the specified SQL query in the specified connection handle. [OUT: result descriptor]
125+
duckdb_state duckdb_query(duckdb_connection connection, const char *query, duckdb_result *out_result);
126+
//! Destroys the specified result
127+
void duckdb_destroy_result(duckdb_result *result);
128+
129+
//! Returns the column name of the specified column. The result does not need to be freed;
130+
//! the column names will automatically be destroyed when the result is destroyed.
131+
const char *duckdb_column_name(duckdb_result *result, idx_t col);
132+
133+
// SAFE fetch functions
134+
// These functions will perform conversions if necessary. On failure (e.g. if conversion cannot be performed) a special
135+
// value is returned.
136+
137+
//! Converts the specified value to a bool. Returns false on failure or NULL.
138+
bool duckdb_value_boolean(duckdb_result *result, idx_t col, idx_t row);
139+
//! Converts the specified value to an int8_t. Returns 0 on failure or NULL.
140+
int8_t duckdb_value_int8(duckdb_result *result, idx_t col, idx_t row);
141+
//! Converts the specified value to an int16_t. Returns 0 on failure or NULL.
142+
int16_t duckdb_value_int16(duckdb_result *result, idx_t col, idx_t row);
143+
//! Converts the specified value to an int64_t. Returns 0 on failure or NULL.
144+
int32_t duckdb_value_int32(duckdb_result *result, idx_t col, idx_t row);
145+
//! Converts the specified value to an int64_t. Returns 0 on failure or NULL.
146+
int64_t duckdb_value_int64(duckdb_result *result, idx_t col, idx_t row);
147+
//! Converts the specified value to a float. Returns 0.0 on failure or NULL.
148+
float duckdb_value_float(duckdb_result *result, idx_t col, idx_t row);
149+
//! Converts the specified value to a double. Returns 0.0 on failure or NULL.
150+
double duckdb_value_double(duckdb_result *result, idx_t col, idx_t row);
151+
//! Converts the specified value to a string. Returns nullptr on failure or NULL. The result must be freed with free.
152+
char *duckdb_value_varchar(duckdb_result *result, idx_t col, idx_t row);
153+
154+
// Prepared Statements
155+
156+
//! prepares the specified SQL query in the specified connection handle. [OUT: prepared statement descriptor]
157+
duckdb_state duckdb_prepare(duckdb_connection connection, const char *query,
158+
duckdb_prepared_statement *out_prepared_statement);
159+
160+
duckdb_state duckdb_nparams(duckdb_prepared_statement prepared_statement, idx_t *nparams_out);
161+
162+
//! binds parameters to prepared statement
163+
duckdb_state duckdb_bind_boolean(duckdb_prepared_statement prepared_statement, idx_t param_idx, bool val);
164+
duckdb_state duckdb_bind_int8(duckdb_prepared_statement prepared_statement, idx_t param_idx, int8_t val);
165+
duckdb_state duckdb_bind_int16(duckdb_prepared_statement prepared_statement, idx_t param_idx, int16_t val);
166+
duckdb_state duckdb_bind_int32(duckdb_prepared_statement prepared_statement, idx_t param_idx, int32_t val);
167+
duckdb_state duckdb_bind_int64(duckdb_prepared_statement prepared_statement, idx_t param_idx, int64_t val);
168+
duckdb_state duckdb_bind_float(duckdb_prepared_statement prepared_statement, idx_t param_idx, float val);
169+
duckdb_state duckdb_bind_double(duckdb_prepared_statement prepared_statement, idx_t param_idx, double val);
170+
duckdb_state duckdb_bind_varchar(duckdb_prepared_statement prepared_statement, idx_t param_idx, const char *val);
171+
duckdb_state duckdb_bind_null(duckdb_prepared_statement prepared_statement, idx_t param_idx);
172+
173+
//! Executes the prepared statements with currently bound parameters
174+
duckdb_state duckdb_execute_prepared(duckdb_prepared_statement prepared_statement, duckdb_result *out_result);
175+
176+
//! Destroys the specified prepared statement descriptor
177+
void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement);
178+
179+
#ifdef __cplusplus
180+
##}
181+
#endif

‎libduckdb.so

11.1 MB
Binary file not shown.

‎readme.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
PHP example to integrate DuckDB using PHP-FFI
2+
-----------------------------------------------
3+
4+
Currently there is no PHP extension available for using DuckDB, so I created a small library using PHP-FFI.
5+
6+
DuckDB is an embeddable SQL OLAP database management system.
7+
It does not require external servers. Databases are stored in single files (similar to SQLite).
8+
Compared to SQLite, DuckDB is much faster. E.g. I imported 16M rows from a CSV file in 5s on my notebook (i5-8250U).
9+
10+
DuckDB can import CSV files with automatic format detection and automatic table creation using:
11+
12+
CREATE TABLE test1 AS SELECT * FROM read_csv_auto('test1.csv');
13+
CREATE TABLE test2 AS SELECT * FROM read_csv_auto('test2.csv.gz');
14+
15+
Usage:
16+
17+
docker build -t php-ffi .
18+
docker run -it --rm -v $(pwd):/code php-ffi php /code/test.php
19+
20+
Requirements:
21+
22+
PHP 7.4+ with FFI extension enabled
23+
24+
References:
25+
26+
- https://duckdb.org
27+
- https://github.com/cwida/duckdb
28+
- https://www.php.net/manual/en/book.ffi.php

‎test.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
error_reporting(E_ALL);
4+
5+
require 'DuckDB.php';
6+
7+
$db = new DuckDB('/tmp/duck1.db', __DIR__ . '/libduckdb.so', __DIR__ . '/duckdb.h');
8+
9+
$db->query('CREATE TABLE IF NOT EXISTS test_table (i INTEGER, j INTEGER, k VARCHAR)');
10+
11+
$db->query("INSERT INTO test_table VALUES (3, 4, 'FOO'), (5, 6, 'BAR'), (7, NULL, 'BAZ')");
12+
13+
$result = $db->query('SELECT * FROM test_table');
14+
print_r($result);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /