PHP MySQL X DevAPI Collection::existsInDatabase() 方法无法识别带有附加列的集合

Posted

技术标签:

【中文标题】PHP MySQL X DevAPI Collection::existsInDatabase() 方法无法识别带有附加列的集合【英文标题】:PHP MySQL X DevAPI Collection::existsInDatabase() method does not recognise Collections with additional columns 【发布时间】:2020-04-06 12:42:57 【问题描述】:

根据php docs,existsInDatabase() 方法旨在确定数据库中是否存在 Collection 的实例(作为 SchemaObject 又名表)。但是只有当它的表模式完全符合使用Schema::createCollection() 方法创建的集合时,它才会认为集合存在,例如。正好 2 列称为 doc_id

这是不是PHP模块过于严格导致的bug?

这是一个演示(取决于mysql_xdevapi PHP module)...

首先,使用 MySQL 在您的数据库中创建一个名为“people”的集合(取自 MySQL's world_x.sql script in examples for javascript/Python):

CREATE TABLE `countryinfo` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    `_json_schema` json GENERATED ALWAYS AS (_utf8mb4'"type":"object"') VIRTUAL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

其次,创建一个名为“people”的集合,与上面非常相似,但省略了_json_schema 列:

CREATE TABLE `people` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

第三,使用 PHP 的 createCollection() 方法创建一个名为 'animals' 的集合:

$dbSession = mysql_xdevapi\getSession("mysqlx://test:test@localhost");

$schema = $dbSession->getSchema("world_x");    

$collection = $schema->createCollection('animals');

现在,使用 PHP 的 existsInDatabase() 方法检查 3 个集合:

$dbSession = mysql_xdevapi\getSession("mysqlx://test:test@localhost");

$schema = $dbSession->getSchema("world_x");

if ($schema->getCollection('countryinfo')->existsInDatabase()) 
    print "countryinfo collection exist! ";


if ($schema->getCollection('people')->existsInDatabase()) 
    print "People collection exist! ";


if ($schema->getCollection('animals')->existsInDatabase()) 
    print "Animals collection exist! ";

根据 MySQL 文档,所有这 3 个都是有效的集合,但上面的 PHP 代码仅将“人”和“动物”识别为存在。

作为进一步的实验,使用 MySQL 将任何随机命名的列添加到名为“animals”的集合中(通过 PHP 创建),然后重新执行脚本,看看它现在如何不认为它存在。

ALTER TABLE animals ADD COLUMN test tinyint;

现在,如果您使用$schema->getCollections()$schema->getTables(),您将看到animals 被归类为表而不是集合。删除该列,它会再次跳回。在引擎盖下,这是模块决定 Collection 是否“存在”的方式。

我怀疑 PHP 模块可能过于严格。这是一个问题,因为它将您的数据库模式与特定语言紧密耦合。例如,如果我想构建一个 PHP 脚本来与最初通过 JavaScript 填充的现有 MySQL 文档存储进行交互,那么它可能会出现错误,因为可能会在 some/all 中存在额外的 _json_schema 列(或其他一些列)的集合。

我认为拥有doc_id 以外的列是合法的,因为我没有找到相反的规范。我可以想象一个非常真实的用例,在 Collection 上有一个 created_at 日期时间列。

另外,我是否真的发现了不是 PHP 模块的问题,而是 MySQLs 文档的问题? _json_schema 列是早期版本的遗留问题,现在应该删除吗?集合表的定义现在是否已在所有语言中标准化,并且它们必须严格按照指定的那两列来定义?

MySQL 含糊不清,而 PHP 严格,这一切都显得有些松散。

感谢任何帮助。如果一致认为存在错误,我将向维护人员报告错误。

【问题讨论】:

【参考方案1】:

------------------- 编辑 4 月 21 日 ----------- --------------------

看起来 xplugin/server 的行为既没有任何错误也没有变化。不允许向集合添加额外的列。 'countryinfo' 集合中的 _json_schema 列不被视为额外的东西 - 它用于称为“模式验证”的新功能,并且是可选的。所以它可能出现在集合的表中,也可能不出现。因此,给定的表仍然被视为集合,无论它是否包含“_json_schema”。虽然,将其名称从 '_json_schema' 更改为例如就足够了。 'json_schema'(即只删除前导下划线),它将被视为额外列,因此给定的表将不再报告为集合(existsInDatabase()返回false)。

引用的样本:

https://dev.mysql.com/doc/refman/8.0/en/mysql-shell-tutorial-javascript-download.html

适用于 8.0.19,但看起来该版本的服务器已经为“模式验证”做好了准备,而 mysql_xdevapi 将从 v8.0.21 开始正式支持该功能。

在这样的调用之后提到的新版本.21:

$coll = $schema->createCollection("animals");

“动物”表中的列如下所示

mysql> show columns from animals;
+--------------+---------------+------+-----+---------+-------------------+
| Field        | Type          | Null | Key | Default | Extra             |
+--------------+---------------+------+-----+---------+-------------------+
| doc          | json          | YES  |     | NULL    |                   |
| _id          | varbinary(32) | NO   | PRI | NULL    | STORED GENERATED  |
| _json_schema | json          | YES  |     | NULL    | VIRTUAL GENERATED |
+--------------+---------------+------+-----+---------+-------------------+
3 rows in set (0.00 sec)

它会被报告为一个集合。

------------------- 4 月 21 日编辑结束 --------- ----------------------

我们检查了 con/php 的行为,看起来它的性能与 con/nodejs 相似,即

    'countryinfo' 被报告为集合 添加/删除列也会导致 con/nodejs 或 shell 中报告的“动物”类型发生变化(集合与表格)。

请运行以下脚本,让我们知道您的环境中的输出是什么。

// please align below variables according to your environment
$connection_uri = "mysqlx://test:test@localhost";
$db = "testx";

$session = mysql_xdevapi\getSession($connection_uri);
$session->sql("create database $db")->execute();
$schema = $session->getSchema($db);

$session->sql("USE $db")->execute();

$countryinfo_table_stmt="CREATE TABLE `countryinfo` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    `_json_schema` json GENERATED ALWAYS AS (_utf8mb4'\"type\":\"object\"') VIRTUAL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";

echo "$countryinfo_table_stmt\n";
$session->sql($countryinfo_table_stmt)->execute();

$people_table_stmt="CREATE TABLE `people` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";

echo "$people_table_stmt\n";
$session->sql($people_table_stmt)->execute();

if ($schema->getCollection('countryinfo')->existsInDatabase()) 
    print "countryinfo collection exists!\n";
 else 
    print "countryinfo collection DOES NOT exist!\n";


if ($schema->getCollection('people')->existsInDatabase()) 
    print "People collection exists!\n";
 else 
    print "People collection DOES NOT exist!\n";


$coll = $schema->createCollection("animals");

if ($schema->getCollection('animals')->existsInDatabase()) 
    print "Animals collection exists!\n";
 else 
    print "Animals collection DOES NOT exist!\n";


$session->sql("ALTER TABLE $db.animals ADD COLUMN test tinyint")->execute();

if ($schema->getCollection('animals')->existsInDatabase()) 
    print "Animals collection exists!\n";
 else 
    print "Animals collection DOES NOT exist!\n";


$session->sql("ALTER TABLE $db.animals DROP COLUMN test")->execute();

if ($schema->getCollection('animals')->existsInDatabase()) 
    print "Animals collection exists!\n";
 else 
    print "Animals collection DOES NOT exist!\n";


print "done!\n";

在我们的环境中,输出如下所示:

CREATE TABLE `countryinfo` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    `_json_schema` json GENERATED ALWAYS AS (_utf8mb4'"type":"object"') VIRTUAL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
CREATE TABLE `people` (
    `doc` json DEFAULT NULL,
    `_id` varbinary(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,_utf8mb4'$._id'))) STORED NOT NULL,
    PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
countryinfo collection exists!
People collection exists!
Animals collection exists!
Animals collection DOES NOT exist!
Animals collection exists!
done!

此外,您使用的服务器/连接器版本是什么?

【讨论】:

我得到一个不同的输出:“countryinfo 集合不存在!/ People 集合存在!/ Animals 集合存在!/ Animals 集合不存在!/ Animals 集合存在!/完成!”。我正在使用 mysql 版本 8.0.18、mysql_xdevapi 8.0.19、PHP 版本 7.3.16-1+ubuntu18.04.1+deb.sury.org+1。 好的,我确认服务器 v8.0.18 con/php 的行为不同,即返回“countryinfo 集合不存在!”。我假设你机器上的 con/nodejs 或 shell 的唯一区别(我假设 v8.0.18)是它们返回“countryinfo 集合存在!”,但输出的其余部分(对于集合 'People' 和 'Animals')是一样的吗? 我没有尝试过 NodeJS。不管是 Node 还是 PHP,补丁版本是 18 还是 19。我们可以定义 /correct/ 的行为应该是什么吗?是否有关于何时 MySQL 表是“集合”以及何时未定义和记录的规范?因为这种二元区分似乎对跨所有连接器非常重要。 好的,正如你提到的那样,PHP 可能过于严格,所以我想研究一下连接器之间可能存在的这些差异。但这没关系。无论如何,在 PHP 中,我们不执行任何额外的限制性检查并依赖来自服务器的信息,所以我联系了 xplugin/server 人员以进一步进行。 请检查我的答案 - 它在上述消息的开头(我已经编辑过)

以上是关于PHP MySQL X DevAPI Collection::existsInDatabase() 方法无法识别带有附加列的集合的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 MySQL 连接器/Python X DevAPI 连接的选项?

如何在 Mysql X DevAPI 中查询 Null 或 Missing 字段?

您如何为 MySQL X DevAPI 使用 Java 连接池?

使用 X DevAPI 搜索嵌套数组

X DevAPI mysqlx::Session() over linux socket 失败并显示“CDK 错误:意外消息”

使用SpringBoot和X DevApi构建真异步的API