<commit_before>package space.pxls.data;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.exceptions.NoResultsException;
import space.pxls.App;
import space.pxls.server.Packet;
import space.pxls.user.Role;
import space.pxls.user.User;

import java.io.Closeable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.Math.toIntExact;

public class Database implements Closeable {
    private final DBI dbi;
    
    private Map<Thread,Database_handle> handles = new ConcurrentHashMap<>();;

    public Database() {
        try {
            Class.forName("org.mariadb.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(App.getConfig().getString("database.url"));
        config.setUsername(App.getConfig().getString("database.user"));
        config.setPassword(App.getConfig().getString("database.pass"));
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("allowMultiQueries", "true");
        config.setMaximumPoolSize(200); // this is plenty, the websocket uses 32

        dbi = new DBI(new HikariDataSource(config));
        

        getHandle().createPixelsTable();
        getHandle().createUsersTable();
        getHandle().createSessionsTable();
        getHandle().createAdminLogTable();
        getHandle().createReportsTable();
    }

    private DAO getHandle() {
        Thread t = Thread.currentThread();
        Database_handle h = handles.get(t);
        if (h != null) {
            h.last_use = System.currentTimeMillis();
            return h.dao;
        }
        h = new Database_handle();
        h.dao = dbi.open(DAO.class);
        h.last_use = System.currentTimeMillis();
        System.out.println("Creating new mariadb connection...");
        System.out.println(h);
        handles.put(t, h);
        return h.dao;
    }

    public void cleanup() {
        for (Thread t : handles.keySet()) {
            Database_handle d = handles.get(t);
            if (d.last_use + (1000 * 60 * 5)  < System.currentTimeMillis()) {
                // ok destroy it
                System.out.println("Destroying mariadb connection...");
                System.out.println(d.dao);
                d.dao.close();
                handles.remove(t);
            }
        }
    }

    public void placePixel(int x, int y, int color, User who, boolean mod_action) {
        getHandle().putPixel(x, y, (byte) color, who != null ? who.getId() : null, mod_action);
    }

    public void updateUserTime(int uid, long seconds) {
        getHandle().updateUserTime(uid, seconds);
    }

    public DBPixelPlacement getPixelAt(int x, int y) {
        return getHandle().getPixel(x, y);
    }

    public DBPixelPlacementUser getPixelAtUser(int x, int y) {
        return getHandle().getPixelUser(x, y);
    }

    public DBPixelPlacement getPixelByID(int id) {
        return getHandle().getPixel(id);
    }

    // returns ids of all pixels that should be rolled back and the DBPixelPlacement for all pixels to rollback to
    // DBRollbackPixel is (DBPixelPlacement and fromID) so it has all the info needed to rollback
    public List<DBRollbackPixel> getRollbackPixels(User who, int fromSeconds) {
        Handle h = dbi.open();
        List<Map<String, Object>> output = h
                .createQuery("SELECT id, secondary_id FROM pixels WHERE most_recent AND who = :who AND (time + INTERVAL :seconds SECOND > NOW())")
                .bind("who", who.getId())
                .bind("seconds", fromSeconds)<commit_msg>Fix for nuke command<commit_after>package space.pxls.data;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.exceptions.NoResultsException;
import space.pxls.App;
import space.pxls.server.Packet;
import space.pxls.user.Role;
import space.pxls.user.User;

import java.io.Closeable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.Math.toIntExact;

public class Database implements Closeable {
    private final DBI dbi;
    
    private Map<Thread,Database_handle> handles = new ConcurrentHashMap<>();;

    public Database() {
        try {
            Class.forName("org.mariadb.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(App.getConfig().getString("database.url"));
        config.setUsername(App.getConfig().getString("database.user"));
        config.setPassword(App.getConfig().getString("database.pass"));
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("allowMultiQueries", "true");
        config.setMaximumPoolSize(200); // this is plenty, the websocket uses 32

        dbi = new DBI(new HikariDataSource(config));
        

        getHandle().createPixelsTable();
        getHandle().createUsersTable();
        getHandle().createSessionsTable();
        getHandle().createAdminLogTable();
        getHandle().createReportsTable();
    }

    private DAO getHandle() {
        Thread t = Thread.currentThread();
        Database_handle h = handles.get(t);
        if (h != null) {
            h.last_use = System.currentTimeMillis();
            return h.dao;
        }
        h = new Database_handle();
        h.dao = dbi.open(DAO.class);
        h.last_use = System.currentTimeMillis();
        System.out.println("Creating new mariadb connection...");
        System.out.println(h);
        handles.put(t, h);
        return h.dao;
    }

    public void cleanup() {
        for (Thread t : handles.keySet()) {
            Database_handle d = handles.get(t);
            if (d.last_use + (1000 * 60 * 5)  < System.currentTimeMillis()) {
                // ok destroy it
                System.out.println("Destroying mariadb connection...");
                System.out.println(d.dao);
                d.dao.close();
                handles.remove(t);
            }
        }
    }

    public void placePixel(int x, int y, int color, User who, boolean mod_action) {
        getHandle().putPixel(x, y, (byte) color, who != null ? who.getId() : 0, mod_action);
    }

    public void updateUserTime(int uid, long seconds) {
        getHandle().updateUserTime(uid, seconds);
    }

    public DBPixelPlacement getPixelAt(int x, int y) {
        return getHandle().getPixel(x, y);
    }

    public DBPixelPlacementUser getPixelAtUser(int x, int y) {
        return getHandle().getPixelUser(x, y);
    }

    public DBPixelPlacement getPixelByID(int id) {
        return getHandle().getPixel(id);
    }

    // returns ids of all pixels that should be rolled back and the DBPixelPlacement for all pixels to rollback to
    // DBRollbackPixel is (DBPixelPlacement and fromID) so it has all the info needed to rollback
    public List<DBRollbackPixel> getRollbackPixels(User who, int fromSeconds) {
        Handle h = dbi.open();
        List<Map<String, Object>> output = h
                .createQuery("SELECT id, secondary_id FROM pixels WHERE most_recent AND who = :who AND (time + INTERVAL :seconds SECOND > NOW())")
                .bind("who", who.getId())
                .bind("seconds", fromSeconds)
{'fraction_non_alphanumeric': 0.119358074222668, 'fraction_numerical': 0.005641925777331996, 'mean_word_length': 2.6896392229417208, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '1680194', 'n_tokens_mistral': 2410, 'n_tokens_neox': 2355, 'n_words': 532}