db = $db; } /** @return array{rows: object[], cursor: array, done: bool} */ public function page(TableSchema $s, array $cursor, int $limit = self::PAGE): array { $limit = max(1, min(self::PAGE, $limit)); $table = Db::ident($s->name); // Explicit column list: SELECT * skips INVISIBLE columns, and generated columns are left out on purpose. $select = implode(',', array_map([Db::class, 'ident'], $s->columns)); foreach (array_keys($s->bins) as $col) { $select .= ', HEX(' . Db::ident($col) . ') AS ' . Db::ident(strtolower($col) . '__hex'); } foreach (array_keys($s->bits) as $col) { $select .= ', ' . Db::ident($col) . '+0 AS ' . Db::ident(strtolower($col) . '__bit'); } if ($s->key !== []) { $where = !empty($cursor['after']) ? ' WHERE ' . $this->after_clause($s->key, $cursor['after']) : ''; $order = implode(',', array_map([Db::class, 'ident'], $s->key)); $sql = "SELECT {$select} FROM {$table}{$where} ORDER BY {$order} LIMIT {$limit}"; } else { $offset = (int) ($cursor['offset'] ?? 0); $order = implode(',', range(1, count($s->columns))); $sql = "SELECT {$select} FROM {$table} ORDER BY {$order} LIMIT {$offset}, {$limit}"; } $rows = $this->db->results($sql); if ($rows === []) { return ['rows' => [], 'cursor' => $cursor, 'done' => true]; } $last = $rows[count($rows) - 1]; if ($s->key !== []) { $after = []; foreach ($s->key as $col) { $after[$col] = $last->$col; } $next = ['after' => $after]; } else { $next = ['offset' => (int) ($cursor['offset'] ?? 0) + count($rows)]; } return ['rows' => $rows, 'cursor' => $next, 'done' => count($rows) < $limit]; } // Ported from WP Migrate 2.7.11 (GPL) Table::build_select_query compound-key clause: // (a = 1 AND b = 2 AND c > 3) OR (a = 1 AND b > 2) OR (a > 1) public function after_clause(array $key, array $after): string { $wpdb = $this->db->wpdb(); $clauses = []; $cols = array_values($key); while ($cols !== []) { $parts = []; $last = count($cols) - 1; foreach ($cols as $i => $col) { $parts[] = Db::ident($col) . ($i === $last ? ' > ' : ' = ') . $wpdb->prepare('%s', (string) $after[$col]); } $clauses[] = '(' . implode(' AND ', $parts) . ')'; array_pop($cols); } return '(' . implode(' OR ', $clauses) . ')'; } }