From 8f9112896de4acd84def9b030500875aa6480c57 Mon Sep 17 00:00:00 2001 From: Keuin Date: Sat, 21 Oct 2023 20:21:33 +0800 Subject: fix `limit` and time not working; colored output --- .../com/keuin/blame/command/BlameBlockCommand.java | 4 +- .../java/com/keuin/blame/lookup/QueryExecutor.java | 62 +++++++++++++++------- .../java/com/keuin/blame/util/TablePrinter.java | 40 +++++++++----- 3 files changed, 71 insertions(+), 35 deletions(-) (limited to 'src/main/java/com') diff --git a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java index 695f9ad..b10a977 100644 --- a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java +++ b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java @@ -82,6 +82,8 @@ public class BlameBlockCommand { timeRange *= amplifier; if (timeRange < 0) return FAILED; + // convert to maximum unix millis + timeRange = System.currentTimeMillis() - timeRange * 1000L; } int amountLimit; @@ -95,7 +97,7 @@ public class BlameBlockCommand { amountLimit = BlameLimitCommand.DEFAULT_LOOKUP_LIMIT; } - Blame.queryExecutor.byBlockPos(world, x, y, z, (msg) -> + Blame.queryExecutor.byBlockPos(world, x, y, z, timeRange, amountLimit, (msg) -> context.getSource().sendFeedback(msg, false)); return SUCCESS; diff --git a/src/main/java/com/keuin/blame/lookup/QueryExecutor.java b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java index 8522de7..7acac0a 100644 --- a/src/main/java/com/keuin/blame/lookup/QueryExecutor.java +++ b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java @@ -1,13 +1,11 @@ package com.keuin.blame.lookup; -import com.clickhouse.client.ClickHouseParameterizedQuery; import com.clickhouse.data.ClickHouseFormat; -import com.clickhouse.data.value.ClickHouseLongValue; -import com.clickhouse.data.value.ClickHouseStringValue; import com.keuin.blame.data.enums.ActionType; import com.keuin.blame.util.DatabaseUtil; import com.keuin.blame.util.TablePrinter; import net.minecraft.text.LiteralText; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -23,27 +21,51 @@ public class QueryExecutor { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - public void byBlockPos(String world, long x, long y, long z, Consumer callback) { + private String getSQL( + String world, + long x, long y, long z, + long timeRange, + int maxCount + ) { + // ClickHouse driver's parameterized SQL generator is a piece of shit. + // I won't use that. Use string interpolation instead. + var sql = "select subject_id, object_id, action_type, ts"; + sql += " from " + escape(DatabaseUtil.DB_CONFIG.table()); + sql += " where subject_world=%s and object_x=%d and object_y=%d and object_z=%d".formatted( + escape(world), x, y, z + ); + if (timeRange > 0) { + sql += " and ts>=" + timeRange; + } + if (maxCount > 0) { + sql += " limit " + maxCount; + } + return sql; + } + + private static String escape(String s) { + return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"; + } + + public void byBlockPos( + String world, + long x, long y, long z, + long timeRange, + int maxCount, + Consumer callback + ) { var server = DatabaseUtil.getServer(); - var stmt = ClickHouseParameterizedQuery.of(server.getConfig(), - "select subject_id, object_id, action_type, ts " + - "from :table where subject_world=:world and object_x=:x and object_y=:y and object_z=:z"); + var sql = getSQL(world, x, y, z, timeRange, maxCount); + logger.info("SQL: " + sql); try (var client = DatabaseUtil.getClient(server)) { var resp = client.read(server) .format(ClickHouseFormat.RowBinaryWithNamesAndTypes) - .query(stmt) - .params( - ClickHouseStringValue.of(DatabaseUtil.DB_CONFIG.table()), - ClickHouseStringValue.of(world), - ClickHouseLongValue.of(x), - ClickHouseLongValue.of(y), - ClickHouseLongValue.of(z) - ) + .query(sql) .execute() .get(); int cnt = 0; - var sb = new StringBuilder(); - sb.append(String.format("Result for block at %s (%d, %d, %d):\n", world, x, y, z)); + MutableText t = new LiteralText(""); + t = t.append(String.format("Result for block at %s (%d, %d, %d):\n", world, x, y, z)); final int columns = 4; var table = new TablePrinter(columns); table.add(new TablePrinter.Row("Player", "Object", "Action", "Time")); @@ -57,9 +79,9 @@ public class QueryExecutor { table.add(new TablePrinter.Row(player, obj, actionType, time)); cnt++; } - sb.append(table); - sb.append(String.format("%d records in total.", cnt)); - callback.accept(new LiteralText(sb.toString())); + t = t.append(table.build()); + t = t.append(String.format("%d records in total.", cnt)); + callback.accept(t); } catch (Exception ex) { logger.error("Query block failed", ex); callback.accept(new LiteralText("Query failed: " + ex)); diff --git a/src/main/java/com/keuin/blame/util/TablePrinter.java b/src/main/java/com/keuin/blame/util/TablePrinter.java index 8b8eca3..e296e3c 100644 --- a/src/main/java/com/keuin/blame/util/TablePrinter.java +++ b/src/main/java/com/keuin/blame/util/TablePrinter.java @@ -1,7 +1,11 @@ package com.keuin.blame.util; +import net.minecraft.text.LiteralText; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -12,7 +16,8 @@ public class TablePrinter { private Function widthSupplier = (String::length); - private final int BORDER = 1; + private List colors = List.of( + Formatting.RED, Formatting.GOLD, Formatting.GREEN, Formatting.BLUE, Formatting.DARK_PURPLE); public TablePrinter(int columns) { this.columns = columns; @@ -30,8 +35,12 @@ public class TablePrinter { return this; } - @Override - public String toString() { + private Formatting getColor(int i) { + return colors.get(i % colors.size()); + } + + public Text build() { + final String SEPARATOR = "."; var maxWidths = new int[columns]; for (var r : rows) { for (int i = 0; i < r.row.length; i++) { @@ -39,27 +48,30 @@ public class TablePrinter { maxWidths[i] = Math.max(maxWidths[i], len); } } - var sb = new StringBuilder(); + MutableText t = new LiteralText(""); for (var r : rows) { var i = 0; for (String s : r.row) { - sb.append('|'); - sb.append(' '); - sb.append(s); + t = t.append(new LiteralText(SEPARATOR + " ")); + var n = maxWidths[i] - s.length(); if (n > 0) { - sb.append(" ".repeat(n)); + t = t.append(" ".repeat(n / 2)); + } + t = t.append(new LiteralText(s).formatted(getColor(i))); + if (n > 0) { + t = t.append(" ".repeat(n - n / 2)); } - sb.append(' '); + t = t.append(" "); i++; } if (r.row.length == 0) { - sb.append('|'); + t = t.append(SEPARATOR); } - sb.append("|"); - sb.append('\n'); + t = t.append(SEPARATOR); + t = t.append("\n"); } - return sb.toString(); + return t; } public static class Row { -- cgit v1.2.3