effectiveactivism/sparql-client

提供SparQl客户端。

10.0.1 2024-09-20 12:55 UTC

README

一个面向对象的SparQl 1.1客户端,用于Symfony,支持SELECT、CONSTRUCT、DELETE、INSERT和DELETE+INSERT操作。包括术语、命名空间和(基本)语句验证。

目录

安装

要安装,运行

composer require effectiveactivism/sparql-client

配置

此包需要一个SparQl端点字符串。您可以可选地定义应在每个请求中包含的命名空间。您还可以可选地定义一个SHACL端点。

sparql_client:
  sparql_endpoint: http://test-sparql-endpoint:9999/blazegraph/sparql
  shacl_endpoint: http://test-validator-endpoint/shacl/myshapes/api/validate
  namespaces:
    - schema: http://schema.org/
    - dbo: http://dbpedia.org/ 

使用

选择语句

检索具有schema:headline"Lorem"@la的所有主题。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\TermInterface;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Variable('subject');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $triple = new Triple($subject, $predicate, $object);
        // Create a select statement.
        $selectStatement = $sparQlClient->select([$subject])->where([$triple]);
        // Perform the query.
        $sets = $sparQlClient->execute($selectStatement);
        // The result will contain each 'subject' found.
        /** @var TermInterface[] $set */
        foreach ($sets as $set) {
            dump($set[$subject->getVariableName()]);
        }
    }
}

限制、偏移和排序

选择语句可以使用结果修改器

<?php

use EffectiveActivism\SparQlClient\Syntax\Order\Asc;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\Operator\Binary\Multiply;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\TypedLiteral;

$selectStatement->limit(3);

$selectStatement->offset(2);

$orderVariable = new Variable('orderByThis');
$multiplier = new TypedLiteral(2);
$selectStatement->orderBy([new Asc(new Multiply($orderVariable, $multiplier))]);

询问语句

查询一组子句是否有解决方案

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\Iri;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Iri('urn:uuid:5b08b896-a8ee-11eb-acf0-a3d5edd5c2a6');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $triple = new Triple($subject, $predicate, $object);
        // Create an ask statement.
        $askStatement = $sparQlClient->ask()->where([$triple]);
        // Perform the query.
        $result = $sparQlClient->execute($askStatement);
        // The result will be a boolean value.
        if ($result === true) {
            dump('yes');
        }
    }
}

构造语句

构造一组三元组

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\TermInterface;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Variable('subject');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $triple = new Triple($subject, $predicate, $object);
        // Create a construct statement.
        $constructStatement = $sparQlClient->construct([$triple])->where([$triple]);
        // Perform the query.
        $sets = $sparQlClient->execute($constructStatement);
        // The result will contain each 'subject' found.
        /** @var TermInterface[] $set */
        foreach ($sets as $set) {
            dump($set[$subject->getVariableName()]);
        }
    }
}

插入语句

插入以下三元组

<urn:uuid:a61d21a4-824d-11eb-95c3-ebff6d3fb918> schema:headline "Lorem"@la .
<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\Iri;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Iri('urn:uuid:a61d21a4-824d-11eb-95c3-ebff6d3fb918');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $triple = new Triple($subject, $predicate, $object);
        // Create an insert statement.
        $insertStatement = $sparQlClient->insert([$triple]);
        // Perform the update.
        $sparQlClient->execute($insertStatement);
    }
}

删除语句

删除所有主题具有标题"lorem"@la的三元组,但仅当主题具有类型schema:Article时。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Variable('subject');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $tripleToDelete = new Triple($subject, $predicate, $object);
        // Add a prefixed IRI of the form 'rdf:type'.
        $predicate2 = new PrefixedIri('rdf', 'type');
        // Add a prefixed IRI of the form 'schema:Article'.
        $object2 = new PrefixedIri('schema', 'Article');
        $tripleToFilter = new Triple($subject, $predicate2, $object2);
        // Create a delete statement.
        $deleteStatement = $sparQlClient->delete([$tripleToDelete])->where([$tripleToFilter]);
        // Perform the update.
        $sparQlClient->execute($deleteStatement);
    }
}

替换语句

也称为DELETE+INSERT语句。

将所有具有schema:headline"lorem"@la的主题的标题替换为schema:headline"ipsum"@la

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        // Add the 'schema' namespace.
        $sparQlClient->setExtraNamespaces(['schema' => 'http://schema.org/']);
        // Add a subject as a variable '_subject'.
        $subject = new Variable('subject');
        // Add a prefixed IRI of the form 'schema:headline'.
        $predicate = new PrefixedIri('schema', 'headline');
        // Add a plain literal of the form 'Lorem@la'.
        $object = new PlainLiteral('Lorem', 'la');
        // Add a triple that contains all the above terms.
        $tripleToReplace = new Triple($subject, $predicate, $object);
        // Add a prefixed IRI of the form 'rdf:type'.
        $predicate2 = new PrefixedIri('schema', 'headline');
        // Add a prefixed IRI of the form 'schema:Article'.
        $object2 = new PlainLiteral('ipsum', 'la');
        // Add a replacement triple.
        $replacementTriple = new Triple($subject, $predicate2, $object2);
        // Create a replace statement.
        $replaceStatement = $sparQlClient
            ->replace([$tripleToReplace])
            ->with([$replacementTriple])
            ->where([$tripleToReplace]);
        // Perform the update.
        $sparQlClient->execute($replaceStatement);
    }
}

属性路径和集合

要使用路径运算符,例如逆路径(^),请使用EffectiveActivism\SparQlClient\Syntax\Term\Path类。

逆路径示例

<?php

use \EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use \EffectiveActivism\SparQlClient\Syntax\Term\Path\InversePath; 

$predicate = new PrefixedIri('schema', 'headline');
// Inverse predicate
$inversePredicate = new InversePath($predicate);
// The below will output "^schema:headline"
dump($inversePredicate->serialize());

序列路径示例

<?php

use \EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use \EffectiveActivism\SparQlClient\Syntax\Term\Path\InversePath;
use \EffectiveActivism\SparQlClient\Syntax\Term\Path\SequencePath; 

$predicate = new PrefixedIri('schema', 'headline');
// Inverse predicate
$inversePredicate = new InversePath($predicate);
// Sequence of predicate and inverse predicate.
$sequencePath = new SequencePath($predicate, $inversePredicate);
// The below will output "schema:headline / ^schema:headline"
dump($sequencePath->serialize());

否定集合示例

<?php

use \EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use \EffectiveActivism\SparQlClient\Syntax\Term\Path\InversePath;
use \EffectiveActivism\SparQlClient\Syntax\Term\Set\NegatedPropertySet; 

$predicate = new PrefixedIri('schema', 'headline');
// Inverse predicate
$inversePredicate = new InversePath($predicate);
// Negated set of predicate and inverse predicate.
$negatedSet = new NegatedPropertySet([$predicate, $inversePredicate]);
// The below will output "!(schema:headline | (^schema:headline))"
dump($negatedSet->serialize());

赋值

有两种赋值选项可用:BIND和VALUES。

BIND示例

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Assignment\Bind;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\Operator\Binary\Multiply;use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\TypedLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        $commentCountVariable = new Variable('commentCount');
        $multipliedCommentCountVariable = new Variable('multipliedCommentCount');
        $multiplier = new TypedLiteral(2);
        // Bind an expression such as a multiplication to a variable.
        $bind = new Bind(new Multiply($multiplier, $commentCountVariable), $multipliedCommentCountVariable);
        $selectStatement = $sparQlClient
            ->select([$commentCountVariable])
            ->where([$bind]);
        $sparQlClient->execute($selectStatement);
    }
}

VALUES示例

要在VALUES表达式中使用UNDEF,请使用null。值数组必须与变量数组具有相同的维度。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Assignment\Values;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\TypedLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        $headlineVariable = new Variable('headline');
        $commentCountVariable = new Variable('commentCount');
        $headlineValue1 = new PlainLiteral('Lorem');
        $headlineValue2 = new PlainLiteral('Ipsum');
        $commentCountValue1 = new TypedLiteral(2);
        $values = new Values([
            $headlineVariable,
            $commentCountVariable
        ], [
            // Each sub-array has the same dimension as the variable array
            // above.
            [$headlineValue1, $headlineValue2],
            // Null values are used to signify an undefined value.
            [$commentCountValue1, null]
        ]);
        $selectStatement = $sparQlClient
            ->select([$headlineVariable, $commentCountVariable])
            ->where([$values]);
        $sparQlClient->execute($selectStatement);
    }
}

上述示例将生成以下语句

SELECT ?headline ?commentCount WHERE { VALUES ( ?headline ?commentCount ) { ( "Lorem" "2"^^xsd:integer ) ( "Ipsum" UNDEF ) } . }

验证

此包支持术语验证。例如,以下赋值将引发InvalidArgumentException,因为前缀包含非法字符。

<?php

use \EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;

$subject = new PrefixedIri('‿schema', 'headline');

命名空间在配置中验证,无论是在配置中定义(通过sparql_client.yml)还是在代码中动态添加时。

也支持语句的基本验证。例如,以下语句将引发InvalidArgumentException,因为'where'子句不包含请求的任何变量。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\Iri;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        $variable = new Variable('foo');
        $subject = new Iri('urn:uuid:c40f9982-8322-11eb-b0ba-57776fae8cf3');
        $predicate = new PrefixedIri('schema', 'headline');
        $object = new PlainLiteral('Lorem', 'la');
        $statement = $sparQlClient
            ->select([$variable])
            ->where([new Triple($subject, $predicate, $object)]);
        // Throws an InvalidArgumentException.
        $sparQlClient->execute($statement);
    }
}

可选子句

要添加可选子句,请使用EffectiveActivism\SparQlClient\Syntax\Optionally\Optionally类。

<?php

use \EffectiveActivism\SparQlClient\Syntax\Pattern\Optionally\Optionally;

$optionalClause = new Optionally([$triple, $filter]);
$statement->where([$triple, $optionalClause]);

服务

要使用服务,请使用EffectiveActivism\SparQlClient\Syntax\Pattern\Service\Service类。

第一个参数是服务的IRI,第二个参数是使用该服务的模式数组。

<?php

use \EffectiveActivism\SparQlClient\Syntax\Pattern\Service\Service;
use \EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use \EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use \EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use \EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;

$service = new Service(
    new PrefixedIri('bds', 'search'),
    [
        new Triple(
            new Variable('object'),
            new PrefixedIri('bds', 'search'),
            new PlainLiteral('foo')
        )
    ]
);
$statement->where([
    $service,
    new Triple(
      new Variable('subject'),
      new Variable('predicate'),
      new Variable('object')
    )
]);

约束

要应用约束,例如过滤器,请使用EffectiveActivism\SparQlClient\Syntax\Constraint类。

要使用Filter()类中的运算符,请使用EffectiveActivism\SparQlClient\Syntax\Constraint\Operator类。

过滤示例

以下示例展示了如何选择所有具有schema:headline值为lorem的主题,但不包括任何具有schema:identifier值为13a5b1da-9060-11eb-a695-2bfde2d1d6bd的主题。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\FilterNotExists;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Term\Iri\PrefixedIri;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;
use EffectiveActivism\SparQlClient\Syntax\Term\Variable\Variable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(SparQlClientInterface $sparQlClient)
    {
        $variable = new Variable('foo');
        $predicate = new PrefixedIri('schema', 'headline');
        $object = new PlainLiteral('Lorem');
        $filterPredicate = new PrefixedIri('schema', 'identifier');
        $filterObject = new PlainLiteral('13a5b1da-9060-11eb-a695-2bfde2d1d6bd');
        $statement = $sparQlClient
            ->select([$variable])
            ->where([
                new Triple($variable, $predicate, $object),
                new FilterNotExists([
                    new Triple($variable, $filterPredicate, $filterObject)
                ]),
            ]);
        $result = $sparQlClient->execute($statement);
    }
}

此示例展示了如何使用过滤运算符。类型强制确保某些参数验证。

<?php

use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\Filter;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\Operator\Binary\Equal;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Constraint\Operator\Binary\NotEqual;
use EffectiveActivism\SparQlClient\Syntax\Term\Literal\PlainLiteral;

$object1 = new PlainLiteral('Lorem');
$object2 = new PlainLiteral('Ipsum');
$object3 = new PlainLiteral(12);

// Returns a filter of the form FILTER('Lorem' = 'Ipsum')
new Filter(new Equal($object1, $object2));

// Throws an InvalidArgumentException because the argument data types do not match.
new Filter(new NotEqual($object1, $object3));

上传文件

要上传包含词汇的文件,请使用upload方法。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;

class MyController extends AbstractController
{
    public function view(Request $request, SparQlClientInterface $sparQlClient)
    {
        /** @var UploadedFile $file */
        $file = $request->files->get('file');
        if ($sparQlClient->upload($file, 'application/rdf+xml')) {
            dump('File uploaded!');
        }
    }
}

您必须将文件的MIME类型指定为第二个参数。默认的内容类型假定为application/x-turtle

SHACL验证器

要使用验证器服务,请定义SHACL验证器端点。您可以以服务的形式获取SHACL客户端。可以验证插入、删除、替换和构造语句。

<?php

namespace App\Controller;

use EffectiveActivism\SparQlClient\Client\ShaclClientInterface;
use EffectiveActivism\SparQlClient\Client\SparQlClientInterface;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\Triple;
use EffectiveActivism\SparQlClient\Syntax\Pattern\Triple\TripleInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    public function view(ShaclClientInterface $shaclClient, SparQlClientInterface $sparQlClient)
    {
        /** @var TripleInterface $triple */
        $triple = new Triple(...);
        $statement = $sparQlClient
            ->insert([$triple])
            ->where([$triple]);
        if ($shaclClient->validate($statement)) {
            dump('statement is valid!');
        }
    }
}

示例docker-compose配置

以下Docker服务展示了工作配置示例。此客户端尚未与其他三元组存储或验证器进行测试。

Blazegraph Docker镜像无需设置。要设置isaitb SHACL验证器,请访问此处

version: '3.3'

services:
  
  ###############
  # Triplestore #
  ###############

  triplestore:
    restart: unless-stopped
    image: nawer/blazegraph:2.1.5
    volumes:
      - ./triplestore/data:/var/lib/blazegraph
    ports:
      - 9999:9999

  #############
  # Validator #
  #############

  validator:
    restart: unless-stopped
    image: isaitb/shacl-validator:1.0.0
    environment:
      - validator.resourceRoot:/validator/resources/
    volumes:
      - ./validator/resources:/validator/resources
    ports:
      - 8080:8080

计划中的功能

  • 支持图,包括命名图和管理操作。
  • 支持DESCRIBE语句。
  • 支持UNION。
  • 支持空前缀。
  • 使用其数据类型验证类型化字面量。
  • 改进来自三元组存储的错误报告。
  • 可能在INSERT和DELETE语句执行中返回更有意义的数据。
  • 扩展语句验证。