Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions client.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

try {
$pdo = new PDO("mysql:host=127.0.0.1;dbname=test", "root", "");
$pdo = new PDO("mysql:host=127.0.0.1;port=3316;dbname=test", "root", "test");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
die("Connection failed: " . $e->getMessage());
Expand All @@ -14,14 +14,16 @@
decimal_column DECIMAL(10,2) NOT NULL DEFAULT 0,
float_column FLOAT(10,2) NOT NULL DEFAULT 0,
enum_column ENUM('a', 'b', 'c') NOT NULL DEFAULT 'a',
date_column DATE NOT NULL DEFAULT CURRENT_DATE,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't support default current_date?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamziel Actually, MySQL doesn't. Only in version 8 with expressions (like DEFAULT (CURRENT_DATE()), but we don't support those in DEFAULT yet.

PRIMARY KEY (ID),
date_column DATE NOT NULL,
PRIMARY KEY (ID)
)
");
$result = $pdo->exec("INSERT INTO wptests_users (decimal_column, float_column, enum_column, date_column) VALUES (123.45, 678.90, 'b', '2024-02-14')");
");

$result = $pdo->exec("INSERT INTO wptests_users (decimal_column, float_column, enum_column, date_column) VALUES (123.45, 678.90, 'a', '2024-02-14')");
$result = $pdo->exec("INSERT INTO wptests_users (decimal_column, float_column, enum_column, date_column) VALUES (987, 321, 'b', '2024-02-14')");

$stmt = $pdo->prepare("SELECT * FROM wptests_users WHERE ID > :id");
$stmt->execute(['id' => 0]);
$stmt->execute(['id' => 2]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);

var_dump($row);
Expand Down
138 changes: 76 additions & 62 deletions php-implementation/handler-sqlite-translation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,100 @@

define('WP_DEBUG', false);

require_once __DIR__ . '/wpdb-polyfill.php';

// A polyfill – function is called by the wpdb class.
function apply_filters($tag, $value) {
return $value;
}

require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-lexer.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-query-rewriter.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-translator.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-token.php';
require_once __DIR__ . '/sqlite-database-integration/version.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/parser/class-wp-parser-grammar.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/parser/class-wp-parser.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/parser/class-wp-parser-node.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/parser/class-wp-parser-token.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/mysql/class-wp-mysql-token.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/mysql/class-wp-mysql-lexer.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/mysql/class-wp-mysql-parser.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-connection.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-configurator.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-driver.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-driver-exception.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-exception.php';
require_once __DIR__ . '/sqlite-database-integration/wp-includes/sqlite-ast/class-wp-sqlite-information-schema-reconstructor.php';


class SQLiteTranslationHandler implements MySQLQueryHandler {
private $wpdb;
/** @var WP_SQLite_Driver */
private $sqlite_driver;

public function __construct($sqlite_database_path) {
define('FQDB', $sqlite_database_path);
define('FQDBDIR', dirname(FQDB) . '/');
$this->wpdb = new WP_SQLite_DB();

$this->sqlite_driver = new WP_SQLite_Driver(
new WP_SQLite_Connection( array( 'path' => $sqlite_database_path ) ),
'wordpress'
);
}

public function handleQuery(string $query): MySQLServerQueryResult {
// An extremely naive check. We should be using the MySQL parser to
// determine this:
if(!str_starts_with(strtolower($query), 'select')) {
$this->wpdb->query($query);
try {
$rows = $this->sqlite_driver->query($query);
if ( $this->sqlite_driver->get_last_column_count() > 0 ) {
$columns = $this->computeColumnInfo();
return new SelectQueryResult($columns, $rows);
}
return new OkayPacketResult(
$this->wpdb->rows_affected ?? 0,
$this->wpdb->insert_id ?? 0
$this->sqlite_driver->get_last_return_value() ?? 0,
$this->sqlite_driver->get_insert_id() ?? 0
);
} catch (Throwable $e) {
return new ErrorQueryResult($e->getMessage());
}
$rows = $this->wpdb->get_results($query, ARRAY_A);
$columns = $this->computeColumnInfo($rows);
return new SelectQueryResult($columns, $rows);
}

public function computeColumnInfo($rows) {
if (empty($rows)) {
return [];
}

public function computeColumnInfo() {
$columns = [];
$firstRow = $rows[0];

foreach ($firstRow as $key => $value) {
$columnType = 8; // Default to LONGLONG
$columnLength = 1;
$decimals = 0;

// Analyze all rows to find the maximum length and most specific type
foreach ($rows as $row) {
$currentValue = $row[$key];

if (is_string($currentValue)) {
$columnType = 253; // VARCHAR
$columnLength = max($columnLength, strlen($currentValue));
} elseif (is_numeric($currentValue)) {
if (is_int($currentValue) || $currentValue == (int)$currentValue) {
if ($columnType != 253) { // Don't override VARCHAR
$columnType = 3; // LONG
$columnLength = 11;
}
} else {
if ($columnType != 253) { // Don't override VARCHAR
$columnType = 246; // DECIMAL
$columnLength = 10;
$decimals = 2;
}
}
}

$column_meta = $this->sqlite_driver->get_last_column_meta();

$types = [
'DECIMAL' => MySQLProtocol::FIELD_TYPE_DECIMAL,
'TINY' => MySQLProtocol::FIELD_TYPE_TINY,
'SHORT' => MySQLProtocol::FIELD_TYPE_SHORT,
'LONG' => MySQLProtocol::FIELD_TYPE_LONG,
'FLOAT' => MySQLProtocol::FIELD_TYPE_FLOAT,
'DOUBLE' => MySQLProtocol::FIELD_TYPE_DOUBLE,
'NULL' => MySQLProtocol::FIELD_TYPE_NULL,
'TIMESTAMP' => MySQLProtocol::FIELD_TYPE_TIMESTAMP,
'LONGLONG' => MySQLProtocol::FIELD_TYPE_LONGLONG,
'INT24' => MySQLProtocol::FIELD_TYPE_INT24,
'DATE' => MySQLProtocol::FIELD_TYPE_DATE,
'TIME' => MySQLProtocol::FIELD_TYPE_TIME,
'DATETIME' => MySQLProtocol::FIELD_TYPE_DATETIME,
'YEAR' => MySQLProtocol::FIELD_TYPE_YEAR,
'NEWDATE' => MySQLProtocol::FIELD_TYPE_NEWDATE,
'VARCHAR' => MySQLProtocol::FIELD_TYPE_VARCHAR,
'BIT' => MySQLProtocol::FIELD_TYPE_BIT,
'NEWDECIMAL' => MySQLProtocol::FIELD_TYPE_NEWDECIMAL,
'ENUM' => MySQLProtocol::FIELD_TYPE_ENUM,
'SET' => MySQLProtocol::FIELD_TYPE_SET,
'TINY_BLOB' => MySQLProtocol::FIELD_TYPE_TINY_BLOB,
'MEDIUM_BLOB' => MySQLProtocol::FIELD_TYPE_MEDIUM_BLOB,
'LONG_BLOB' => MySQLProtocol::FIELD_TYPE_LONG_BLOB,
'BLOB' => MySQLProtocol::FIELD_TYPE_BLOB,
'VAR_STRING' => MySQLProtocol::FIELD_TYPE_VAR_STRING,
'STRING' => MySQLProtocol::FIELD_TYPE_STRING,
'GEOMETRY' => MySQLProtocol::FIELD_TYPE_GEOMETRY,
];

foreach ($column_meta as $column) {
$type = $types[$column['native_type']] ?? null;
if ( null === $type ) {
throw new Exception('Unknown column type: ' . $column['native_type']);
}

$columns[] = [
'name' => $key,
'length' => $columnLength ?? 1,
'type' => $columnType,
'flags' => 129,
'decimals' => $decimals
'name' => $column['name'],
'length' => $column['len'],
'type' => $type,
'flags' => 129,
'decimals' => $column['precision']
];
}
return $columns;
Expand Down
Loading