summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-05-21 13:55:25 -0400
committerJune McEnroe <june@causal.agency>2020-05-21 13:55:25 -0400
commita6e1441bec7609a121e442bb75100a61a43f0258 (patch)
tree335023069987fb23d94efcb121c86730458a81ca
parentExpand and clarify documentation (diff)
downloadlitterbox-a6e1441bec7609a121e442bb75100a61a43f0258.tar.gz
litterbox-a6e1441bec7609a121e442bb75100a61a43f0258.zip
Do not sort results in scoop by default
The -s flag now enables sorting (-g and -l also imply it), while the -S
flag now launches the sqlite3 shell.

Avoiding sorting by default is much faster and less memory-intensive as
it doesn't require loading the entire result set into memory at once.
-rw-r--r--scoop.125
-rw-r--r--scoop.c63
2 files changed, 44 insertions, 44 deletions
diff --git a/scoop.1 b/scoop.1
index e4eedfd..fddd749 100644
--- a/scoop.1
+++ b/scoop.1
@@ -8,7 +8,7 @@
 .
 .Sh SYNOPSIS
 .Nm
-.Op Fl Lgpqv
+.Op Fl Lgpqsv
 .Op Fl D Ar date
 .Op Fl F Ar format
 .Op Fl N Ar network
@@ -27,7 +27,7 @@
 .Op Fl w Ar expr
 .Op Ar search
 .Nm
-.Fl s
+.Fl S
 .
 .Sh DESCRIPTION
 The
@@ -37,10 +37,6 @@ utility queries the
 IRC log database,
 matching events on the intersection
 of the criteria specified in arguments.
-With no arguments,
-all events in the database are matched.
-Events are output in order
-from oldest to newest.
 .
 .Pp
 If standard output is a terminal,
@@ -78,6 +74,12 @@ Output timestamps in local time.
 Match events from
 .Ar network .
 .
+.It Fl S
+Instead of performing a query,
+launch the
+.Xr sqlite3 1
+shell for the database.
+.
 .It Fl T Ar target
 Match events
 .Po
@@ -131,6 +133,8 @@ otherwise.
 .
 .It Fl g
 Group events by network and channel or query name.
+Implies
+.Fl s .
 .
 .It Fl h Ar host
 Match events from users with the hostname
@@ -139,6 +143,8 @@ Match events from users with the hostname
 .It Fl l Ar limit
 Limit the number of events matched,
 ordered by most recent.
+Implies
+.Fl s .
 .
 .It Fl m Ar regexp
 Match events with messages
@@ -158,10 +164,9 @@ Match only events from channels.
 Match only events from queries.
 .
 .It Fl s
-Instead of performing a query,
-launch the
-.Xr sqlite3 1
-shell for the database.
+Sort the results from oldest to newest.
+By default events are output
+in the order they appear in the database.
 .
 .It Fl t Ar type
 Match events of
diff --git a/scoop.c b/scoop.c
index 4c4ee35..3ab6e5d 100644
--- a/scoop.c
+++ b/scoop.c
@@ -295,7 +295,7 @@ static void regexp(sqlite3_context *ctx, int n, sqlite3_value *args[]) {
 	sqlite3_result_int(ctx, !error);
 }
 
-static const char *Inner = SQL(
+static const char *Query = SQL(
 	SELECT
 		contexts.network,
 		contexts.name AS context,
@@ -343,16 +343,6 @@ static const char *Limit = SQL(
 	LIMIT coalesce(:limit, -1)
 );
 
-static const char *Outer = SQL(
-	SELECT * FROM results
-	ORDER BY time, event
-);
-
-static const char *Group = SQL(
-	SELECT * FROM results
-	ORDER BY network, context, time, event
-);
-
 static const char *TypeNames[] = {
 #define X(id, name) [id] = name,
 	ENUM_TYPE
@@ -395,35 +385,38 @@ int main(int argc, char *argv[]) {
 
 	char *path = NULL;
 	bool shell = false;
-	bool group = false;
 	Format *format = (tty ? formatColor : formatPlain);
 
-	int n = 0;
-	struct Bind binds[argc];
+	bool sort = false;
+	bool group = false;
 	const char *search = NULL;
 	const char *where = NULL;
+	const char *limit = NULL;
 
-	const char *Opts = "D:F:LN:T:a:b:c:d:f:gh:l:m:n:pqst:u:vw:";
+	int n = 0;
+	struct Bind binds[argc];
+	const char *Opts = "D:F:LN:ST:a:b:c:d:f:gh:l:m:n:pqst:u:vw:";
 	for (int opt; 0 < (opt = getopt(argc, argv, Opts));) {
 		switch (opt) {
 			break; case 'D': binds[n++] = Bind(":date", optarg, 0);
 			break; case 'F': binds[n++] = Bind(":format", optarg, 0);
 			break; case 'L': binds[n++] = Bind(":local", NULL, 1);
 			break; case 'N': binds[n++] = Bind(":network", optarg, 0);
+			break; case 'S': shell = true;
 			break; case 'T': binds[n++] = Bind(":target", optarg, 0);
 			break; case 'a': binds[n++] = Bind(":after", optarg, 0);
 			break; case 'b': binds[n++] = Bind(":before", optarg, 0);
 			break; case 'c': binds[n++] = Bind(":context", optarg, 0);
 			break; case 'd': path = optarg;
 			break; case 'f': format = parseFormat(optarg);
-			break; case 'g': group = true;
+			break; case 'g': group = true; sort = true;
 			break; case 'h': binds[n++] = Bind(":host", optarg, 0);
-			break; case 'l': binds[n++] = Bind(":limit", optarg, 0);
+			break; case 'l': limit = optarg; sort = true;
 			break; case 'm': binds[n++] = Bind(":regexp", optarg, 0);
 			break; case 'n': binds[n++] = Bind(":nick", optarg, 0);
 			break; case 'p': binds[n++] = Bind(":query", NULL, 0);
 			break; case 'q': binds[n++] = Bind(":query", NULL, 1);
-			break; case 's': shell = true;
+			break; case 's': sort = true;
 			break; case 't': binds[n++] = Bind(":type", NULL, parseType(optarg));
 			break; case 'u': binds[n++] = Bind(":user", optarg, 0);
 			break; case 'v': verbose = true;
@@ -450,25 +443,27 @@ int main(int argc, char *argv[]) {
 		err(EX_UNAVAILABLE, "sqlite3");
 	}
 
-	int len;
 	char sql[4096];
-	if (search) {
-		len = snprintf(
-			sql, sizeof(sql),
-			"WITH results AS (%s AND %s AND %s %s) %s;",
-			Inner, Search, (where ? where : "true"), Limit,
-			(group ? Group : Outer)
-		);
-		binds[n++] = Bind(":search", search, 0);
-	} else {
-		len = snprintf(
-			sql, sizeof(sql),
-			"WITH results AS (%s AND %s %s) %s;",
-			Inner, (where ? where : "true"), Limit, (group ? Group : Outer)
-		);
-	}
+	int len = snprintf(
+		sql, sizeof(sql),
+		SQL(
+			WITH results AS (
+				%s AND %s AND %s
+				%s
+			)
+			SELECT * FROM results %s %s %s;
+		),
+		Query, (search ? Search : "true"), (where ? where : "true"),
+		(limit ? Limit : ""),
+		(sort ? "ORDER BY" : ""),
+		(group ? "network, context," : ""),
+		(sort ? "time, event" : "")
+	);
 	assert((size_t)len < sizeof(sql));
 
+	if (search) binds[n++] = Bind(":search", search, 0);
+	if (limit) binds[n++] = Bind(":limit", limit, 0);
+
 	sqlite3_stmt *stmt = dbPrepare(sql);
 	for (int i = 0; i < n; ++i) {
 		if (binds[i].text) {