<commit_before>import ceylon.collection {
    HashMap,
    ArrayList
}
import ceylon.dbc {
    newConnectionFromDataSource
}
import ceylon.interop.java {
    toIntegerArray
}
import ceylon.math.decimal {
    Decimal,
    parseDecimal
}
import ceylon.math.whole {
    parseWhole
}
import ceylon.time {
    today, Instant
}
import ceylon.time.internal {
    GregorianDateTime,
    GregorianDate,
    TimeOfDay
}

import java.lang {
    JBoolean=Boolean,
    JInteger=Integer,
    JLong=Long,
    JString=String,
    ObjectArray
}
import java.math {
    BigDecimal,
    BigInteger
}
import java.sql {
    PreparedStatement,
    CallableStatement,
    ResultSet,
    ResultSetMetaData,
    SqlArray=Array,
    SqlTimestamp=Timestamp,
    SqlTime=Time,
    SqlDate=Date,
    Statement {
        returnGeneratedKeys=RETURN_GENERATED_KEYS
    },
    Connection
}
import java.util {
    Date,
    UUID
}


"A row of results is represented as a [[Map]] with column
 names as keys, and values as items."
shared alias Row=>Map<String,Object>;

"An object that exposes operations for executing SQL DML or
 DDL queries against JDBC connections obtained by calling a 
 given [[function|newConnection]]."
by ("<NAME>", "<NAME>")
shared class Sql(newConnection) {
    
    "Obtain a JDBC connection."
    see (`function newConnectionFromDataSource`,
        `function newConnectionFromDataSourceWithCredentials`)
    Connection newConnection();
    
    value connection = ThreadLocalConnection(newConnection);
    
    PreparedStatement prepareStatement(ConnectionStatus conn, String sql, {Object*} arguments) {
        value stmt = conn.connection().prepareStatement(sql);
        setParameters(stmt, arguments);
        return stmt;
    }
    
    void setParameters(PreparedStatement stmt, {Object*} arguments) {
        variable value i=1;
        for (argument in arguments) {
            switch (argument)
            case (is Integer) { 
                stmt.setLong(i,argument); 
            }
            case (is Boolean) { 
                stmt.setBoolean(i,argument); 
            }
            case (is String)  { 
                stmt.setString(i,argument); 
            }
            case (is Decimal) {
                assert (is BigDecimal bd = argument.implementation);
                stmt.setBigDecimal(i,bd); 
            }
            case (is Date) {
                if (is SqlTimestamp argument) {
                    stmt.setTimestamp(i, argument);
                } 
                else if (is SqlTime argument) {
                    stmt.setTime(i, argument);
                }
                else if (is SqlDate argument) {
                    stmt.setDate(i, argument);
                }
                else {
                    stmt.setTimestamp(i, SqlTimestamp(argument.time));
                }
            }
            case (is Float) { 
                stmt.setDouble(i, argument); 
            }
            case (is SqlNull) { 
                stmt.setNull(i, argument.sqlType); 
            }
            case (is GregorianDateTime) {
                stmt.setTimestamp(i, 
                    SqlTimestamp(argument.instant().millisecondsOfEpoch));
            }
            case (is GregorianDate) {
                stmt.setDate(i, 
                    SqlDate(argument.at(TimeOfDay(0)).instant().millisecondsOfEpoch));
            }
            case (is TimeOfDay) {
                stmt.setTime(i, 
                    SqlTime(today().at(argument).instant().millisecondsOfEpoch));
            }
            // UUID conversion works in the else also, this is a placeholder in case Ceylon gets a native UUID type.
            case(is UUID) {
                stmt.setObject(i, argument);
            }
            case(is ObjectArray<Object>) {
                stmt.setArray(i, connection.get().createSqlArray( argument, sqlArrayType(argument)));
            }
            //TODO reader, inputStream, byte array
            else { stmt.setObject(i,argument); }
            i++;
        }
    }
    
    String sqlArrayType(ObjectArray<Object> argument) {
        if (argument.size > 0) {
            value first = argument.get(0);
            
            /*
             TODO:  Due to https://github.com/ceylon/ceylon-compiler/issues/1753, we need to check the type of 
                    first element of the array to determine the type of the database array.
                    There is also the question of non-standard behaviour for at least on database, PostgreSQL,
                    which will only accept lowercase versions of the types below.  (ex varchar, but not VARCHAR).
                    As well, JDK 8 has java.sql.JDBCType that contains these String values, but JDK 7 does not,
                    which is why the values below are hard-coded.
                    
             */
            switch (first)
            case (is String) {
                return "varchar";
            }
            case (is Integer) {
                return "integer";
            }
            case (is Boolean) {
                return "boolean";
            }
            case (is Decimal) {
                return "decimal";
            }
            case (is Float) {
                return "float";
            }
            case (is Date) {
                if (is SqlTimestamp first) {
                    return "timestamp";
                } 
                else if (is SqlTime first) {
                    return "time";
                }
                else if (is SqlDate first) {
                    return "date";
                }
                else {
                    return "timestamp";
                }
            }
            case (is GregorianDateTime) {
                return "timestamp";
            }
            case (is GregorianDate) {<commit_msg>Add Ceylon array support to JDBC array conversion.<commit_after>import ceylon.collection {
    HashMap,
    ArrayList
}
import ceylon.dbc {
    newConnectionFromDataSource
}
import ceylon.interop.java {
    toIntegerArray,
    createJavaObjectArray
}
import ceylon.language.meta.model {
    Type
}
import ceylon.math.decimal {
    Decimal,
    parseDecimal
}
import ceylon.math.whole {
    parseWhole
}
import ceylon.time {
    today,
    Instant,
    Time,
    DateTime,
    Date
}
import ceylon.time.internal {
    GregorianDateTime,
    GregorianDate,
    TimeOfDay
}

import java.lang {
    JBoolean=Boolean,
    JInteger=Integer,
    JLong=Long,
    JString=String,
    ObjectArray
}
import java.math {
    BigDecimal,
    BigInteger
}
import java.sql {
    PreparedStatement,
    CallableStatement,
    ResultSet,
    ResultSetMetaData,
    SqlArray=Array,
    SqlTimestamp=Timestamp,
    SqlTime=Time,
    SqlDate=Date,
    Statement {
        returnGeneratedKeys=RETURN_GENERATED_KEYS
    },
    Connection
}
import java.util {
    JDate=Date,
    UUID
}


"A row of results is represented as a [[Map]] with column
 names as keys, and values as items."
shared alias Row=>Map<String,Object>;

"An object that exposes operations for executing SQL DML or
 DDL queries against JDBC connections obtained by calling a 
 given [[function|newConnection]]."
by ("<NAME>", "<NAME>")
shared class Sql(newConnection) {
    
    "Obtain a JDBC connection."
    see (`function newConnectionFromDataSource`,
        `function newConnectionFromDataSourceWithCredentials`)
    Connection newConnection();
    
    value connection = ThreadLocalConnection(newConnection);
    
    PreparedStatement prepareStatement(ConnectionStatus conn, String sql, {Object*} arguments) {
        value stmt = conn.connection().prepareStatement(sql);
        setParameters(stmt, arguments);
        return stmt;
    }
    
    void setParameters(PreparedStatement stmt, {Object*} arguments) {
        variable value i=1;
        for (argument in arguments) {
            switch (argument)
            case (is Integer) { 
                stmt.setLong(i,argument); 
            }
            case (is Boolean) { 
                stmt.setBoolean(i,argument); 
            }
            case (is String)  { 
                stmt.setString(i,argument); 
            }
            case (is Decimal) {
                assert (is BigDecimal bd = argument.implementation);
                stmt.setBigDecimal(i,bd); 
            }
            case (is JDate) {
                if (is SqlTimestamp argument) {
                    stmt.setTimestamp(i, argument);
                } 
                else if (is SqlTime argument) {
                    stmt.setTime(i, argument);
                }
                else if (is SqlDate argument) {
                    stmt.setDate(i, argument);
                }
                else {
                    stmt.setTimestamp(i, SqlTimestamp(argument.time));
                }
            }
            case (is Float) { 
                stmt.setDouble(i, argument); 
            }
            case (is SqlNull) { 
                stmt.setNull(i, argument.sqlType); 
            }
            case (is GregorianDateTime) {
                stmt.setTimestamp(i, 
                    SqlTimestamp(argument.instant().millisecondsOfEpoch));
            }
            case (is GregorianDate) {
                stmt.setDate(i, 
                    SqlDate(argument.at(TimeOfDay(0)).instant().millisecondsOfEpoch));
            }
            case (is TimeOfDay) {
                stmt.setTime(i, 
                    SqlTime(today().at(argument).instant().millisecondsOfEpoch));
            }
            // UUID conversion works in the else also, this is a placeholder in case Ceylon gets a native UUID type.
            case(is UUID) {
                stmt.setObject(i, argument);
            }
            case(is ObjectArray<Object>) {
                stmt.setArray(i, connection.get().createSqlArray( argument, sqlArrayType(argument)));
            }
            case(is Array<String>) {
                setArray(i, argument, stmt);
            }
            case(is Array<Integer>) {
                setArray(i, argument, stmt);
            }
            case(is Array<UUID>) {
                setArray(i, argument, stmt);
            }
            case(is Array<Date>) {
                setArray(i, argument, stmt);
            }
            case(is Array<Boolean>) {
                setArray(i, argument, stmt);
            }
            case(is Array<Float>) {
                setArray(i, argument, stmt);
            }
            case(is Array<Time>) {
                setArray(i, argument, stmt);
            }
            case(is Array<DateTime>) {
                setArray(i, argument, stmt);
            }
            case(is Array<GregorianDate>) {
                setArray(i, argument, stmt);
            }
            case(is Array<TimeOfDay>) {
                setArray(i, argument, stmt);
            }
            //TODO reader, inputStream, byte array
            else { stmt.setObject(i,argument); }
            i++;
        }
    }

    void setArray<in ArrayType>(Integer position, Array<ArrayType> array, PreparedStatement stmt) 
            given ArrayType satisfies Object { 
        Type<ArrayType> type = `ArrayType`;
        
        String sqlArrayType;

        if (type.exactly(`String`)) {
            sqlArrayType = "varchar";
        } else if (type.exactly(`Integer`)) {
            sqlArrayType = "integer";
        } else if (type.exactly(`Decimal`)) {
            sqlArrayType = "decimal";
        } else if (type.exactly(`Boolean`)) {
            sqlArrayType = "boolean";
        } else if (type.exactly(`Float`)) {
            sqlArrayType = "float";
        } else if (type.exactly(`Date`)) {
            sqlArrayType = "date";
        } else if (type.exactly(`Time`)) {
            sqlArrayType = "time";
        } else if (type.exactly(`DateTime`)) {
            sqlArrayType = "timestamp";
        } else if (type.exactly(`GregorianDate`)) {
            sqlArrayType = "date";
        } else if (type.exactly(`TimeOfDay`)) {
            sqlArrayType = "time";
        // This is a special case not part of JDBCTypes but is supported by H2 and PostgreSQL.
        } else if (type.exactly(`UUID`)) {
            sqlArrayType = "uuid";
        } else {
            throw Exception("Unknown or unsupported array type for SQL array conversion: ``array``");
        }

        stmt.setArray(position, 
            connection.get().createSqlArray(createJavaObjectArray<Object>(array), sqlArrayType));
    }
    
    String sqlArrayType(ObjectArray<Object> argument) {
        if (argument.size > 0) {
            value first = argument.get(0);
            
            /*
             TODO:  Due to https://github.com/ceylon/ceylon-compiler/issues/1753, we need to check the type of 
                    first element of the array to determine the type of the database array.
                    There is also the question of non-standard behaviour for at least on database, PostgreSQL,
                    which will only accept lowercase versions of the types below.  (ex varchar, but not VARCHAR).
                    As well, JDK 8 has java.sql.JDBCType that contains these String values, but JDK 7 does not,
                    which is why the values below are hard-coded.
                    
             */
            switch (first)
            case (is String) {
                return "varchar";
            }
            case (is Integer) {
                return "integer";
            }
            case (is Boolean) {
                return "boolean";
            }
            case (is Decimal) {
                return "decimal";
            }
            case (is Float) {
                return "float";
            }
            case (is JDate) {
                if (is SqlTimestamp first) {
                    return "timestamp";
                } 
                else if (is SqlTime first) {
                    return "time";
                }
                else if (is SqlDate first) {
                    return "date";
                }
                else {
                    return "timestamp";
                }
            }
            case (is GregorianDateTime) {
                return "timestamp";
            }
            case (is GregorianDate) {
{'fraction_non_alphanumeric': 0.09039865244244806, 'fraction_numerical': 0.0014738910724312183, 'mean_word_length': 1.505098452883263, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 31, 'https://': 2, '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': '6247310', 'n_tokens_mistral': 3854, 'n_tokens_neox': 3601, 'n_words': 966}