Problem/Motivation

\Drupal\views\Plugin\views\HandlerBase contains this gem:

  public function ensureMyTable() {
    if (!isset($this->tableAlias)) {
      $this->tableAlias = $this->query->ensureTable($this->table, $this->relationship);
    }
    return $this->tableAlias;
  }

The $this->query property is documented as an instance of \Drupal\views\Plugin\views\query\QueryPluginBase, which does not have an ensureTable() method, however. The only real subclass of QueryPluginBase is the Sql implementation which has the ensureTable() method. The QueryTest test implementation has an empty ensureTable() method, probably because it is required due to this bug.

The Search API module provides an alternate views query plugin, which does not provide an ensureTable() method leading to fatal errors.

Proposed resolution

?

Remaining tasks

User interface changes

API changes

CommentFileSizeAuthor
#41 2484565-41.patch688 bytesdaffie

Comments

tstoeckler’s picture

Issue summary: View changes

So a possible resolution would be:
Provide an empty ensureTable() in QueryPluginBase. (Sadly, there's no QueryInterface so this would be the poor man's equivalent of putting the method on the interface.)

I'm not knowledgable enough about views and views query plugins in particular to judge whether that makes any sense, though.

dawehner’s picture

Mh, so yeah views sucks, especially in that regard.

The handlers are currently all bound to SQL, which is kind of okay. What about introducing an SqlAwareQueryInterface implemented by Sql and check this here?

tstoeckler’s picture

Yes, that sounds sensible to me. I guess the interface should be called SqlQueryInterface directly, though, right? Because the query is not just aware of SQL, it *is* an SQL query. Or am I mixing things up?

tstoeckler’s picture

\Drupal\views\Plugin\views\field\FieldPluginBase::query() has the same problem:

  public function query() {
    $this->ensureMyTable();
    // Add the field.
    $params = $this->options['group_type'] != 'group' ? array('function' => $this->options['group_type']) : array();
    $this->field_alias = $this->query->addField($this->tableAlias, $this->realField, NULL, $params);

    $this->addAdditionalFields();
  }

QueryPluginBase does not have an addField() method. This one is pretty fun in combination with Search API because the SQL implementation returns a string and the Search API one returns a Search API query object.

Tempted to mark this major and rename it "Views does not work with non-SQL query backends"...

googletorp’s picture

Priority: Normal » Major

I'm upping this to major:

A PHP error which is only triggered under rare circumstances or which affects only a small percentage of all users.

Is there any workaround for this? Could Search API implement these methods itself?

dawehner’s picture

Is there any workaround for this? Could Search API implement these methods itself?

In order to work searchapi has to implement all those handlers anyway, if I understand it correctly. Handlers are bound to their underlying query implementation, I don't see
how they could not.

tstoeckler’s picture

Right the problem is not the individual handlers but the base classes, which the Search API handlers extend for all sorts of utility stuff.

Working on this now.

tstoeckler’s picture

Title: HandlerBase calls ensureTable() on $this->query which does not exist generally » Views is broken with non-Sql query plugins
Issue tags: +Contributed project blocker

So I can find the following public methods on Sql which are not on QueryPluginBase. So all of these need to be either:
1. Moved onto the newly created SqlQueryInterface
2. Moved to QueryPluginBase (a.k.a. poor man's QueryInterface...)

  public function setCountField($table, $field, $alias = NULL);
  public function addRelationship($alias, JoinPluginBase $join, $base, $link_point = NULL);
  public function addTable($table, $relationship = NULL, JoinPluginBase $join = NULL, $alias = NULL);
  public function queueTable($table, $relationship = NULL, JoinPluginBase $join = NULL, $alias = NULL);
  public function ensureTable($table, $relationship = NULL, JoinPluginBase $join = NULL);
  public function getJoinData($table, $base_table);
  public function getTableInfo($table);
  public function addField($table, $field, $alias = '', $params = array());
  public function clearFields();
  public function addWhere($group, $field, $value = NULL, $operator = NULL);
  public function addWhereExpression($group, $snippet, $args = array());
  public function addHavingExpression($group, $snippet, $args = array());
  public function addOrderBy($table, $field = NULL, $order = 'ASC', $alias = '', $params = array());
  public function addGroupBy($clause);
  public function addTag($tag);
  function placeholder($base = 'views');
  public function getWhereArgs();
  public function aggregationMethodSimple($group_type, $field);
  public function aggregationMethodDistinct($group_type, $field);
dawehner’s picture

I doubt that is a contrib blocker, the entire lifetime of d6/d7 was able to deal with it

drunken monkey’s picture

I doubt that is a contrib blocker, the entire lifetime of d6/d7 was able to deal with it

I also don't think it's a blocker per se. However, with field handlers using these methods that are only on that specific query plugin, it becomes unnecessarily harder (and much more verbose, since you manually need to override these instances) to provide Views integration with a different query plugin (e.g., Search API). If the field handlers could be changed to only use the methods on the interface, this would greatly simplify things for new query plugins, because they have proper documentation on the methods they need to provide in order for the query plugin to work.

In D7 Search API we were "able to deal with it", yes, but it required an awkward workaround with boilerplate code like this for working around all this hard-wiring to SQL backends.

Also, since there was no proper separation between the SQL query plugin and the general definition of "that's how a query plugin should look" (i.e., a missing interface), contrib modules were always using the SQL-specific methods when dealing with Views queries leading to numerous instances where things would break horribly when trying to use such contrib modules' functionality with Search API views.
Having a proper QueryPluginInterface would be the best solution here, of course, but in lieu of that having code in the field handlers that shows that not all query plugins are SQL-based, and how to deal with that fact (SqlQueryInterface might really be a good option), would surely help a lot by providing a good example to contrib modules.

dawehner’s picture

Having a generic query plugin interface and a more specialized sql query interface sounds like a great approach. I would really like to see proper work in that area.

@drunken monkey
Do you want to take over this initiative, especially given that you have the usecase in contrib?

drunken monkey’s picture

I fear I don't have the time for that, sorry, at least not now. If no-one else steps up, maybe I'll give it a shot when I have time, but I'd really be grateful if someone else could find the time to work on this.
tstoeckler, maybe?

Would you then also be open to changing the field handlers (and maybe filters, etc., too) to explicitly check for the SqlQueryInterface before calling the SQL-specific methods, to make adaption easier for non-SQL query plugins? Or are you just for providing interfaces?

Also, how do you rate the chances this can be committed still for 8.0.0? If there's no chance it will be considered unfrozen, then I guess working on it doesn't make much sense anyways.

tstoeckler’s picture

I think I could find some time to work on this. @dawehner and @drunken monkey what would really be useful if you two could give some hints on which methods you think are generic and which are SQL-specific. The list of methods is in #8. Since both of you have written query plugins (in contrast to me) that would be very helpful as a first start. Maybe just go by the name of the method and see which make sense to you if you don't have time to go through the actual implementation. Thanks!

drunken monkey’s picture

I think just putting the methods of the plugin base class on the interface would be the most natural. After all, it seems those methods were previously determined to be backend-independent. Deciding to move other methods to the interface, too, could also be done in follow-up issues.
Also, first you should probably wait for Daniel's opinion on whether this can even still make it into Core. I don't think adding the interface would be possible after 8.0.0 stable? (Don't really understand what will or won't be allowed for 8.1.0, etc., though.)

If Daniel thinks it would be a good idea to already move possible common methods to interface and base class, then these are the ones which I think would still be useful/applicable in the Search API (and probably most other backends):

  • addField() (could just be ignored if not applicable, but would make field handlers easier to implement generically)
  • clearFields()
  • addWhere() (maybe renamed to condition())
  • addOrderBy() (should maybe also be renamed)
  • addTag() (could be ignored for now, but we might add query tags later)

Since renaming methods would be even more disruptive than the rest, though, I don't think that part will be possible.

In any case, thanks a lot for volunteering for this!

dawehner’s picture

It would be great if the generic query interface could be kept as simple as possible and then have some interface with sane things like fields, where order by etc.

jibran’s picture

+1 to #15

dawehner’s picture

I would also aim onto an interface which looks kinda similar to EQ. This would make it easier to move to a EQ based view in the future,
as every handle implementing just a simple query interface could be seen as EQ-able.

mkalkbrenner’s picture

Any progress here, maybe on github?
I ran into this issue today while working on apachesolr_multilingual 8.x which is based on search_api now.

christianadamski’s picture

Any updates here?

nick_vh’s picture

Also Acquia Search using the Acquia Connector contrib module is affected by this bug.

 diff --git a/docroot/core/modules/views/src/Plugin/views/HandlerBase.php b/docroot/core/modules/views/src/Plugin/views/HandlerBase.php
  index 82a4a92..1b12184 100644
  --- a/docroot/core/modules/views/src/Plugin/views/HandlerBase.php
  +++ b/docroot/core/modules/views/src/Plugin/views/HandlerBase.php
  @@ -549,7 +549,7 @@ public function setRelationship() {
  	* {@inheritdoc}
  	*/
 	public function ensureMyTable() {
  -	if (!isset($this->tableAlias)) {
  +	if (!isset($this->tableAlias) && method_exists($this->query, "ensureTable")) {
     	$this->tableAlias = $this->query->ensureTable($this->table, $this->relationship);
   	}
   	return $this->tableAlias;

Would changing this in Drupal core really break things so horribly? If we can proceed with this patch or if someone can explain me what the better work-around is, I'm happy to document and implement that. This patch was provided to me by users that tried certain combinations of contrib modules and found this to break. I'm not submitting this as a patch to solve all the things but I do want to show this is a real issue that needs to be solved sooner rather than later.

christianadamski’s picture

I think we need a decision here. Either this gets fixed now, or is pushed back to Drupal-Next.
Other modules are in limbo over this.

dawehner’s picture

I think we need a decision here

Alright, let me decide that @christianadamski will work on that :P
So yeah, the problem is that we need to design a more generic query interface and well, time is limited.

Regarding the patch in #20, what about adding an @internal interface with #8 and document that this is just a temporary workaround ?

christianadamski’s picture

@dawehner I'm just a pretty face with a Drupal T-Shirt, you don't want me to work on important stuff :)

dawehner’s picture

No worries, just wanted to explain with a bit hurmor what is going on, or what isn't, in this issue.

chx’s picture

Does this really block anything? Add an empty ensureMyTable and move on. The question rather is, what are you doing that this method is called at all? For example, random pick, BooleanOperatorString has $where = '(' . $where . " OR $this->tableAlias.$this->realField IS NULL)"; you need to override the whole plugin if you are not SQL anyways. It's rather odd to pick at this method, it's the least of your problems.

dawehner’s picture

Well for sure it doesn't block anyone, people worked around that problem since Drupal 6, but in general having a more sane architecture would be nice, but guess, noone came up with something.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Version: 8.1.x-dev » 8.2.x-dev
Category: Bug report » Task
Issue tags: +Triaged core major

This issue is not really a bug per se since we did not support this before either, but the core committers and Views maintainers agreed that this agreed it should be a major issue since it definitely interferes with non-SQL Views support in a way that is at least really frustrating from a DX perspective.

To move this forward, I think we should (a) address @dawehner's feedback in #22 and (b) see if there are any reasons @chx's proposed workaround in #25 does not work as a temporary fix.

chx’s picture

Wait, what "workaround"? If ensureMyTable is called very often (always?) SQL calls follow so you can't use the plugin as it is. If there's a major issue it is "Views is SQL bound". You are trying to untangle a gordian knot by pulling at a single thread. This is not conductive to success. Cutting through the knot with a sword is.

You need a way to describe data structures and then manipulate them without ever writing a lick of SQL.

So the real question is: how can you enhance entity query to achieve all that Views does? Then get efq_views in core, convert all core views to efq views, mark old ones deprecated, and when D9 comes remove those.

Edit: normally I do not post any more to the issue queue but since I was pinged, somewhat reluctantly, I am. If anyone has a problem, let me know and I will immediately delete this.

drunken monkey’s picture

Wait, what "workaround"? If ensureMyTable is called very often (always?) SQL calls follow so you can't use the plugin as it is.

That's not true. What follows is often, e.g., a call to addWhere() – and since that passes a field, value and operator separately, it will also work just as well for a lot of other backends.
In the Search API we now managed, by just implementing a few methods from the SQL query plugin in our query plugin (e.g., addWhere()) and with the help of a few traits (1, 2, 3) to re-use all Views plugins with relatively little trouble – e.g., see the date field and filter.
It's just pretty ugly, I'd say, and I think it could be a lot less work and complexity with a little more help from Views itself – but it basically works. We just need to explicitly override those instances where a concrete SQL expression is used (as a condition or a column) – and that's very few places, actually.

So, to summarize (and answer xjm): yes, just implementing a few methods from the SQL query plugin and maybe doing some automatic conversions will go a long way to making the plugins reusable – but it's a lot more work than it needs to be. Definitely not a bug, though – you're right.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

dqd’s picture

Any news or discussion on this which maybe hasn't been reported back here? Is this still an issue or should we close it in favour of other routes on the map? Just humbled over this one here be reading thru the issues and thought it looks important enough to wake it up or to discuss closing it with links to the new places.

daffie’s picture

Status: Active » Needs review
StatusFileSize
new688 bytes

Lets see what happens when we remove the method.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative, +Needs change record

This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

Think instead of removing it should be deprecated first
#41 applies but had a bunch of failures

Definitely think it will need a change record.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.