<commit_before>/// Information about the table, columns, and rows read by a SelectStatement.
///
/// You don't create SelectionInfo directly. Instead, you use one of
/// those methods:
///
///     class SelectStatement {
///         var selectionInfo: SelectionInfo
///     }
///     protocol Request {
///         func selectionInfo(_ db: Database) throws -> SelectionInfo
///     }
///     class Database {
///         func selectionInfo(rowIds: Set<Int64>, in tableName: String) throws -> SelectionInfo
///     }
public struct DatabaseSelectionInfo {
    private let tables: [String: TableSelectionInfo]?
    private init(tables: [String: TableSelectionInfo]?) {
        self.tables = tables
    }
    
    /// TODO
    public var isEmpty: Bool {
        guard let tables = tables else { return false }
        return tables.isEmpty
    }
    
    var tableNames: Set<String> {
        guard let tables = tables else { return [] }
        return Set(tables.keys)
    }
    
    /// Full database = (All columns in all tables) × (all rows)
    static let fullDatabase = DatabaseSelectionInfo(tables: nil)
    
    /// Empty selection
    init() {
        self.init(tables: [:])
    }
    
    /// (all columns in a table) × (all rows)
    init(table: String) {
        self.init(tables: [table: TableSelectionInfo(columns: nil, rowIds: nil)])
    }
    
    /// (some columns in a table) × (all rows)
    init(table: String, columns: Set<String>) {
        self.init(tables: [table: TableSelectionInfo(columns: columns, rowIds: nil)])
    }
    
    /// (all columns in a table) × (some rows)
    init(table: String, rowIds: Set<Int64>) {
        self.init(tables: [table: TableSelectionInfo(columns: nil, rowIds: rowIds)])
    }
    
    public func intersection(_ other: DatabaseSelectionInfo) -> DatabaseSelectionInfo {
        guard let tables = tables else { return other }
        guard let otherTables = other.tables else { return self }
        
        var tablesIntersection: [String: TableSelectionInfo] = [:]
        for (table, tableInfo) in tables {
            guard let otherTableInfo = otherTables
                .first(where: { (otherTable, _) in otherTable == table })?
                .value else { continue }
            let tableInfoIntersection = tableInfo.intersection(otherTableInfo)<commit_msg>DatabaseSelectionInfo documentation WIP<commit_after>/// DatabaseSelectionInfo holds information about the table, columns, and
/// rows read by database requests.
///
/// Selection infos can represent full tables, columns of a database table, and
/// rows of a database table (identified by their rowids).
///
///    |A|B|C|  |A|B|C|   |A|B|C|
///    |x|x|x|  |x| | |   | | | |
///    |x|x|x|  |x| | |   |x|x|x|
///    |x|x|x|  |x| | |   | | | |
///    |x|x|x|  |x| | |   | | | |
///
/// Unions and intersections of basic selections allow the creation of more
/// complex selections:
///
///    |A|B|C|  |A|B|C|   |A|B|C|
///    |x| |x|  |x|x|x|   |x| |x|
///    |x| |x|  | | | |   | | | |
///    |x| |x|  |x|x|x|   |x| |x|
///    |x| |x|  | | | |   | | | |
///
/// You don't create DatabaseSelectionInfo directly. Instead, you use one of
/// those methods:
///
/// - `SelectStatement.selectionInfo`:
///
///     let statement = db.makeSelectStatement("SELECT name, score FROM players")
///     print(statement.statementInfo)
///     // prints "players(name,score)"
///
/// - `Request.selectionInfo(_:)`
///
///     let request = Player.filter(key: 1)
///     try print(statement.statementInfo(db))
///     // prints "players(*)[1]"
///
/// - `Database.selectionInfo(rowIds:in:)`
///
///     try print(db.selectionInfo(rowIds: [1, 2], in: "players")
///     // prints "players(*)[1, 2]"
public struct DatabaseSelectionInfo {
    private let tables: [String: TableSelectionInfo]?
    private init(tables: [String: TableSelectionInfo]?) {
        self.tables = tables
    }
    
    /// Returns whether the selection is empty.
    public var isEmpty: Bool {
        guard let tables = tables else { return false }
        return tables.isEmpty
    }
    
    var tableNames: Set<String> {
        guard let tables = tables else { return [] }
        return Set(tables.keys)
    }
    
    /// Full database = (All columns in all tables) × (all rows)
    static let fullDatabase = DatabaseSelectionInfo(tables: nil)
    
    /// Empty selection
    init() {
        self.init(tables: [:])
    }
    
    /// (all columns in a table) × (all rows)
    init(table: String) {
        self.init(tables: [table: TableSelectionInfo(columns: nil, rowIds: nil)])
    }
    
    /// (some columns in a table) × (all rows)
    init(table: String, columns: Set<String>) {
        self.init(tables: [table: TableSelectionInfo(columns: columns, rowIds: nil)])
    }
    
    /// (all columns in a table) × (some rows)
    init(table: String, rowIds: Set<Int64>) {
        self.init(tables: [table: TableSelectionInfo(columns: nil, rowIds: rowIds)])
    }
    
    /// Returns a new selection with the database content that is common to both
    /// this selection and the given one.
    public func intersection(_ other: DatabaseSelectionInfo) -> DatabaseSelectionInfo {
        guard let tables = tables else { return other }
        guard let otherTables = other.tables else { return self }
        
        var tablesIntersection: [String: TableSelectionInfo] = [:]
        for (table, tableInfo) in tables {
            guard let otherTableInfo = otherTables
                .first(where: { (otherTable, _) in otherTable == table })?
                .value else { continue }
            let tableInfoIntersection = tableInfo.intersection(otherTableInfo)
{'fraction_non_alphanumeric': 0.14215080346106304, 'fraction_numerical': 0.002119018188239449, 'mean_word_length': 2.310344827586207, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1671671', 'n_tokens_mistral': 1684, 'n_tokens_neox': 1655, 'n_words': 499}