package sqlancer.common.schema;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import sqlancer.Randomly;

public class AbstractTable<C extends AbstractTableColumn<?, ?>, I extends TableIndex>
        implements Comparable<AbstractTable<?, ?>> {

    private final String name;
    private final List<C> columns;
    private final List<I> indexes;
    private final boolean isView;

    public AbstractTable(String name, List<C> columns, List<I> indexes, boolean isView) {
        this.name = name;
        this.indexes = indexes;
        this.isView = isView;
        this.columns = Collections.unmodifiableList(columns);
    }

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(AbstractTable<?, ?> o) {
        return o.getName().compareTo(getName());
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(getName());
        sb.append("\n");
        for (C c : columns) {
            sb.append("\t" + c + "\n");
        }
        return sb.toString();
    }

    public List<I> getIndexes() {
        return indexes;
    }

    public List<C> getColumns() {
        return columns;
    }

    public String getColumnsAsString() {
        return columns.stream().map(c -> c.getName()).collect(Collectors.joining(", "));
    }

    public String getColumnsAsString(Function<C, String> function) {
        return columns.stream().map(function).collect(Collectors.joining(", "));
    }

    public C getRandomColumn() {
        return Randomly.fromList(columns);
    }

    public boolean hasIndexes() {
        return !indexes.isEmpty();
    }

    public TableIndex getRandomIndex() {
        return Randomly.fromList(indexes);
    }

    public List<C> getRandomNonEmptyColumnSubset() {
        return Randomly.nonEmptySubset(getColumns());
    }

    public List<C> getRandomNonEmptyColumnSubset(int size) {
        return Randomly.nonEmptySubset(getColumns(), size);
    }

    public boolean isView() {
        return isView;
    }

    public String getFreeColumnName() {
        int i = 0;
        if (Randomly.getBooleanWithRatherLowProbability()) {
            i = (int) Randomly.getNotCachedInteger(0, 100);
        }
        do {
            String columnName = String.format("c%d", i++);
            if (columns.stream().noneMatch(t -> t.getName().contentEquals(columnName))) {
                return columnName;
            }
        } while (true);

    }

}

@@ -26,16 +26,46 @@
 chema;%0A%0A
+import java.sql.SQLException;%0A
 import j
@@ -198,24 +198,179 @@
 qlancer.
-Randomly
+GlobalState;%0Aimport sqlancer.IgnoreMeException;%0Aimport sqlancer.Randomly;%0Aimport sqlancer.common.query.QueryAdapter;%0Aimport sqlancer.common.query.SQLancerResultSet
 ;%0A%0Apubli
@@ -504,16 +504,74 @@
  ?%3E%3E %7B%0A%0A
+    private static final int NO_ROW_COUNT_AVAILABLE = -1;%0A
     priv
@@ -696,16 +696,68 @@
  isView;
+%0A    private long rowCount = NO_ROW_COUNT_AVAILABLE;
 %0A%0A    pu
@@ -2847,10 +2847,742 @@
 %0A    %7D%0A%0A
+    public long getNrRows(GlobalState%3C?, ?%3E globalState) %7B%0A        if (rowCount == NO_ROW_COUNT_AVAILABLE) %7B%0A            QueryAdapter q = new QueryAdapter(%22SELECT COUNT(*) FROM %22 + name);%0A            try (SQLancerResultSet query = q.executeAndGet(globalState)) %7B%0A                if (query == null) %7B%0A                    throw new IgnoreMeException();%0A                %7D%0A                query.next();%0A                rowCount = query.getLong(1);%0A                return rowCount;%0A            %7D catch (SQLException e) %7B%0A                // an exception might be expected, for example, when invalid view is created%0A                throw new IgnoreMeException();%0A            %7D%0A        %7D else %7B%0A            return rowCount;%0A        %7D%0A    %7D%0A
 %7D%0A

Add a general method in AbstractTable to obtain a table's row count