blic function add_all( $items, $id_prefix ) { global $wpdb; $query = "INSERT INTO {$this->table_name} (queue_id, event_id, event_payload ) VALUES "; $rows = array(); $count_items = count( $items ); for ( $i = 0; $i < $count_items; ++$i ) { // skip empty items. if ( empty( $items[ $i ] ) ) { continue; } try { $event_id = esc_sql( $id_prefix . '-' . $i ); $event_payload = esc_sql( serialize( $items[ $i ] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize $rows[] = "('{$this->queue_id}', '$event_id','$event_payload')"; } catch ( \Exception $e ) { // Item cannot be serialized so skip. continue; } } $rows_added = $wpdb->query( $query . implode( ',', $rows ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $rows_added; } /** * Return $max_count items from the queue, including their value string length. * * @param int $max_count How many items to fetch from the queue. * * @return array|object|stdClass[]|null */ public function get_items_ids_with_size( $max_count ) { global $wpdb; // TODO optimize the fetch to happen by queue name not by the IDs as it can be issue cross-queues. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared return $wpdb->get_results( $wpdb->prepare( /** * Ignoring the linting warning, as there's still no placeholder replacement for DB field name, * in this case this is `$this->table_name` */ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT event_id AS id, LENGTH( event_payload ) AS value_size FROM {$this->table_name} WHERE queue_id = %s ORDER BY event_id ASC LIMIT %d", $this->queue_id, $max_count ), OBJECT ); } /** * Delete items with specific IDs from the queue. * * @param array $ids IDs of the items to remove from the queue. * * @return bool|int|\mysqli_result|resource|null */ public function delete_items_by_ids( $ids ) { global $wpdb; $ids_placeholders = implode( ', ', array_fill( 0, count( $ids ), '%s' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared return $wpdb->query( $wpdb->prepare( /** * Ignoring the linting warning, as there's still no placeholder replacement for DB field name, * in this case this is `$this->table_name` */ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "DELETE FROM {$this->table_name} WHERE queue_id = %s AND event_id IN ( $ids_placeholders )", array_merge( array( $this->queue_id ), $ids ) ) ); } /** * Table initialization */ public static function initialize_custom_sync_table() { /** * Initialize an instance of the class with a test name, so we can use table prefix and then test if the table is healthy. */ $custom_table_instance = new Queue_Storage_Table( 'test_queue' ); // Check if the table exists if ( ! $custom_table_instance->custom_table_exists() ) { $custom_table_instance->create_table(); } return $custom_table_instance->is_custom_table_healthy(); } /** * Migrates the existing Sync events from the options table to the Custom table * * @return bool */ public static function migrate_from_options_table_to_custom_table() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $count_result = $wpdb->get_row( " SELECT COUNT(*) as item_count FROM {$wpdb->options} WHERE option_name LIKE 'jpsq_%' " ); $item_count = $count_result->item_count; $limit = 100; $offset = 0; do { // get all the records from the options table $query = " SELECT option_name as event_id, option_value as event_payload FROM {$wpdb->options} WHERE option_name LIKE 'jpsq_%' ORDER BY option_name ASC LIMIT $offset, $limit "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $rows = $wpdb->get_results( $query ); $insert_rows = array(); foreach ( $rows as $event ) { $event_id = $event->event_id; // Parse the event if ( preg_match( '!jpsq_(?P[^-]+)-(?P[^-]+)-.+!', $event_id, $events_matches ) ) { $queue_id = $events_matches['queue_id']; $timestamp = $events_matches['timestamp']; $insert_rows[] = $wpdb->prepare( '(%s, %s, %s, %s)', array( $queue_id, $event_id, $event->event_payload, (int) $timestamp, ) ); } } // Instantiate table storage, so we can get the table name. Queue ID is just a placeholder here. $queue_table_storage = new Queue_Storage_Table( 'test_queue' ); if ( ! empty( $insert_rows ) ) { $insert_query = 'INSERT INTO ' . $queue_table_storage->table_name . ' (queue_id, event_id, event_payload, timestamp) VALUES '; $insert_query .= implode( ',', $insert_rows ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $insert_query ); } $offset += $limit; } while ( $offset < $item_count ); // Clear out the options queue // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name LIKE %s", 'jpsq_%-%' ) ); return true; } /** * Migrates the existing Sync events from the Custom table to the Options table * * @return void */ public static function migrate_from_custom_table_to_options_table() { global $wpdb; // Instantiate table storage, so we can get the table name. Queue ID is just a placeholder here. $queue_table_storage = new Queue_Storage_Table( 'test_queue' ); $custom_table_name = $queue_table_storage->table_name; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $count_result = $wpdb->get_row( "SELECT COUNT(*) as item_count FROM {$custom_table_name}" ); $item_count = $count_result->item_count; $limit = 100; $offset = 0; do { // get all the records from the options table $query = " SELECT event_id, event_payload FROM {$custom_table_name} ORDER BY event_id ASC LIMIT $offset, $limit "; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $rows = $wpdb->get_results( $query ); $insert_rows = array(); foreach ( $rows as $event ) { $insert_rows[] = $wpdb->prepare( '(%s, %s, "no")', array( $event->event_id, $event->event_payload, ) ); } if ( ! empty( $insert_rows ) ) { $insert_query = "INSERT INTO {$wpdb->options} (option_name, option_value, autoload) VALUES "; $insert_query .= implode( ',', $insert_rows ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( $insert_query ); } $offset += $limit; } while ( $offset < $item_count ); // Clear the custom table // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query( "DELETE FROM {$custom_table_name}" ); // TODO should we drop the table here instead? } }