<commit_before>package sqlancer.sqlite3.schema;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import sqlancer.IgnoreMeException;
import sqlancer.Randomly;
import sqlancer.common.schema.AbstractRowValue;
import sqlancer.common.schema.AbstractSchema;
import sqlancer.common.schema.AbstractTable;
import sqlancer.common.schema.AbstractTableColumn;
import sqlancer.common.schema.AbstractTables;
import sqlancer.common.schema.TableIndex;
import sqlancer.sqlite3.SQLite3Provider.SQLite3GlobalState;
import sqlancer.sqlite3.ast.SQLite3Constant;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Column.SQLite3CollateSequence;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Table;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Table.TableKind;

public class SQLite3Schema extends AbstractSchema<SQLite3Table> {

    /**
     * All possible aliases for the rowid column.
     */
    public static final List<String> ROWID_STRINGS = Collections
            .unmodifiableList(Arrays.asList("rowid", "_rowid_", "oid"));
    private final List<String> indexNames;

    public List<String> getIndexNames() {
        return indexNames;
    }

    public String getRandomIndexOrBailout() {
        if (indexNames.isEmpty()) {
            throw new IgnoreMeException();
        } else {
            return Randomly.fromList(indexNames);
        }
    }

    public static class SQLite3Column extends AbstractTableColumn<SQLite3Table, SQLite3DataType> {

        private final boolean isInteger; // "INTEGER" type, not "INT"
        private final SQLite3CollateSequence collate;
        boolean generated;
        private final boolean isPrimaryKey;

        public enum SQLite3CollateSequence {
            NOCASE, RTRIM, BINARY;

            public static SQLite3CollateSequence random() {
                return Randomly.fromOptions(values());
            }
        }

        public SQLite3Column(String name, SQLite3DataType columnType, boolean isInteger, boolean isPrimaryKey,
                SQLite3CollateSequence collate) {
            super(name, null, columnType);
            this.isInteger = isInteger;
            this.isPrimaryKey = isPrimaryKey;
            this.collate = collate;
            this.generated = false;
            assert !isInteger || columnType == SQLite3DataType.INT;
        }

        public SQLite3Column(String rowId, SQLite3DataType columnType, boolean isInteger,
                SQLite3CollateSequence collate, boolean generated) {
            this(rowId, columnType, isInteger, generated, collate);
            this.generated = generated;
        }

        public boolean isPrimaryKey() {
            return isPrimaryKey;
        }

        public boolean isOnlyPrimaryKey() {
            return isPrimaryKey && getTable().getColumns().stream().filter(c -> c.isPrimaryKey()).count() == 1;
        }

        // see https://www.sqlite.org/lang_createtable.html#rowid
        /**
         * If a table has a single column primary key and the declared type of that column is "INTEGER" and the table is
         * not a WITHOUT ROWID table, then the column is known as an INTEGER PRIMARY KEY.
         *
         * @return whether the column is an INTEGER PRIMARY KEY
         */
        public boolean isIntegerPrimaryKey() {
            return isInteger && isOnlyPrimaryKey() && !getTable().hasWithoutRowid();
        }

        public SQLite3CollateSequence getCollateSequence() {
            return collate;
        }

        public boolean isGenerated() {
            return generated;
        }

        public static SQLite3Column createDummy(String name) {
            return new SQLite3Column(name, SQLite3DataType.INT, false, false, null);
        }

    }

    public static SQLite3Constant getConstant(ResultSet randomRowValues, int columnIndex, SQLite3DataType valueType)
            throws SQLException, AssertionError {
        Object value;
        SQLite3Constant constant;
        switch (valueType) {
        case INT:
            value = randomRowValues.getLong(columnIndex);
            constant = SQLite3Constant.createIntConstant((long) value);
            break;
        case REAL:
            value = randomRowValues.getDouble(columnIndex);
            if (!Double.isFinite((double) value)) {
                // TODO: the JDBC driver seems to sometimes return infinity for NULL values
                throw new IgnoreMeException();
            }
            constant = SQLite3Constant.createRealConstant((double) value);
            break;
        case TEXT:
        case NONE:
            value = randomRowValues.getString(columnIndex);
            constant = SQLite3Constant.createTextConstant((String) value);
            break;
        case BINARY:
            value = randomRowValues.getBytes(columnIndex);
            constant = SQLite3Constant.createBinaryConstant((byte[]) value);
            break;
        case NULL:
            return SQLite3Constant.createNullConstant();
        default:
            throw new AssertionError(valueType);
        }
        return constant;
    }

    public static class SQLite3Tables extends AbstractTables<SQLite3Table, SQLite3Column> {

        public SQLite3Tables(List<SQLite3Table> tables) {
            super(tables);
        }

        public SQLite3RowValue getRandomRowValue(Connection con) throws SQLException {
            String randomRow = String.format("SELECT %s, %s FROM %s ORDER BY RANDOM() LIMIT 1", columnNamesAsString(
                    c -> c.getTable().getName() + "." + c.getName() + " AS " + c.getTable().getName() + c.getName()),
                    columnNamesAsString(c -> "typeof(" + c.getTable().getName() + "." + c.getName() + ")"),
                    tableNamesAsString());
            Map<SQLite3Column, SQLite3Constant> values = new HashMap<>();
            try (Statement s = con.createStatement()) {
                ResultSet randomRowValues;
                try {
                    randomRowValues = s.executeQuery(randomRow);
                } catch (SQLException e) {
                    throw new IgnoreMeException();
                }<commit_msg>[SQLite] Work around a JDBC driver issue<commit_after>package sqlancer.sqlite3.schema;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import sqlancer.IgnoreMeException;
import sqlancer.Randomly;
import sqlancer.common.schema.AbstractRowValue;
import sqlancer.common.schema.AbstractSchema;
import sqlancer.common.schema.AbstractTable;
import sqlancer.common.schema.AbstractTableColumn;
import sqlancer.common.schema.AbstractTables;
import sqlancer.common.schema.TableIndex;
import sqlancer.sqlite3.SQLite3Provider.SQLite3GlobalState;
import sqlancer.sqlite3.ast.SQLite3Constant;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Column.SQLite3CollateSequence;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Table;
import sqlancer.sqlite3.schema.SQLite3Schema.SQLite3Table.TableKind;

public class SQLite3Schema extends AbstractSchema<SQLite3Table> {

    /**
     * All possible aliases for the rowid column.
     */
    public static final List<String> ROWID_STRINGS = Collections
            .unmodifiableList(Arrays.asList("rowid", "_rowid_", "oid"));
    private final List<String> indexNames;

    public List<String> getIndexNames() {
        return indexNames;
    }

    public String getRandomIndexOrBailout() {
        if (indexNames.isEmpty()) {
            throw new IgnoreMeException();
        } else {
            return Randomly.fromList(indexNames);
        }
    }

    public static class SQLite3Column extends AbstractTableColumn<SQLite3Table, SQLite3DataType> {

        private final boolean isInteger; // "INTEGER" type, not "INT"
        private final SQLite3CollateSequence collate;
        boolean generated;
        private final boolean isPrimaryKey;

        public enum SQLite3CollateSequence {
            NOCASE, RTRIM, BINARY;

            public static SQLite3CollateSequence random() {
                return Randomly.fromOptions(values());
            }
        }

        public SQLite3Column(String name, SQLite3DataType columnType, boolean isInteger, boolean isPrimaryKey,
                SQLite3CollateSequence collate) {
            super(name, null, columnType);
            this.isInteger = isInteger;
            this.isPrimaryKey = isPrimaryKey;
            this.collate = collate;
            this.generated = false;
            assert !isInteger || columnType == SQLite3DataType.INT;
        }

        public SQLite3Column(String rowId, SQLite3DataType columnType, boolean isInteger,
                SQLite3CollateSequence collate, boolean generated) {
            this(rowId, columnType, isInteger, generated, collate);
            this.generated = generated;
        }

        public boolean isPrimaryKey() {
            return isPrimaryKey;
        }

        public boolean isOnlyPrimaryKey() {
            return isPrimaryKey && getTable().getColumns().stream().filter(c -> c.isPrimaryKey()).count() == 1;
        }

        // see https://www.sqlite.org/lang_createtable.html#rowid
        /**
         * If a table has a single column primary key and the declared type of that column is "INTEGER" and the table is
         * not a WITHOUT ROWID table, then the column is known as an INTEGER PRIMARY KEY.
         *
         * @return whether the column is an INTEGER PRIMARY KEY
         */
        public boolean isIntegerPrimaryKey() {
            return isInteger && isOnlyPrimaryKey() && !getTable().hasWithoutRowid();
        }

        public SQLite3CollateSequence getCollateSequence() {
            return collate;
        }

        public boolean isGenerated() {
            return generated;
        }

        public static SQLite3Column createDummy(String name) {
            return new SQLite3Column(name, SQLite3DataType.INT, false, false, null);
        }

    }

    public static SQLite3Constant getConstant(ResultSet randomRowValues, int columnIndex, SQLite3DataType valueType)
            throws SQLException, AssertionError {
        Object value;
        SQLite3Constant constant;
        switch (valueType) {
        case INT:
            value = randomRowValues.getLong(columnIndex);
            constant = SQLite3Constant.createIntConstant((long) value);
            break;
        case REAL:
            value = randomRowValues.getDouble(columnIndex);
            if (!Double.isFinite((double) value)) {
                // TODO: the JDBC driver seems to sometimes return infinity for NULL values
                throw new IgnoreMeException();
            }
            constant = SQLite3Constant.createRealConstant((double) value);
            break;
        case TEXT:
        case NONE:
            value = randomRowValues.getString(columnIndex);
            constant = SQLite3Constant.createTextConstant((String) value);
            break;
        case BINARY:
            value = randomRowValues.getBytes(columnIndex);
            constant = SQLite3Constant.createBinaryConstant((byte[]) value);
            if (((byte[]) value).length == 0) {
                // TODO: the JDBC driver seems to sometimes return a zero-length array for NULL values
                throw new IgnoreMeException();
            }
            break;
        case NULL:
            return SQLite3Constant.createNullConstant();
        default:
            throw new AssertionError(valueType);
        }
        return constant;
    }

    public static class SQLite3Tables extends AbstractTables<SQLite3Table, SQLite3Column> {

        public SQLite3Tables(List<SQLite3Table> tables) {
            super(tables);
        }

        public SQLite3RowValue getRandomRowValue(Connection con) throws SQLException {
            String randomRow = String.format("SELECT %s, %s FROM %s ORDER BY RANDOM() LIMIT 1", columnNamesAsString(
                    c -> c.getTable().getName() + "." + c.getName() + " AS " + c.getTable().getName() + c.getName()),
                    columnNamesAsString(c -> "typeof(" + c.getTable().getName() + "." + c.getName() + ")"),
                    tableNamesAsString());
            Map<SQLite3Column, SQLite3Constant> values = new HashMap<>();
            try (Statement s = con.createStatement()) {
                ResultSet randomRowValues;
                try {
                    randomRowValues = s.executeQuery(randomRow);
                } catch (SQLException e) {
                    throw new IgnoreMeException();
                }
{'fraction_non_alphanumeric': 0.08650652660848072, 'fraction_numerical': 0.008264462809917356, 'mean_word_length': 2.441786283891547, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 27, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '4144879', 'n_tokens_mistral': 3410, 'n_tokens_neox': 3332, 'n_words': 833}