<commit_before>package org.sqldroid;

import java.lang.reflect.Constructor;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class SQLDroidConnection implements Connection {
    /**
    * A map to a single instance of a SQLiteDatabase per DB.
    */
    private static final Map<String, SQLiteDatabase> dbMap =
            new HashMap<String, SQLiteDatabase>();

    /**
    * A map from a connection to a SQLiteDatabase instance.
    * Used to track the use of each instance, and close the database when last conneciton is closed.
    */
    private static final Map<SQLDroidConnection, SQLiteDatabase> clientMap =
            new HashMap<SQLDroidConnection, SQLiteDatabase>();

    /** The Android sqlitedb. */
    private SQLiteDatabase sqlitedb;

    private boolean autoCommit = true;

    /** Will have the value 9 or greater the version of SQLException has the constructor:
     * SQLException(Throwable theCause) otherwise false.
     * API levels 9 or greater have this constructor.
     * If the value is positive and less than 9 then the SQLException does not have the constructor.
     * If the value is &lt; 0 then the capabilities of SQLException have not been determined.
     */
    protected static int sqlThrowable = -1;

    /** Connect to the database with the given url and properties.
     *
     * @param url the URL string, typically something like
     * "jdbc:sqlite:/data/data/your-package/databasefilename" so for example:
     *  "jdbc:sqlite:/data/data/org.sqldroid.examples/databases/sqlite.db"
     * @param info Properties object with options.  Supported options are "timeout", "retry", and "shared".
     */
    public SQLDroidConnection(String url, Properties info) throws SQLException {
        Log.v("SQLDroidConnection: " + Thread.currentThread().getId() + " \"" + Thread.currentThread().getName() + "\" " + this);
        Log.v("New sqlite jdbc from url '" + url + "', " + "'" + info + "'");

        // Make a filename from url
        String dbQname;
        if(url.startsWith(SQLDroidDriver.xerialPrefix)) {
            dbQname = url.substring(SQLDroidDriver.xerialPrefix.length());
        } else {
            // there does not seem to be any possibility of error handling.
            // So we could check that the url starts with SQLDroidDriver.sqldroidPrefix
            // but if it doesn't there's nothing we can do (no Exception is specified)
            // so it has to be assumed that the URL is valid when passed to this method.
            dbQname = url.substring(SQLDroidDriver.sqldroidPrefix.length());
        }
        long timeout = 0;  // default to no retries to be consistent with other JDBC implemenations.
        long retryInterval = 50; // this was 1000 in the original code.  1 second is too long for each loop.
        int queryPart = dbQname.indexOf('?');

        // if there's a query part, we accept "timeout=xxx" and "retry=yyy"
        if ( queryPart > 0 ) {
            dbQname = dbQname.substring(0, queryPart);
            String options = dbQname.substring(queryPart);
            while (options.length() > 0) {
                int optionEnd = options.indexOf('&');
                if (optionEnd == -1) {
                    optionEnd = options.length();
                }
                int equals = options.lastIndexOf('=', optionEnd);
                String optionName = options.substring(0, equals).trim();
                String optionValueString = options.substring(equals+1, optionEnd).trim();
                long optionValue;
                try {
                    optionValue = Long.parseLong(optionValueString);
                    if (optionName.equals("timeout")) {
                        timeout = optionValue;
                    } else if (optionName.equals("retry")) {
                        timeout = optionValue;
                        retryInterval = optionValue;
                    }
                    Log.v("Timeout: " + timeout);
                } catch ( NumberFormatException nfe ) {
                    // print and ignore
                    Log.e("Error Parsing URL \"" + url + "\" Timeout String \"" + optionValueString + "\" is not a valid long", nfe);
                }
                options = options.substring(optionEnd + 1);
            }
        }
        Log.v("opening database " + dbQname);
        int flags = android.database.sqlite.SQLiteDatabase.CREATE_IF_NECESSARY
                | android.database.sqlite.SQLiteDatabase.OPEN_READWRITE
                | android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS;
        if ( info != null ) {
            if ( info.getProperty(SQLDroidDriver.DATABASE_FLAGS) != null ) {

                try {
                    flags = Integer.parseInt(info.getProperty(SQLDroidDriver.DATABASE_FLAGS));
                } catch ( NumberFormatException nfe ) {
                    Log.e("Error Parsing DatabaseFlags \"" + info.getProperty(SQLDroidDriver.DATABASE_FLAGS) + " not a number ", nfe);
                }
            } else if ( info != null && info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS) != null ) {
                try {
                    int extraFlags = Integer.parseInt(info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS));
                    flags |= extraFlags;
                } catch ( NumberFormatException nfe ) {
                    Log.e("Error Parsing DatabaseFlags \"" + info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS) + " not a number ", nfe);
                }
            }
        }
        synchronized(dbMap) {
            sqlitedb = dbMap.get(dbQname);
            if (sqlitedb == null) {
                Log.i("SQLDroidConnection: " + Thread.currentThread().getId() + " \"" + Thread.currentThread().getName() + "\" " + this + " Opening new database: " + dbQname);
                sqlitedb = new SQLiteDatabase(dbQname, timeout, retryInterval, flags);
                dbMap.put(dbQname, sqlitedb);
                clientMap.put(this, sqlitedb);
            }
        }
    }

    /** Returns the delegate SQLiteDatabase. */
    public SQLiteDatabase getDb() {
        return sqlitedb;
    }

    @Override
    public void clearWarnings() throws SQLException {
    }

    /** This will create and return an exception.  For API levels less than 9 this will return
     * a SQLDroidSQLException, for later APIs it will return a SQLException.
     */
    public static SQLException chainException(android.database.SQLException sqlException) {
        if ( sqlThrowable < 0 || sqlThrowable >= 9 ) {
            try {<commit_msg>new connections always have to be added to clientMap, not only if the database is openend<commit_after>package org.sqldroid;

import java.lang.reflect.Constructor;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class SQLDroidConnection implements Connection {
    /**
    * A map to a single instance of a SQLiteDatabase per DB.
    */
    private static final Map<String, SQLiteDatabase> dbMap =
            new HashMap<String, SQLiteDatabase>();

    /**
    * A map from a connection to a SQLiteDatabase instance.
    * Used to track the use of each instance, and close the database when last conneciton is closed.
    */
    private static final Map<SQLDroidConnection, SQLiteDatabase> clientMap =
            new HashMap<SQLDroidConnection, SQLiteDatabase>();

    /** The Android sqlitedb. */
    private SQLiteDatabase sqlitedb;

    private boolean autoCommit = true;

    /** Will have the value 9 or greater the version of SQLException has the constructor:
     * SQLException(Throwable theCause) otherwise false.
     * API levels 9 or greater have this constructor.
     * If the value is positive and less than 9 then the SQLException does not have the constructor.
     * If the value is &lt; 0 then the capabilities of SQLException have not been determined.
     */
    protected static int sqlThrowable = -1;

    /** Connect to the database with the given url and properties.
     *
     * @param url the URL string, typically something like
     * "jdbc:sqlite:/data/data/your-package/databasefilename" so for example:
     *  "jdbc:sqlite:/data/data/org.sqldroid.examples/databases/sqlite.db"
     * @param info Properties object with options.  Supported options are "timeout", "retry", and "shared".
     */
    public SQLDroidConnection(String url, Properties info) throws SQLException {
        Log.v("SQLDroidConnection: " + Thread.currentThread().getId() + " \"" + Thread.currentThread().getName() + "\" " + this);
        Log.v("New sqlite jdbc from url '" + url + "', " + "'" + info + "'");

        // Make a filename from url
        String dbQname;
        if(url.startsWith(SQLDroidDriver.xerialPrefix)) {
            dbQname = url.substring(SQLDroidDriver.xerialPrefix.length());
        } else {
            // there does not seem to be any possibility of error handling.
            // So we could check that the url starts with SQLDroidDriver.sqldroidPrefix
            // but if it doesn't there's nothing we can do (no Exception is specified)
            // so it has to be assumed that the URL is valid when passed to this method.
            dbQname = url.substring(SQLDroidDriver.sqldroidPrefix.length());
        }
        long timeout = 0;  // default to no retries to be consistent with other JDBC implemenations.
        long retryInterval = 50; // this was 1000 in the original code.  1 second is too long for each loop.
        int queryPart = dbQname.indexOf('?');

        // if there's a query part, we accept "timeout=xxx" and "retry=yyy"
        if ( queryPart > 0 ) {
            dbQname = dbQname.substring(0, queryPart);
            String options = dbQname.substring(queryPart);
            while (options.length() > 0) {
                int optionEnd = options.indexOf('&');
                if (optionEnd == -1) {
                    optionEnd = options.length();
                }
                int equals = options.lastIndexOf('=', optionEnd);
                String optionName = options.substring(0, equals).trim();
                String optionValueString = options.substring(equals+1, optionEnd).trim();
                long optionValue;
                try {
                    optionValue = Long.parseLong(optionValueString);
                    if (optionName.equals("timeout")) {
                        timeout = optionValue;
                    } else if (optionName.equals("retry")) {
                        timeout = optionValue;
                        retryInterval = optionValue;
                    }
                    Log.v("Timeout: " + timeout);
                } catch ( NumberFormatException nfe ) {
                    // print and ignore
                    Log.e("Error Parsing URL \"" + url + "\" Timeout String \"" + optionValueString + "\" is not a valid long", nfe);
                }
                options = options.substring(optionEnd + 1);
            }
        }
        Log.v("opening database " + dbQname);
        int flags = android.database.sqlite.SQLiteDatabase.CREATE_IF_NECESSARY
                | android.database.sqlite.SQLiteDatabase.OPEN_READWRITE
                | android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS;
        if ( info != null ) {
            if ( info.getProperty(SQLDroidDriver.DATABASE_FLAGS) != null ) {

                try {
                    flags = Integer.parseInt(info.getProperty(SQLDroidDriver.DATABASE_FLAGS));
                } catch ( NumberFormatException nfe ) {
                    Log.e("Error Parsing DatabaseFlags \"" + info.getProperty(SQLDroidDriver.DATABASE_FLAGS) + " not a number ", nfe);
                }
            } else if ( info != null && info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS) != null ) {
                try {
                    int extraFlags = Integer.parseInt(info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS));
                    flags |= extraFlags;
                } catch ( NumberFormatException nfe ) {
                    Log.e("Error Parsing DatabaseFlags \"" + info.getProperty(SQLDroidDriver.ADDITONAL_DATABASE_FLAGS) + " not a number ", nfe);
                }
            }
        }
        synchronized(dbMap) {
            sqlitedb = dbMap.get(dbQname);
            if (sqlitedb == null) {
                Log.i("SQLDroidConnection: " + Thread.currentThread().getId() + " \"" + Thread.currentThread().getName() + "\" " + this + " Opening new database: " + dbQname);
                sqlitedb = new SQLiteDatabase(dbQname, timeout, retryInterval, flags);
                dbMap.put(dbQname, sqlitedb);
            }
            clientMap.put(this, sqlitedb);
        }
    }

    /** Returns the delegate SQLiteDatabase. */
    public SQLiteDatabase getDb() {
        return sqlitedb;
    }

    @Override
    public void clearWarnings() throws SQLException {
    }

    /** This will create and return an exception.  For API levels less than 9 this will return
     * a SQLDroidSQLException, for later APIs it will return a SQLException.
     */
    public static SQLException chainException(android.database.SQLException sqlException) {
        if ( sqlThrowable < 0 || sqlThrowable >= 9 ) {
            try {
{'fraction_non_alphanumeric': 0.09739069111424542, 'fraction_numerical': 0.003244005641748942, 'mean_word_length': 2.2141885766092475, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, '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': '129736', 'n_tokens_mistral': 3802, 'n_tokens_neox': 3716, 'n_words': 1087}