You are here

protected function Database::createFieldTable in Search API 8

Creates or modifies a table to add an indexed field.

Used as a helper method in fieldsUpdated().

Parameters

\Drupal\search_api\Item\FieldInterface|null $field: The field to add. Or NULL if only the initial table with an "item_id" column should be created.

array $db: Associative array containing the following:

  • table: The table to use for the field.
  • column: (optional) The column to use in that table. Defaults to "value". For creating a separate field table, it must be left empty!

string $type: (optional) The type of table being created. Either "index" (for the denormalized table for an entire index) or "field" (for field-specific tables).

Throws

\Drupal\search_api\SearchApiException Thrown if creating the table failed.

2 calls to Database::createFieldTable()
Database::addIndex in modules/search_api_db/src/Plugin/search_api/backend/Database.php
Adds a new index to this server.
Database::fieldsUpdated in modules/search_api_db/src/Plugin/search_api/backend/Database.php
Updates the storage tables when the field configuration changes.

File

modules/search_api_db/src/Plugin/search_api/backend/Database.php, line 786

Class

Database
Indexes and searches items using the database.

Namespace

Drupal\search_api_db\Plugin\search_api\backend

Code

protected function createFieldTable(FieldInterface $field = NULL, array $db = [], $type = 'field') {

  // @todo Make $field required but nullable (and $db required again) once we
  //   depend on PHP 7.1+.
  $new_table = !$this->database
    ->schema()
    ->tableExists($db['table']);
  if ($new_table) {
    $table = [
      'name' => $db['table'],
      'module' => 'search_api_db',
      'fields' => [
        'item_id' => [
          'type' => 'varchar',
          'length' => 150,
          'description' => 'The primary identifier of the item',
          'not null' => TRUE,
        ],
      ],
    ];

    // For the denormalized index table, add a primary key right away. For
    // newly created field tables we first need to add the "value" column.
    if ($type === 'index') {
      $table['primary key'] = [
        'item_id',
      ];
    }
    $this->database
      ->schema()
      ->createTable($db['table'], $table);
    $this->dbmsCompatibility
      ->alterNewTable($db['table'], $type);
  }

  // Stop here if we want to create a table with just the 'item_id' column.
  if (!isset($field)) {
    return;
  }
  $column = $db['column'] ?? 'value';
  $db_field = $this
    ->sqlType($field
    ->getType());
  $db_field += [
    'description' => "The field's value for this item",
  ];
  if ($new_table || $type === 'field') {
    $db_field['not null'] = TRUE;
  }
  $this->database
    ->schema()
    ->addField($db['table'], $column, $db_field);
  if ($db_field['type'] === 'varchar') {
    $index_spec = [
      [
        $column,
        10,
      ],
    ];
  }
  else {
    $index_spec = [
      $column,
    ];
  }

  // Create a table specification skeleton to pass to addIndex().
  $table_spec = [
    'fields' => [
      $column => $db_field,
    ],
    'indexes' => [
      $column => $index_spec,
    ],
  ];

  // This is a quick fix for a core bug, so we can run the tests with SQLite
  // until this is fixed.
  //
  // In SQLite, indexes and tables can't have the same name, which is
  // the case for Search API DB. We have following situation:
  // - a table named search_api_db_default_index_title
  // - a table named search_api_db_default_index
  //
  // The last table has an index on the title column, which results in an
  // index with the same as the first table, which conflicts in SQLite.
  //
  // The core issue addressing this (https://www.drupal.org/node/1008128) was
  // closed as it fixed the PostgreSQL part. The SQLite fix is added in
  // https://www.drupal.org/node/2625664
  // We prevent this by adding an extra underscore (which is also the proposed
  // solution in the original core issue).
  //
  // @todo: Remove when #2625664 lands in Core. See #2625722 for a patch that
  // implements this.
  try {
    $this->database
      ->schema()
      ->addIndex($db['table'], '_' . $column, $index_spec, $table_spec);
  } catch (\PDOException $e) {
    $variables['%column'] = $column;
    $variables['%table'] = $db['table'];
    $this
      ->logException($e, '%type while trying to add a database index for column %column to table %table: @message in %function (line %line of %file).', $variables, RfcLogLevel::WARNING);
  } catch (DatabaseException $e) {
    $variables['%column'] = $column;
    $variables['%table'] = $db['table'];
    $this
      ->logException($e, '%type while trying to add a database index for column %column to table %table: @message in %function (line %line of %file).', $variables, RfcLogLevel::WARNING);
  }

  // Add a covering index for field tables.
  if ($new_table && $type == 'field') {
    $this->database
      ->schema()
      ->addPrimaryKey($db['table'], [
      'item_id',
      $column,
    ]);
  }
}