cache-command icon indicating copy to clipboard operation
cache-command copied to clipboard

SQLite Compatibility

Open swissspidy opened this issue 2 years ago • 5 comments

When I manually run the tests locally with SQLite, the following scenarios are failing:

001 Scenario: Deleting all transients on single site # features/transient.feature:58
      Then STDOUT should be:                         # features/transient.feature:76
        $ wp transient delete --all
        Success: No transients found.

002 Scenario: Deleting expired transients on single site # features/transient.feature:123
      Then STDOUT should be:                             # features/transient.feature:134
        $ wp transient delete --expired
        Success: No expired transients found.

003 Scenario: Deleting all transients on multisite # features/transient.feature:194
      Then STDOUT should be:                       # features/transient.feature:215
        $ wp transient delete --all
        Success: No transients found.

004 Scenario: Deleting expired transients on multisite # features/transient.feature:286
      Then STDOUT should be:                           # features/transient.feature:301
        $ wp transient delete --expired
        Success: No expired transients found.

005 Scenario: List transients on single site # features/transient.feature:386
      Then STDOUT should contain:            # features/transient.feature:400
        $ wp transient list --format=csv
        name,value,expiration

006 Scenario: List transients on multisite # features/transient.feature:431
      Then STDOUT should contain:          # features/transient.feature:445
        $ wp transient list --format=csv
        name,value,expiration

007 Scenario: List transients with search and exclude pattern # features/transient.feature:476
      Then STDOUT should be:                                  # features/transient.feature:485
        $ wp transient list --format=csv --fields=name --search="foo"
        name

The transient command uses lots of custom MySQL queries. The SQLite integration plugin is supposed to make those SQLite-compatible, but perhaps something is not working there.

Or, since deletion and listing are failing, maybe the issue is that the insertion using set_transient() is failing in the first place.

Let's figure it out together. This could involve changes to the command, the tests, core, or the SQLite plugin.

swissspidy avatar Oct 21 '23 13:10 swissspidy

OK, so for the transient delete command there seem to be issue with the way LIKEs are being escaped by WordPress. The SQLite plugin might not be translating those properly and ending up deleting 0 rows. "Raw" queries without $wpdb->prepare do seem to work. That said, the returned deletion counts seem to be wrong. Not sure if we need something like $wpdb->query( "SELECT changes() FROM {$wpdb->options}" ) as well.

swissspidy avatar Oct 23 '23 20:10 swissspidy

@swissspidy I took a look at this, I'm not familiar with SQLite so I might be mistaking, but it seems that SQLite doesn't support DELETE statement for multiple tables like MySQL does :

DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b
WHERE a.option_name LIKE %s
AND a.option_name NOT LIKE %s
AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )

I did manage to get the tests to pass by doing three queries to replicate what this DELETE statement, but maybe the SQLite WordPress plugin could handle that automatically.

petitphp avatar Apr 29 '24 08:04 petitphp

Interesting! Do you perhaps have a draft PR with that solution so that we could take a look?

It does seem like a case the SQLite plugin could handle, so If you could open an issues I'm sure the maintainers can take a look.

swissspidy avatar Apr 29 '24 09:04 swissspidy

Turn out it's related to the escaping, like you suspected.

Looking at the wpdb::esc_like method in the SQLite integration plugin, they purposely remove the addslashes since SQLite doesn't care for the _% characters.

I'm able to fix the issue by modifying the Utils::esc_like to use the native wpdb method if available. But this will break the associated test in SQLite mode. We would need to create a new test to run in that mode.

function esc_like( $text ) {
	global $wpdb;

	if ( method_exists( $wpdb, 'esc_like' ) ) {
		return $wpdb->esc_like( $text );
	}

	return addcslashes( $text, '_%\\' );
}

petitphp avatar Apr 29 '24 19:04 petitphp

Let‘s try that 👍

swissspidy avatar Apr 29 '24 20:04 swissspidy

Or, since deletion and listing are failing, maybe the issue is that the insertion using set_transient() is failing in the first place.

Looking at the database after a wp transient set foo bar, you can see that it is properly added to wp_options table.

Also, if you try the same transient again, it correctly fails with an error because this particular transient was already set and conly be updated.

This means that the insertion succeeded, and the list fails because it does not see the existing transients.

schlessera avatar May 05 '25 19:05 schlessera

Fixed via https://github.com/wp-cli/wp-cli/pull/6088

schlessera avatar May 05 '25 22:05 schlessera

Is there maybe an issue where you track overall SQLite compatibility? I'm curious what other commands fail in WP-CLI.

bgrgicak avatar May 06 '25 04:05 bgrgicak