.
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/planner/BloomFilterTempTable.java b/src/main/java/com/teragrep/pth_06/planner/BloomFilterTempTable.java
new file mode 100644
index 00000000..c38a1c87
--- /dev/null
+++ b/src/main/java/com/teragrep/pth_06/planner/BloomFilterTempTable.java
@@ -0,0 +1,236 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner;
+
+import com.teragrep.blf_01.Token;
+import org.apache.spark.util.sketch.BloomFilter;
+import org.jooq.*;
+import org.jooq.impl.DSL;
+import org.jooq.types.ULong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Set;
+
+import static com.teragrep.pth_06.jooq.generated.bloomdb.Bloomdb.BLOOMDB;
+import static org.jooq.impl.SQLDataType.BIGINTUNSIGNED;
+
+/**
+ * Class to represent a SQL Temp. Filled with bloom filters of all the parent tables records filter types. Search term
+ * token set parameter is inserted into each filter. Used to generate bloommatch condition that compares this filter
+ * column against parent table filter column row by row using bloommatch UDF.
+ *
+ * Parent Table Schema:
+ *
+ * - id PK
+ * - partition_id FK journaldb.logfile.id
+ * - filter_type_id FK bloomdb.filtertype.id
+ * - filter - bloomfilter byte array
+ *
+ *
+ * Temp Table Schema:
+ *
+ * - id PK
+ * - termId - count of the current search term
+ * - typeId FK bloomdb.filtertype.id
+ * - filter - bloomfilter bytes with only the search term token set inserted
+ *
+ * Parent table create table example:
+ *
+ *
+ * CREATE TABLE `example` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `partition_id` bigint(20) unsigned NOT NULL, `filter_type_id` bigint(20) unsigned NOT NULL, `filter` longblob NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `partition_id` (`partition_id`), KEY `example_ibfk_1` (`filter_type_id`), CONSTRAINT `example_ibfk_1` FOREIGN KEY (`filter_type_id`) REFERENCES `filtertype` (`id`) ON DELETE CASCADE, CONSTRAINT `example_ibfk_2` FOREIGN KEY (`partition_id`) REFERENCES `journaldb`.`logfile` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=54787 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
+ *
+ *
+ * @see com.teragrep.pth_06.jooq.generated.bloomdb.Bloomdb
+ */
+public final class BloomFilterTempTable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BloomFilterTempTable.class);
+
+ private final DSLContext ctx;
+ private final Table> parentTable;
+ private final long bloomTermId;
+ private final Set tokenSet;
+
+ public BloomFilterTempTable(DSLContext ctx, Table> parentTable, long bloomTermId, Set tokenSet) {
+ this.ctx = ctx;
+ this.parentTable = parentTable;
+ this.bloomTermId = bloomTermId;
+ this.tokenSet = tokenSet;
+ }
+
+ private void create() {
+ final Table table = DSL.table(DSL.name(("term_" + bloomTermId + "_" + parentTable.getName())));
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Creating temporary table <{}>", table.getName());
+ }
+ ctx.dropTemporaryTableIfExists(table).execute();
+ final String sql = "create temporary table " + table.getName()
+ + "(id bigint auto_increment primary key, term_id bigint, type_id bigint, filter longblob, unique key "
+ + table.getName() + "_unique_key (term_id, type_id))";
+ final Query createQuery = ctx.query(sql);
+ createQuery.execute();
+ final Index typeIndex = DSL.index(DSL.name(table.getName() + "_ix_type_id"));
+ final CreateIndexIncludeStep indexStep = ctx
+ .createIndex(typeIndex)
+ .on(table, DSL.field("type_id", BIGINTUNSIGNED.nullable(false)));
+ LOGGER.trace("BloomFilterTempTable create index <{}>", indexStep);
+ indexStep.execute();
+ }
+
+ /**
+ * Inserts a filter for each filtertype inside parent table records. Filter is filled with search term token set,
+ * filter size is selected using parent table filtertype expected and fpp values.
+ */
+ private void insertFilters() {
+ final Table> joined = parentTable
+ .join(BLOOMDB.FILTERTYPE)
+ .on(BLOOMDB.FILTERTYPE.ID.eq((Field) parentTable.field("filter_type_id")));
+ final Table table = DSL.table(DSL.name(("term_" + bloomTermId + "_" + parentTable.getName())));
+ final Field expectedField = DSL.field(DSL.name(table.getName(), "expectedElements"), ULong.class);
+ final Field fppField = DSL.field(DSL.name(table.getName(), "targetFpp"), Double.class);
+ final SelectField>[] resultFields = {
+ BLOOMDB.FILTERTYPE.ID,
+ joined.field("expectedElements").as(expectedField),
+ joined.field("targetFpp").as(fppField)
+ };
+ // Fetch filtertype values
+ final Result records = ctx
+ .select(resultFields)
+ .from(joined)
+ .groupBy(joined.field("filter_type_id"))
+ .fetch();
+ if (records.isEmpty()) {
+ throw new RuntimeException("Parent table was empty");
+ }
+ for (Record record : records) {
+ insertFilterFromRecord(record, expectedField, fppField);
+ }
+ }
+
+ private void insertFilterFromRecord(
+ final Record record,
+ final Field expectedField,
+ final Field fppField
+ ) {
+ final ULong filterTypeId = record.getValue(BLOOMDB.FILTERTYPE.ID); // filter_type_id
+ final ULong expected = record.getValue(expectedField); // expectedElements
+ final Double fpp = record.getValue(fppField); // targetFpp
+ final BloomFilter filter = BloomFilter.create(expected.longValue(), fpp);
+ tokenSet.forEach(token -> filter.put(token.toString()));
+ final ByteArrayOutputStream filterBAOS = new ByteArrayOutputStream();
+ try {
+ filter.writeTo(filterBAOS);
+ filterBAOS.close();
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException(new IOException("Error writing filter bytes: " + e.getMessage()));
+ }
+ final Table table = DSL.table(DSL.name(("term_" + bloomTermId + "_" + parentTable.getName())));
+ final Field>[] insertFields = {
+ DSL.field("term_id", BIGINTUNSIGNED.nullable(false)),
+ DSL.field("type_id", BIGINTUNSIGNED.nullable(false)),
+ DSL.field(DSL.name(table.getName(), "filter"), byte[].class)
+ };
+ ctx
+ .insertInto(table)
+ .columns(insertFields)
+ .values(DSL.val(bloomTermId, ULong.class), DSL.val(filterTypeId, ULong.class), DSL.val(filterBAOS.toByteArray(), byte[].class)).execute();
+ }
+
+ /**
+ * Generates a condition that returns true if this temp tables search term tokens might be contained in the parent
+ * table or parent table bloom filter is null. Selects to same sized filter from the temp table for each parent
+ * table row.
+ *
+ * expects the user defined function 'bloommatch' to be present to compare filter bytes. expects the parent table to
+ * be joined in the query that uses the generated Condition.
+ *
+ * @return Condition - generated condition
+ */
+ public Condition generateCondition() {
+ create();
+ insertFilters();
+ final Table table = DSL.table(DSL.name(("term_" + bloomTermId + "_" + parentTable.getName())));
+ final Field termIdField = DSL.field("term_id", BIGINTUNSIGNED.nullable(false));
+ final Field typeIdField = DSL.field("type_id", BIGINTUNSIGNED.nullable(false));
+ final Field filterField = DSL.field(DSL.name(table.getName(), "filter"), byte[].class);
+ final SelectConditionStep> selectFilterStep = DSL
+ .select(filterField)
+ .from(table)
+ .where(termIdField.eq(ULong.valueOf(bloomTermId)))
+ .and(typeIdField.eq((Field) parentTable.field("filter_type_id")));
+ final Field filterColumn = selectFilterStep.asField();
+ final Condition filterFieldCondition = DSL
+ .function("bloommatch", Boolean.class, filterColumn, parentTable.field("filter"))
+ .eq(true);
+ // null check allows SQL to optimize query
+ final Condition notNullCondition = parentTable.field("filter").isNotNull();
+ return filterFieldCondition.and(notNullCondition);
+ }
+
+ /**
+ * Equal only if all object parameters are same value and the instances of DSLContext are same
+ *
+ * @param object object compared against
+ * @return true if all object is same class, object fields are equal and DSLContext is same instance
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (this == object)
+ return true;
+ if (object == null)
+ return false;
+ if (object.getClass() != this.getClass())
+ return false;
+ final BloomFilterTempTable cast = (BloomFilterTempTable) object;
+ return this.parentTable.equals(cast.parentTable) && this.ctx == cast.ctx && // equal only if same instance of DSLContext
+ this.bloomTermId == cast.bloomTermId && this.tokenSet.equals(cast.tokenSet);
+ }
+}
diff --git a/src/main/java/com/teragrep/pth_06/planner/PatternMatch.java b/src/main/java/com/teragrep/pth_06/planner/PatternMatch.java
new file mode 100644
index 00000000..0337a95c
--- /dev/null
+++ b/src/main/java/com/teragrep/pth_06/planner/PatternMatch.java
@@ -0,0 +1,137 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner;
+
+import com.teragrep.blf_01.Token;
+import com.teragrep.blf_01.Tokenizer;
+
+import org.jooq.Condition;
+import org.jooq.DSLContext;
+import org.jooq.Field;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+import org.jooq.types.ULong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.teragrep.pth_06.jooq.generated.bloomdb.Bloomdb.BLOOMDB;
+
+public class PatternMatch {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PatternMatch.class);
+
+ private final DSLContext ctx;
+ private final String input;
+
+ public PatternMatch(DSLContext ctx, String input) {
+ this.ctx = ctx;
+ this.input = input;
+ }
+
+ /**
+ * Finds all non-empty Tables from bloomdb that are not filtertype and that match regex condition from token set
+ * Note: Table records are not fetched fully
+ *
+ * @return List of tables that matched condition and were not empty
+ */
+ public List> toList() {
+ final Set tokenSet = tokenSet();
+ Condition patternCondition = DSL.noCondition();
+ // tokens = ['one, 'two'] -> ('one' regex like BLOOMDB.FILTERTYPE.PATTERN).or('two' regex like BLOOMDB.FILTERTYPE.PATTERN)
+ for (Token token : tokenSet) {
+ Field tokenStringField = DSL.val(token.toString());
+ patternCondition = patternCondition.or(tokenStringField.likeRegex(BLOOMDB.FILTERTYPE.PATTERN));
+ }
+ final Condition finalPatternCondition = patternCondition;
+ // SQL metadata
+ final List> tables = ctx
+ .meta()
+ .filterSchemas(s -> s.equals(BLOOMDB)) // select bloomdb
+ .filterTables(t -> !t.equals(BLOOMDB.FILTERTYPE)) // remove filtertype table
+ .filterTables(t -> ctx.select((Field) t.field("id"))// for each remaining table
+ .from(t)
+ .leftJoin(BLOOMDB.FILTERTYPE)// join filtertype to access patterns
+ .on(BLOOMDB.FILTERTYPE.ID.eq((Field) t.field("filter_type_id")))
+ .where(finalPatternCondition)// select tables that match pattern condition
+ .limit(1)// limit 1 since we are checking only if table is not empty
+ .fetch()
+ .isNotEmpty() // select table if not empty
+ )
+ .getTables();
+ LOGGER.debug("Table(s) with a pattern match <{}>", tables);
+ return tables;
+ }
+
+ public Set tokenSet() {
+ return new HashSet<>(
+ new Tokenizer(0).tokenize(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)))
+ );
+ }
+
+ /**
+ * Equal only if all values are equal and same instance of DSLContext
+ *
+ * @param object object compared against
+ * @return true if all object is same class, object fields are equal and DSLContext is same instance
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (this == object)
+ return true;
+ if (object == null)
+ return false;
+ if (object.getClass() != this.getClass())
+ return false;
+ final PatternMatch cast = (PatternMatch) object;
+ return this.input.equals(cast.input) && this.ctx == cast.ctx; // only same instance of DSLContext is equal
+ }
+}
diff --git a/src/main/java/com/teragrep/pth_06/planner/StreamDBClient.java b/src/main/java/com/teragrep/pth_06/planner/StreamDBClient.java
index c3b880d0..e733ca25 100644
--- a/src/main/java/com/teragrep/pth_06/planner/StreamDBClient.java
+++ b/src/main/java/com/teragrep/pth_06/planner/StreamDBClient.java
@@ -45,16 +45,12 @@
*/
package com.teragrep.pth_06.planner;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UncheckedIOException;
import java.sql.*;
import java.time.Instant;
+import java.util.Set;
import com.teragrep.pth_06.config.Config;
-import com.teragrep.pth_06.jooq.generated.bloomdb.Bloomdb;
import com.teragrep.pth_06.planner.walker.ConditionWalker;
-import org.apache.spark.util.sketch.BloomFilter;
import org.jooq.*;
import org.jooq.conf.MappedSchema;
import org.jooq.conf.RenderMapping;
@@ -70,7 +66,6 @@
import static com.teragrep.pth_06.jooq.generated.journaldb.Journaldb.JOURNALDB;
import static org.jooq.impl.DSL.select;
-import static org.jooq.impl.SQLDataType.BIGINT;
// https://stackoverflow.com/questions/33657391/qualifying-a-temporary-table-column-name-in-jooq
// https://www.jooq.org/doc/latest/manual/sql-building/dynamic-sql/
@@ -96,6 +91,7 @@ public class StreamDBClient {
private long includeBeforeEpoch;
private final boolean bloomEnabled;
private final Condition journaldbCondition;
+ private final ConditionWalker walker;
public StreamDBClient(Config config) throws SQLException {
@@ -128,10 +124,7 @@ public StreamDBClient(Config config) throws SQLException {
ctx.execute("SET sql_mode = 'NO_ENGINE_SUBSTITUTION';");
}
// -- TODO use dslContext.batch for all initial operations
- BloomFiltersTempTable.create(ctx);
-
- final ConditionWalker walker = new ConditionWalker(ctx, bloomEnabled);
-
+ this.walker = new ConditionWalker(ctx, bloomEnabled);
Condition streamdbCondition;
try {
@@ -154,7 +147,7 @@ void setIncludeBeforeEpoch(long includeBeforeEpoch) {
this.includeBeforeEpoch = includeBeforeEpoch;
}
- int pullToSliceTable(Date day) {
+ public int pullToSliceTable(Date day) {
NestedTopNQuery nestedTopNQuery = new NestedTopNQuery();
SelectOnConditionStep> select = ctx
.select(
@@ -263,76 +256,6 @@ private static void create(DSLContext ctx) {
}
- // TODO WIP
- public static class BloomFiltersTempTable {
-
- public static final String bloomTable = "bloomFiltersTable";
- public static final Table BLOOM_TABLE = DSL.table(DSL.name(bloomTable));
-
- public static final Field id = DSL.field("id", BIGINT.identity(true));
- public static final Field fe100kfp001 = DSL.field(DSL.name(bloomTable, "fe100kfp001"), byte[].class);
- public static final Field fe1000kfpp003 = DSL
- .field(DSL.name(bloomTable, "fe1000kfpp003"), byte[].class);
- public static final Field fe2500kfpp005 = DSL
- .field(DSL.name(bloomTable, "fe2500kfpp005"), byte[].class);
-
- private static void create(DSLContext ctx) {
-
- DropTableStep dropQuery = ctx.dropTemporaryTableIfExists(BloomFiltersTempTable.BLOOM_TABLE);
- dropQuery.execute();
-
- Query query = ctx
- .query(
- "create temporary table bloomFiltersTable(id bigint auto_increment primary key, fe100kfp001 longblob, fe1000kfpp003 longblob, fe2500kfpp005 longblob)"
- );
-
- /*CreateTableConstraintStep query = ctx.createTemporaryTable(BloomFiltersTempTable.BLOOM_TABLE)
- .columns(
- id,
- fe100kfp001,
- fe1000kfpp003,
- fe2500kfpp005)
- .constraints(primaryKey(id));
-
- */
- query.execute();
-
- }
-
- public static long insert(
- DSLContext ctx,
- BloomFilter smallFilter,
- BloomFilter mediumFilter,
- BloomFilter largeFilter
- ) {
-
- final ByteArrayOutputStream smallBaos = new ByteArrayOutputStream();
- final ByteArrayOutputStream mediumBaos = new ByteArrayOutputStream();
- final ByteArrayOutputStream largeBaos = new ByteArrayOutputStream();
-
- try {
- smallFilter.writeTo(smallBaos);
- mediumFilter.writeTo(mediumBaos);
- largeFilter.writeTo(largeBaos);
-
- smallBaos.close();
- mediumBaos.close();
- largeBaos.close();
- }
- catch (IOException e) {
- throw new UncheckedIOException(e);
- }
-
- ctx
- .insertInto(BLOOM_TABLE)
- .columns(fe100kfp001, fe1000kfpp003, fe2500kfpp005)
- .values(DSL.val(smallBaos.toByteArray(), byte[].class), DSL.val(mediumBaos.toByteArray(), byte[].class), DSL.val(largeBaos.toByteArray(), byte[].class)).execute();
-
- long rv = ctx.lastID().longValue();
- return rv;
- }
- }
-
public static class GetArchivedObjectsFilterTable {
// temporary table created from streamdb
@@ -420,15 +343,17 @@ private Table getTableStatement(Condition journaldbCondition, Date day)
.on(JOURNALDB.LOGFILE.HOST_ID.eq(GetArchivedObjectsFilterTable.host_id).and(JOURNALDB.LOGFILE.LOGTAG.eq(GetArchivedObjectsFilterTable.tag)));
if (bloomEnabled) {
-
- selectOnConditionStep = selectOnConditionStep
- .leftJoin(Bloomdb.BLOOMDB.FILTER_EXPECTED_100000_FPP_001)
- .on(JOURNALDB.LOGFILE.ID.eq(Bloomdb.BLOOMDB.FILTER_EXPECTED_100000_FPP_001.PARTITION_ID))
- .leftJoin(Bloomdb.BLOOMDB.FILTER_EXPECTED_1000000_FPP_003)
- .on(JOURNALDB.LOGFILE.ID.eq(Bloomdb.BLOOMDB.FILTER_EXPECTED_1000000_FPP_003.PARTITION_ID))
- .leftJoin(Bloomdb.BLOOMDB.FILTER_EXPECTED_2500000_FPP_005)
- .on(JOURNALDB.LOGFILE.ID.eq(Bloomdb.BLOOMDB.FILTER_EXPECTED_2500000_FPP_005.PARTITION_ID));
-
+ Set> tables = walker.patternMatchTables();
+ if (!tables.isEmpty()) {
+ for (Table> table : tables) {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Left join pattern match table: <{}>", table.getName());
+ }
+ selectOnConditionStep = selectOnConditionStep
+ .leftJoin(table)
+ .on(JOURNALDB.LOGFILE.ID.eq((Field) table.field("partition_id")));
+ }
+ }
}
return selectOnConditionStep
diff --git a/src/main/java/com/teragrep/pth_06/planner/walker/ConditionWalker.java b/src/main/java/com/teragrep/pth_06/planner/walker/ConditionWalker.java
index 2635ac79..881595d3 100644
--- a/src/main/java/com/teragrep/pth_06/planner/walker/ConditionWalker.java
+++ b/src/main/java/com/teragrep/pth_06/planner/walker/ConditionWalker.java
@@ -47,19 +47,24 @@
import com.teragrep.pth_06.config.ConditionConfig;
import com.teragrep.pth_06.planner.walker.conditions.ElementCondition;
+import com.teragrep.pth_06.planner.walker.conditions.ValidElement;
import org.jooq.Condition;
import org.jooq.DSLContext;
+import org.jooq.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Condition Walker
Walker for conditions.
*
- * @since 23/09/2021
* @author Kimmo Leppinen
* @author Mikko Kortelainen
* @author Ville Manninen
+ * @since 23/09/2021
*/
public class ConditionWalker extends XmlWalker {
@@ -68,20 +73,21 @@ public class ConditionWalker extends XmlWalker {
// Default query is full
private boolean streamQuery = false;
private final DSLContext ctx;
+ private final Set> patternMatchSet;
+ private long bloomTermId = 0;
/**
* Constructor without connection. Used during unit-tests. Enables jooq-query construction.
*/
public ConditionWalker() {
- super();
- this.ctx = null;
- this.bloomEnabled = false;
+ this(null, false);
}
public ConditionWalker(DSLContext ctx, boolean bloomEnabled) {
super();
this.ctx = ctx;
this.bloomEnabled = bloomEnabled;
+ this.patternMatchSet = new HashSet<>();
}
public Condition fromString(String inXml, boolean streamQuery) throws Exception {
@@ -89,6 +95,15 @@ public Condition fromString(String inXml, boolean streamQuery) throws Exception
return fromString(inXml);
}
+ /**
+ * Set of all the tables that pattern matched with tokenized search terms the walkers has visited
+ *
+ * @return Set of Tables that had a pattern match
+ */
+ public Set> patternMatchTables() {
+ return patternMatchSet;
+ }
+
@Override
public Condition emitLogicalOperation(String op, Object l, Object r) throws Exception {
Condition rv;
@@ -138,11 +153,16 @@ public Condition emitUnaryOperation(String op, Element current) throws Exception
return rv;
}
- Condition emitElem(Element current) {
- ElementCondition elementCondition = new ElementCondition(
- current,
- new ConditionConfig(ctx, streamQuery, bloomEnabled, false)
+ Condition emitElem(final Element current) {
+ final ElementCondition elementCondition = new ElementCondition(
+ new ValidElement(current),
+ new ConditionConfig(ctx, streamQuery, bloomEnabled),
+ bloomTermId
);
+ if (elementCondition.isIndexStatement()) {
+ patternMatchTables().addAll(elementCondition.matchSet());
+ bloomTermId++;
+ }
return elementCondition.condition();
}
}
diff --git a/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ElementCondition.java b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ElementCondition.java
index be7d0033..d311aea8 100644
--- a/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ElementCondition.java
+++ b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ElementCondition.java
@@ -48,11 +48,16 @@
import com.teragrep.blf_01.Tokenizer;
import com.teragrep.pth_06.config.ConditionConfig;
import org.jooq.Condition;
+import org.jooq.Table;
import org.jooq.impl.DSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* Creates a query condition from provided dom element
*/
@@ -60,35 +65,26 @@ public final class ElementCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(ElementCondition.class);
- private final Element element;
+ private final ValidElement element;
private final ConditionConfig config;
+ private final long bloomTermId;
+ private final Set> tableSet;
public ElementCondition(Element element, ConditionConfig config) {
- this.element = element;
- this.config = config;
+ this(new ValidElement(element), config, 0L);
}
- private void validate(Element element) {
- if (element.getTagName() == null) {
- throw new IllegalStateException("Tag name for Element was null");
- }
- if (!element.hasAttribute("operation")) {
- throw new IllegalStateException(
- "Could not find specified or default value for 'operation' attribute from Element"
- );
- }
- if (!element.hasAttribute("value")) {
- throw new IllegalStateException(
- "Could not find specified or default value for 'value' attribute from Element"
- );
- }
+ public ElementCondition(ValidElement element, ConditionConfig config, long bloomTermId) {
+ this.element = element;
+ this.config = config;
+ this.bloomTermId = bloomTermId;
+ this.tableSet = new HashSet<>();
}
public Condition condition() {
- validate(element);
- final String tag = element.getTagName();
- final String value = element.getAttribute("value");
- final String operation = element.getAttribute("operation");
+ final String tag = element.tag();
+ final String value = element.value();
+ final String operation = element.operation();
Condition condition = DSL.noCondition();
switch (tag.toLowerCase()) {
case "index":
@@ -119,18 +115,37 @@ public Condition condition() {
IndexStatementCondition indexStatementCondition = new IndexStatementCondition(
value,
config,
- new Tokenizer(32)
+ new Tokenizer(0),
+ condition,
+ bloomTermId
);
condition = indexStatementCondition.condition();
+ List> newMatches = indexStatementCondition.matchList();
+ if (!newMatches.isEmpty()) {
+ tableSet.addAll(newMatches);
+ }
}
}
- if (condition.equals(DSL.noCondition())) {
+ // indexstatement can return condition unmodified
+ if (condition.equals(DSL.noCondition()) && !isIndexStatement()) {
throw new IllegalStateException("Unsupported Element tag " + tag);
}
LOGGER.debug("Query condition: <{}>", condition);
return condition;
}
+ public boolean isIndexStatement() {
+ String tag = element.tag();
+ return !config.streamQuery() && "indexstatement".equalsIgnoreCase(tag) && config.bloomEnabled();
+ }
+
+ public Set> matchSet() {
+ if (tableSet.isEmpty()) {
+ condition();
+ }
+ return tableSet;
+ }
+
@Override
public boolean equals(final Object object) {
if (this == object)
@@ -140,9 +155,6 @@ public boolean equals(final Object object) {
if (object.getClass() != this.getClass())
return false;
final ElementCondition cast = (ElementCondition) object;
- boolean equalName = this.element.getTagName().equals(cast.element.getTagName());
- boolean equalOperation = this.element.getAttribute("operation").equals(cast.element.getAttribute("operation"));
- boolean equalValue = this.element.getAttribute("value").equals(cast.element.getAttribute("value"));
- return equalName && equalOperation && equalValue && this.config.equals(cast.config);
+ return this.element.equals(cast.element) && this.config.equals(cast.config);
}
}
diff --git a/src/main/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementCondition.java b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementCondition.java
index e48e4b49..f9205b4d 100644
--- a/src/main/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementCondition.java
+++ b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementCondition.java
@@ -45,23 +45,18 @@
*/
package com.teragrep.pth_06.planner.walker.conditions;
-import com.teragrep.blf_01.Token;
import com.teragrep.blf_01.Tokenizer;
import com.teragrep.pth_06.config.ConditionConfig;
-import com.teragrep.pth_06.planner.StreamDBClient;
-import org.apache.spark.util.sketch.BloomFilter;
+import com.teragrep.pth_06.planner.BloomFilterTempTable;
+import com.teragrep.pth_06.planner.PatternMatch;
import org.jooq.Condition;
-import org.jooq.Field;
+import org.jooq.Table;
import org.jooq.impl.DSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.ByteArrayInputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Set;
-
-import static com.teragrep.pth_06.jooq.generated.bloomdb.Bloomdb.BLOOMDB;
+import java.util.ArrayList;
+import java.util.List;
public final class IndexStatementCondition implements QueryCondition {
@@ -70,71 +65,72 @@ public final class IndexStatementCondition implements QueryCondition {
private final String value;
private final ConditionConfig config;
private final Tokenizer tokenizer;
+ private final Condition condition;
+ private final long bloomTermId;
+ private final List> tableList;
+
+ public IndexStatementCondition(String value, ConditionConfig config) {
+ this(value, config, new Tokenizer(0), DSL.noCondition(), 0L);
+ }
public IndexStatementCondition(String value, ConditionConfig config, Tokenizer tokenizer) {
+ this(value, config, tokenizer, DSL.noCondition(), 0L);
+ }
+
+ public IndexStatementCondition(String value, ConditionConfig config, Tokenizer tokenizer, long bloomTermId) {
+ this(value, config, tokenizer, DSL.noCondition(), bloomTermId);
+ }
+
+ public IndexStatementCondition(
+ String value,
+ ConditionConfig config,
+ Tokenizer tokenizer,
+ Condition condition,
+ long bloomTermId
+ ) {
this.value = value;
this.config = config;
this.tokenizer = tokenizer;
+ this.condition = condition;
+ this.bloomTermId = bloomTermId;
+ this.tableList = new ArrayList<>();
}
public Condition condition() {
- final Set tokenSet = new HashSet<>(
- tokenizer.tokenize(new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)))
- );
-
- LOGGER.info("BloomFilter tokenSet <[{}]>", tokenSet);
-
- final BloomFilter smallFilter = BloomFilter.create(100000, 0.01);
- final BloomFilter mediumFilter = BloomFilter.create(1000000, 0.03);
- final BloomFilter largeFilter = BloomFilter.create(2500000, 0.05);
-
- tokenSet.forEach(token -> {
- smallFilter.put(token.toString());
- mediumFilter.put(token.toString());
- largeFilter.put(token.toString());
- });
-
- final long rowId = StreamDBClient.BloomFiltersTempTable
- .insert(config.context(), smallFilter, mediumFilter, largeFilter);
-
- final Condition rowIdCondition = StreamDBClient.BloomFiltersTempTable.id.eq(rowId);
-
- final Field smallColumn = DSL
- .select(StreamDBClient.BloomFiltersTempTable.fe100kfp001)
- .from(StreamDBClient.BloomFiltersTempTable.BLOOM_TABLE)
- .where(rowIdCondition)
- .asField();
- final Field mediumColumn = DSL
- .select(StreamDBClient.BloomFiltersTempTable.fe1000kfpp003)
- .from(StreamDBClient.BloomFiltersTempTable.BLOOM_TABLE)
- .where(rowIdCondition)
- .asField();
- final Field largeColumn = DSL
- .select(StreamDBClient.BloomFiltersTempTable.fe2500kfpp005)
- .from(StreamDBClient.BloomFiltersTempTable.BLOOM_TABLE)
- .where(rowIdCondition)
- .asField();
-
- final Field fe100kfp001 = DSL
- .function("bloommatch", Boolean.class, smallColumn, BLOOMDB.FILTER_EXPECTED_100000_FPP_001.FILTER);
- final Field fe1000kfpp003 = DSL
- .function("bloommatch", Boolean.class, mediumColumn, BLOOMDB.FILTER_EXPECTED_1000000_FPP_003.FILTER);
- final Field fe2500kfpp005 = DSL
- .function("bloommatch", Boolean.class, largeColumn, BLOOMDB.FILTER_EXPECTED_2500000_FPP_005.FILTER);
-
- final Condition noBloomFilter = BLOOMDB.FILTER_EXPECTED_100000_FPP_001.FILTER
- .isNull()
- .and(
- BLOOMDB.FILTER_EXPECTED_1000000_FPP_003.FILTER
- .isNull()
- .and(BLOOMDB.FILTER_EXPECTED_2500000_FPP_005.FILTER.isNull())
+ Condition newCondition = condition;
+ LOGGER.info("indexstatement reached with search term <{}>", value);
+ PatternMatch patternMatch = new PatternMatch(config.context(), value);
+ if (tableList.isEmpty()) {
+ tableList.addAll(patternMatch.toList());
+ }
+ if (!tableList.isEmpty()) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Found pattern match on <{}> table(s)", tableList.size());
+ }
+ Condition bloomCondition = DSL.noCondition();
+ Condition noBloomCondition = DSL.noCondition();
+
+ for (Table> table : tableList) {
+ BloomFilterTempTable tempTable = new BloomFilterTempTable(
+ config.context(),
+ table,
+ bloomTermId,
+ patternMatch.tokenSet()
);
- final Condition queryCondition = fe100kfp001
- .eq(true)
- .or(fe1000kfpp003.eq(true).or(fe2500kfpp005.eq(true).or(noBloomFilter)));
- LOGGER.trace("ConditionWalker.emitElement bloomCondition part <{}>", queryCondition);
+ Condition tableCondition = tempTable.generateCondition();
+ bloomCondition = bloomCondition.or(tableCondition);
+ noBloomCondition = noBloomCondition.and(table.field("filter").isNull());
+ }
+ newCondition = bloomCondition.or(noBloomCondition);
+ }
+ return newCondition;
+ }
- return queryCondition;
+ public List> matchList() {
+ if (tableList.isEmpty()) {
+ condition();
+ }
+ return tableList;
}
/**
diff --git a/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ValidElement.java b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ValidElement.java
new file mode 100644
index 00000000..31fa5384
--- /dev/null
+++ b/src/main/java/com/teragrep/pth_06/planner/walker/conditions/ValidElement.java
@@ -0,0 +1,103 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner.walker.conditions;
+
+import org.w3c.dom.Element;
+
+public class ValidElement {
+
+ private final Element element;
+
+ private void validate() {
+ if (element.getTagName() == null) {
+ throw new IllegalStateException("Tag name for Element was null");
+ }
+ if (!element.hasAttribute("operation")) {
+ throw new IllegalStateException(
+ "Could not find specified or default value for 'operation' attribute from Element"
+ );
+ }
+ if (!element.hasAttribute("value")) {
+ throw new IllegalStateException(
+ "Could not find specified or default value for 'value' attribute from Element"
+ );
+ }
+ }
+
+ public ValidElement(Element element) {
+ this.element = element;
+ }
+
+ public String tag() {
+ validate();
+ return element.getTagName();
+ }
+
+ public String value() {
+ validate();
+ return element.getAttribute("value");
+ }
+
+ public String operation() {
+ validate();
+ return element.getAttribute("operation");
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (this == object)
+ return true;
+ if (object == null)
+ return false;
+ if (object.getClass() != this.getClass())
+ return false;
+ final ValidElement cast = (ValidElement) object;
+ boolean equalName = this.element.getTagName().equals(cast.element.getTagName());
+ boolean equalOperation = this.element.getAttribute("operation").equals(cast.element.getAttribute("operation"));
+ boolean equalValue = this.element.getAttribute("value").equals(cast.element.getAttribute("value"));
+ return equalName && equalOperation && equalValue;
+ }
+}
diff --git a/src/test/java/com/teragrep/pth_06/planner/BloomFilterTempTableTest.java b/src/test/java/com/teragrep/pth_06/planner/BloomFilterTempTableTest.java
new file mode 100644
index 00000000..c21cf665
--- /dev/null
+++ b/src/test/java/com/teragrep/pth_06/planner/BloomFilterTempTableTest.java
@@ -0,0 +1,280 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner;
+
+import com.teragrep.blf_01.Token;
+import org.apache.spark.util.sketch.BloomFilter;
+import org.jooq.Condition;
+import org.jooq.DSLContext;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+import org.junit.jupiter.api.*;
+
+import java.io.ByteArrayOutputStream;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class BloomFilterTempTableTest {
+
+ final String url = "jdbc:h2:mem:test;MODE=MariaDB;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE";
+ final String userName = "sa";
+ final String password = "";
+ final List patternList = new ArrayList<>(
+ Arrays
+ .asList(
+ "(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}",
+ "(\\b25[0-5]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
+ )
+ );
+ final Connection conn = Assertions.assertDoesNotThrow(() -> DriverManager.getConnection(url, userName, password));
+
+ @BeforeAll
+ public void setup() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS filtertype").execute();
+ String filtertype = "CREATE TABLE`filtertype`" + "("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `expectedElements` bigint(20) unsigned NOT NULL,"
+ + " `targetFpp` DOUBLE(2) unsigned NOT NULL,"
+ + " `pattern` VARCHAR(2048) NOT NULL,"
+ + " UNIQUE KEY (`expectedElements`, `targetFpp`, `pattern`)" + ")";
+ conn.prepareStatement(filtertype).execute();
+ String typeSQL = "INSERT INTO `filtertype` (`id`,`expectedElements`, `targetFpp`, `pattern`) VALUES (?,?,?,?)";
+ int id = 1;
+ for (String pattern : patternList) {
+ PreparedStatement filterType = conn.prepareStatement(typeSQL);
+ filterType.setInt(1, id);
+ filterType.setInt(2, 1000);
+ filterType.setDouble(3, 0.01);
+ filterType.setString(4, pattern);
+ filterType.executeUpdate();
+ id++;
+ }
+ });
+ }
+
+ @BeforeEach
+ void createTargetTable() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS target").execute();
+ String targetTable = "CREATE TABLE `target`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ conn.prepareStatement(targetTable).execute();
+ });
+ }
+
+ @AfterAll
+ void tearDown() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("DROP ALL OBJECTS").execute(); //h2 clear database
+ conn.close();
+ });
+ }
+
+ @Test
+ public void testEmptyParentTable() {
+ DSLContext ctx = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+
+ Set tokenSet = new PatternMatch(ctx, "test").tokenSet();
+ BloomFilterTempTable tempTable = new BloomFilterTempTable(ctx, table, 0L, tokenSet);
+ RuntimeException ex = Assertions.assertThrows(RuntimeException.class, tempTable::generateCondition);
+ Assertions.assertEquals("Parent table was empty", ex.getMessage());
+ }
+
+ @Test
+ public void testConditionGeneration() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+
+ Set tokenSet = new PatternMatch(ctx, "test").tokenSet();
+ BloomFilterTempTable tempTable = new BloomFilterTempTable(ctx, table, 0L, tokenSet);
+ Condition condition = tempTable.generateCondition();
+ String e = "(\n" + " bloommatch(\n" + " (\n" + " select \"term_0_target\".\"filter\"\n"
+ + " from \"term_0_target\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"target\".\"filter_type_id\"\n" + " )\n" + " ),\n"
+ + " \"bloomdb\".\"target\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"target\".\"filter\" is not null\n" + ")";
+ Assertions.assertEquals(e, condition.toString());
+ }
+
+ @Test
+ public void testBloomTerm() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+
+ Set tokenSet = new PatternMatch(ctx, "test").tokenSet();
+ BloomFilterTempTable tempTable = new BloomFilterTempTable(ctx, table, 1L, tokenSet);
+ Condition condition = tempTable.generateCondition();
+ Assertions.assertTrue(condition.toString().contains("term_1_"));
+ }
+
+ @Test
+ public void testEquality() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ Table> target1 = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+ Table> target2 = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+ Set tokenSet = new PatternMatch(ctx, "test").tokenSet();
+ BloomFilterTempTable table1 = new BloomFilterTempTable(ctx, target1, 1L, tokenSet);
+ BloomFilterTempTable table2 = new BloomFilterTempTable(ctx, target2, 1L, tokenSet);
+ Assertions.assertEquals(table1, table2);
+ Assertions.assertEquals(table2, table1);
+ }
+
+ @Test
+ public void testDifferentTokenSetNotEquals() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+ Set set1 = new PatternMatch(ctx, "one").tokenSet();
+ Set set2 = new PatternMatch(ctx, "two").tokenSet();
+ BloomFilterTempTable table1 = new BloomFilterTempTable(ctx, table, 1L, set1);
+ BloomFilterTempTable table2 = new BloomFilterTempTable(ctx, table, 1L, set2);
+ Assertions.assertNotEquals(table1, table2);
+ Assertions.assertNotEquals(table2, table1);
+ }
+
+ @Test
+ public void testDifferentBloomTermNotEquals() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+ Set set = new PatternMatch(ctx, "one").tokenSet();
+ BloomFilterTempTable table1 = new BloomFilterTempTable(ctx, table, 0L, set);
+ BloomFilterTempTable table2 = new BloomFilterTempTable(ctx, table, 1L, set);
+ Assertions.assertNotEquals(table1, table2);
+ Assertions.assertNotEquals(table2, table1);
+ }
+
+ @Test
+ public void testDifferentDSLContextNotEquals() {
+ fillTargetTable();
+ DSLContext ctx = DSL.using(conn);
+ DSLContext ctx2 = DSL.using(conn);
+ Table> table = ctx
+ .meta()
+ .filterSchemas(s -> s.getName().equals("bloomdb"))
+ .filterTables(t -> !t.getName().equals("filtertype"))
+ .getTables()
+ .get(0);
+ Set set = new PatternMatch(ctx, "one").tokenSet();
+ BloomFilterTempTable table1 = new BloomFilterTempTable(ctx, table, 0L, set);
+ BloomFilterTempTable table2 = new BloomFilterTempTable(ctx2, table, 0L, set);
+ Assertions.assertNotEquals(table1, table2);
+ Assertions.assertNotEquals(table2, table1);
+ }
+
+ void fillTargetTable() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ String sql = "INSERT INTO `target` (`partition_id`, `filter_type_id`, `filter`) "
+ + "VALUES (?, (SELECT `id` FROM `filtertype` WHERE id=?), ?)";
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ BloomFilter filter = BloomFilter.create(1000, 0.01);
+ final ByteArrayOutputStream filterBAOS = new ByteArrayOutputStream();
+ Assertions.assertDoesNotThrow(() -> {
+ filter.writeTo(filterBAOS);
+ filterBAOS.close();
+ });
+ stmt.setInt(1, 1);
+ stmt.setInt(2, 1);
+ stmt.setBytes(3, filterBAOS.toByteArray());
+ stmt.executeUpdate();
+ });
+ }
+}
diff --git a/src/test/java/com/teragrep/pth_06/planner/PatternMatchTest.java b/src/test/java/com/teragrep/pth_06/planner/PatternMatchTest.java
new file mode 100644
index 00000000..2a7b61ab
--- /dev/null
+++ b/src/test/java/com/teragrep/pth_06/planner/PatternMatchTest.java
@@ -0,0 +1,221 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner;
+
+import org.apache.spark.util.sketch.BloomFilter;
+import org.jooq.DSLContext;
+import org.jooq.Named;
+import org.jooq.Table;
+import org.jooq.impl.DSL;
+import org.junit.jupiter.api.*;
+
+import java.io.ByteArrayOutputStream;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class PatternMatchTest {
+
+ final String url = "jdbc:h2:mem:test;MODE=MariaDB;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE";
+ final String userName = "sa";
+ final String password = "";
+ final List patternList = new ArrayList<>(
+ Arrays
+ .asList(
+ "(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}",
+ "(\\b25[0-5]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
+ )
+ );
+ final Connection conn = Assertions.assertDoesNotThrow(() -> DriverManager.getConnection(url, userName, password));
+
+ @BeforeAll
+ void setup() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS filtertype").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip255").execute();
+ String filtertype = "CREATE TABLE`filtertype`" + "("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `expectedElements` bigint(20) unsigned NOT NULL,"
+ + " `targetFpp` DOUBLE(2) unsigned NOT NULL,"
+ + " `pattern` VARCHAR(2048) NOT NULL,"
+ + " UNIQUE KEY (`expectedElements`, `targetFpp`, `pattern`)" + ")";
+ String ip = "CREATE TABLE `pattern_test_ip`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ String ip255 = "CREATE TABLE `pattern_test_ip255`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ conn.prepareStatement(filtertype).execute();
+ conn.prepareStatement(ip).execute();
+ conn.prepareStatement(ip255).execute();
+ String typeSQL = "INSERT INTO `filtertype` (`id`,`expectedElements`, `targetFpp`, `pattern`) VALUES (?,?,?,?)";
+ int id = 1;
+ for (String pattern : patternList) {
+ PreparedStatement filterType = conn.prepareStatement(typeSQL);
+ filterType.setInt(1, id);
+ filterType.setInt(2, 1000);
+ filterType.setDouble(3, 0.01);
+ filterType.setString(4, pattern);
+ filterType.executeUpdate();
+ id++;
+ }
+ writeFilter("pattern_test_ip", 1);
+ writeFilter("pattern_test_ip255", 2);
+ });
+ }
+
+ @AfterAll
+ void tearDown() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("DROP ALL OBJECTS").execute(); //h2 clear database
+ conn.close();
+ });
+ }
+
+ @Test
+ public void testSingleMatch() {
+ DSLContext ctx = DSL.using(conn);
+ String input = "192.168.1.1";
+ PatternMatch patternMatch = new PatternMatch(ctx, input);
+ List> result = patternMatch.toList();
+ Assertions.assertEquals(1, result.size());
+ Assertions.assertEquals("pattern_test_ip", result.get(0).getName());
+ }
+
+ @Test
+ public void testSearchTermTokenizedMatch() {
+ DSLContext ctx = DSL.using(conn);
+ String input = "target_ip=192.168.1.1";
+ PatternMatch patternMatch = new PatternMatch(ctx, input);
+ List> result = patternMatch.toList();
+ Assertions.assertEquals(1, result.size());
+ Assertions.assertEquals("pattern_test_ip", result.get(0).getName());
+ }
+
+ @Test
+ public void testMultipleMatch() {
+ DSLContext ctx = DSL.using(conn);
+ String input = "255.255.255.255";
+ PatternMatch patternMatch = new PatternMatch(ctx, input);
+ List> result = patternMatch.toList();
+ List> result2 = patternMatch.toList();
+ List tableNames = result.stream().map(Named::getName).collect(Collectors.toList());
+ Assertions.assertEquals(2, result.size());
+ Assertions.assertEquals(2, result2.size());
+ Assertions.assertTrue(tableNames.contains("pattern_test_ip"));
+ Assertions.assertTrue(tableNames.contains("pattern_test_ip255"));
+ }
+
+ @Test
+ public void testNoMatch() {
+ DSLContext ctx = DSL.using(conn);
+ String input = "testinput";
+ PatternMatch patternMatch = new PatternMatch(ctx, input);
+ List> result = patternMatch.toList();
+ Assertions.assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void equalsTest() {
+ DSLContext ctx = DSL.using(conn);
+ String input = "testinput";
+ PatternMatch eq1 = new PatternMatch(ctx, input);
+ PatternMatch eq2 = new PatternMatch(ctx, input);
+ Assertions.assertEquals(eq1, eq2);
+ Assertions.assertEquals(eq2, eq1);
+ }
+
+ @Test
+ public void differentInputNotEqualsTest() {
+ DSLContext ctx = DSL.using(conn);
+ PatternMatch eq1 = new PatternMatch(ctx, "testinput");
+ PatternMatch eq2 = new PatternMatch(ctx, "anotherinput");
+ Assertions.assertNotEquals(eq1, eq2);
+ Assertions.assertNotEquals(eq2, eq1);
+ }
+
+ @Test
+ public void differentDSLContextNotEqualsTest() {
+ DSLContext ctx1 = DSL.using(conn);
+ DSLContext ctx2 = DSL.using(conn);
+ PatternMatch eq1 = new PatternMatch(ctx1, "testinput");
+ PatternMatch eq2 = new PatternMatch(ctx2, "testinput");
+ Assertions.assertNotEquals(eq1, eq2);
+ Assertions.assertNotEquals(eq2, eq1);
+ }
+
+ private void writeFilter(String tableName, int filterId) {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ String sql = "INSERT INTO `" + tableName + "` (`partition_id`, `filter_type_id`, `filter`) "
+ + "VALUES (?, (SELECT `id` FROM `filtertype` WHERE id=?), ?)";
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ BloomFilter filter = BloomFilter.create(1000, 0.01);
+ final ByteArrayOutputStream filterBAOS = new ByteArrayOutputStream();
+ Assertions.assertDoesNotThrow(() -> {
+ filter.writeTo(filterBAOS);
+ filterBAOS.close();
+ });
+ stmt.setInt(1, 1);
+ stmt.setInt(2, filterId);
+ stmt.setBytes(3, filterBAOS.toByteArray());
+ stmt.executeUpdate();
+ });
+ }
+}
diff --git a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ElementConditionTest.java b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ElementConditionTest.java
index e4c99561..145f5801 100644
--- a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ElementConditionTest.java
+++ b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ElementConditionTest.java
@@ -48,7 +48,6 @@
import com.teragrep.pth_06.config.ConditionConfig;
import org.jooq.Condition;
import org.jooq.DSLContext;
-import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.impl.DSL;
import org.jooq.tools.jdbc.MockConnection;
import org.jooq.tools.jdbc.MockResult;
@@ -64,7 +63,7 @@ class ElementConditionTest {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final Document document = Assertions.assertDoesNotThrow(() -> factory.newDocumentBuilder().newDocument());
final DSLContext mockCtx = DSL.using(new MockConnection(ctx -> new MockResult[0]));
- final ConditionConfig config = new ConditionConfig(mockCtx, false, true, false);
+ final ConditionConfig config = new ConditionConfig(mockCtx, false, true);
final ConditionConfig streamConfig = new ConditionConfig(mockCtx, true);
@Test
@@ -84,20 +83,6 @@ void testStreamTags() {
Assertions.assertEquals(loops, streamTags.length);
}
- @Test
- void testIndexStatement() {
- Element element = document.createElement("indexstatement");
- element.setAttribute("value", "searchTerm");
- element.setAttribute("operation", "EQUALS");
- Element element2 = document.createElement("indexstatement");
- element2.setAttribute("value", "searchTerm");
- element2.setAttribute("operation", "NOT_EQUALS");
- Assertions
- .assertThrows(SQLDialectNotSupportedException.class, new ElementCondition(element, config)::condition);
- Assertions.assertThrows(IllegalStateException.class, new ElementCondition(element, streamConfig)::condition);
- Assertions.assertThrows(IllegalStateException.class, new ElementCondition(element2, config)::condition);
- }
-
@Test
void testProvidedElementMissingValue() {
Element element = document.createElement("test");
diff --git a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementConditionTest.java b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementConditionTest.java
index 0773a437..d7bcf3a8 100644
--- a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementConditionTest.java
+++ b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/IndexStatementConditionTest.java
@@ -47,55 +47,181 @@
import com.teragrep.blf_01.Tokenizer;
import com.teragrep.pth_06.config.ConditionConfig;
+import org.apache.spark.util.sketch.BloomFilter;
+import org.jooq.Condition;
import org.jooq.DSLContext;
-import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.impl.DSL;
import org.jooq.tools.jdbc.MockConnection;
import org.jooq.tools.jdbc.MockResult;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.*;
-/**
- * Requires database setup for full test
- */
+import java.io.ByteArrayOutputStream;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class IndexStatementConditionTest {
- final ConditionConfig config = new ConditionConfig(
+ final String url = "jdbc:h2:mem:test;MODE=MariaDB;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE";
+ final String userName = "sa";
+ final String password = "";
+ final List patternList = new ArrayList<>(
+ Arrays
+ .asList(
+ "(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}",
+ "(\\b25[0-5]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
+ )
+ );
+ final ConditionConfig mockConfig = new ConditionConfig(
DSL.using(new MockConnection(ctx -> new MockResult[0])),
false,
- true,
- false
+ true
);
- final Tokenizer tokenizer = new Tokenizer(32);
+ final Tokenizer tokenizer = new Tokenizer(0);
+ final Connection conn = Assertions.assertDoesNotThrow(() -> DriverManager.getConnection(url, userName, password));
+
+ @BeforeAll
+ void setup() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS filtertype").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip255").execute();
+ String filtertype = "CREATE TABLE`filtertype`" + "("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `expectedElements` bigint(20) unsigned NOT NULL,"
+ + " `targetFpp` DOUBLE(2) unsigned NOT NULL,"
+ + " `pattern` VARCHAR(2048) NOT NULL,"
+ + " UNIQUE KEY (`expectedElements`, `targetFpp`, `pattern`)" + ")";
+ String ip = "CREATE TABLE `pattern_test_ip`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ String ip255 = "CREATE TABLE `pattern_test_ip255`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ conn.prepareStatement(filtertype).execute();
+ conn.prepareStatement(ip).execute();
+ conn.prepareStatement(ip255).execute();
+ String typeSQL = "INSERT INTO `filtertype` (`id`,`expectedElements`, `targetFpp`, `pattern`) VALUES (?,?,?,?)";
+ int id = 1;
+ for (String pattern : patternList) {
+ PreparedStatement filterType = conn.prepareStatement(typeSQL);
+ filterType.setInt(1, id);
+ filterType.setInt(2, 1000);
+ filterType.setDouble(3, 0.01);
+ filterType.setString(4, pattern);
+ filterType.executeUpdate();
+ id++;
+ }
+ writeFilter("pattern_test_ip", 1);
+ writeFilter("pattern_test_ip255", 2);
+ });
+ }
+
+ @AfterAll
+ void tearDown() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("DROP ALL OBJECTS"); // h2 clear database
+ conn.close();
+ });
+ }
@Test
- void conditionTest() {
- DSLContext ctx = DSL.using(new MockConnection(context -> new MockResult[0]));
- // only tests that database access is tried as expected
- Assertions
- .assertThrows(
- SQLDialectNotSupportedException.class, () -> new IndexStatementCondition(
- "value",
- new ConditionConfig(ctx, false, true, false),
- tokenizer
- ).condition()
- );
+ void noMatchesTest() {
+ DSLContext ctx = DSL.using(conn);
+ Condition e1 = DSL.falseCondition();
+ Condition e2 = DSL.trueCondition();
+ ConditionConfig config = new ConditionConfig(ctx, false, true);
+ IndexStatementCondition cond1 = new IndexStatementCondition("test", config, tokenizer, e1, 0L);
+ IndexStatementCondition cond2 = new IndexStatementCondition("test", config, tokenizer, e2, 1L);
+ Assertions.assertEquals(e1, cond1.condition());
+ Assertions.assertEquals(e2, cond2.condition());
+ Assertions.assertTrue(cond1.matchList().isEmpty());
+ Assertions.assertTrue(cond2.matchList().isEmpty());
+ }
+
+ @Test
+ void oneMatchingTableTest() {
+ DSLContext ctx = DSL.using(conn);
+ ConditionConfig config = new ConditionConfig(ctx, false, true);
+ IndexStatementCondition cond = new IndexStatementCondition("192.168.1.1", config, tokenizer);
+ String e = "(\n" + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_0_pattern_test_ip\".\"filter\"\n" + " from \"term_0_pattern_test_ip\"\n"
+ + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n"
+ + " or \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n" + ")";
+ Assertions.assertEquals(e, cond.condition().toString());
+ Assertions.assertEquals(1, cond.matchList().size());
+ }
+
+ @Test
+ void twoMatchingTableTest() {
+ DSLContext ctx = DSL.using(conn);
+ ConditionConfig config = new ConditionConfig(ctx, false, true);
+ IndexStatementCondition cond = new IndexStatementCondition("255.255.255.255", config, tokenizer);
+ String e = "(\n" + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_0_pattern_test_ip\".\"filter\"\n" + " from \"term_0_pattern_test_ip\"\n"
+ + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " bloommatch(\n" + " (\n" + " select \"term_0_pattern_test_ip255\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip255\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip255\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip255\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is null\n" + " )\n" + ")";
+ Assertions.assertEquals(e, cond.condition().toString());
+ Assertions.assertEquals(2, cond.matchList().size());
}
@Test
void equalsTest() {
- IndexStatementCondition eq1 = new IndexStatementCondition("946677600", config, tokenizer);
- IndexStatementCondition eq2 = new IndexStatementCondition("946677600", config, tokenizer);
+ IndexStatementCondition eq1 = new IndexStatementCondition("946677600", mockConfig, tokenizer);
+ IndexStatementCondition eq2 = new IndexStatementCondition("946677600", mockConfig, tokenizer);
Assertions.assertEquals(eq1, eq2);
Assertions.assertEquals(eq2, eq1);
}
@Test
void notEqualsTest() {
- IndexStatementCondition eq1 = new IndexStatementCondition("946677600", config, tokenizer);
- IndexStatementCondition notEq = new IndexStatementCondition("1000", config, tokenizer);
+ IndexStatementCondition eq1 = new IndexStatementCondition("946677600", mockConfig, tokenizer);
+ IndexStatementCondition notEq = new IndexStatementCondition("1000", mockConfig, tokenizer);
Assertions.assertNotEquals(eq1, notEq);
Assertions.assertNotEquals(notEq, eq1);
Assertions.assertNotEquals(eq1, null);
}
+
+ private void writeFilter(String tableName, int filterId) {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ String sql = "INSERT INTO `" + tableName + "` (`partition_id`, `filter_type_id`, `filter`) "
+ + "VALUES (?, (SELECT `id` FROM `filtertype` WHERE id=?), ?)";
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ BloomFilter filter = BloomFilter.create(1000, 0.01);
+ final ByteArrayOutputStream filterBAOS = new ByteArrayOutputStream();
+ Assertions.assertDoesNotThrow(() -> {
+ filter.writeTo(filterBAOS);
+ filterBAOS.close();
+ });
+ stmt.setInt(1, 1);
+ stmt.setInt(2, filterId);
+ stmt.setBytes(3, filterBAOS.toByteArray());
+ stmt.executeUpdate();
+ stmt.close();
+ });
+ }
}
diff --git a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/LatestConditionTest.java b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/LatestConditionTest.java
index 7937abc3..aa7eaa7c 100644
--- a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/LatestConditionTest.java
+++ b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/LatestConditionTest.java
@@ -71,12 +71,12 @@ void conditionUpdatedTest() {
@Test
void equalsTest() {
- IndexCondition eq1 = new IndexCondition("946720800", "EQUALS", false);
+ LatestCondition eq1 = new LatestCondition("946720800");
eq1.condition();
- IndexCondition eq2 = new IndexCondition("946720800", "EQUALS", false);
- IndexCondition eq3 = new IndexCondition("946720800", "EQUALS", true);
+ LatestCondition eq2 = new LatestCondition("946720800");
+ LatestCondition eq3 = new LatestCondition("946720800");
eq3.condition();
- IndexCondition eq4 = new IndexCondition("946720800", "EQUALS", true);
+ LatestCondition eq4 = new LatestCondition("946720800");
Assertions.assertEquals(eq1, eq2);
Assertions.assertEquals(eq2, eq1);
Assertions.assertEquals(eq3, eq4);
@@ -84,13 +84,10 @@ void equalsTest() {
@Test
void notEqualsTest() {
- IndexCondition eq1 = new IndexCondition("946720800", "EQUALS", false);
- IndexCondition notEq = new IndexCondition("1000", "EQUALS", false);
- IndexCondition notEq2 = new IndexCondition("946720800", "EQUALS", true);
+ LatestCondition eq1 = new LatestCondition("946720800");
+ LatestCondition notEq = new LatestCondition("1000");
Assertions.assertNotEquals(eq1, notEq);
Assertions.assertNotEquals(notEq, eq1);
Assertions.assertNotEquals(eq1, null);
- Assertions.assertNotEquals(eq1, notEq2);
- Assertions.assertNotEquals(notEq, notEq2);
}
}
diff --git a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/SourceTypeConditionTest.java b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/SourceTypeConditionTest.java
index a6179093..87861168 100644
--- a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/SourceTypeConditionTest.java
+++ b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/SourceTypeConditionTest.java
@@ -73,12 +73,12 @@ void negationTest() {
@Test
void equalsTest() {
- IndexCondition eq1 = new IndexCondition("946677600", "EQUALS", false);
+ SourceTypeCondition eq1 = new SourceTypeCondition("946677600", "EQUALS", false);
eq1.condition();
- IndexCondition eq2 = new IndexCondition("946677600", "EQUALS", false);
- IndexCondition eq3 = new IndexCondition("946677600", "EQUALS", true);
+ SourceTypeCondition eq2 = new SourceTypeCondition("946677600", "EQUALS", false);
+ SourceTypeCondition eq3 = new SourceTypeCondition("946677600", "EQUALS", true);
eq3.condition();
- IndexCondition eq4 = new IndexCondition("946677600", "EQUALS", true);
+ SourceTypeCondition eq4 = new SourceTypeCondition("946677600", "EQUALS", true);
Assertions.assertEquals(eq1, eq2);
Assertions.assertEquals(eq2, eq1);
Assertions.assertEquals(eq3, eq4);
@@ -86,9 +86,9 @@ void equalsTest() {
@Test
void notEqualsTest() {
- IndexCondition eq1 = new IndexCondition("946677600", "EQUALS", false);
- IndexCondition notEq = new IndexCondition("1000", "EQUALS", false);
- IndexCondition notEq2 = new IndexCondition("1000", "EQUALS", true);
+ SourceTypeCondition eq1 = new SourceTypeCondition("946677600", "EQUALS", false);
+ SourceTypeCondition notEq = new SourceTypeCondition("1000", "EQUALS", false);
+ SourceTypeCondition notEq2 = new SourceTypeCondition("1000", "EQUALS", true);
Assertions.assertNotEquals(eq1, notEq);
Assertions.assertNotEquals(notEq, eq1);
Assertions.assertNotEquals(eq1, null);
diff --git a/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ValidElementTest.java b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ValidElementTest.java
new file mode 100644
index 00000000..fcf7f31e
--- /dev/null
+++ b/src/test/java/com/teragrep/pth_06/planner/walker/conditions/ValidElementTest.java
@@ -0,0 +1,127 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.planner.walker.conditions;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+class ValidElementTest {
+
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final Document document = Assertions.assertDoesNotThrow(() -> factory.newDocumentBuilder().newDocument());
+
+ @Test
+ void validTest() {
+ Element element = document.createElement("test");
+ element.setAttribute("value", "value");
+ element.setAttribute("operation", "operation");
+ ValidElement valid = new ValidElement(element);
+ Assertions.assertDoesNotThrow(() -> {
+ Assertions.assertEquals("test", valid.tag());
+ Assertions.assertEquals("value", valid.value());
+ Assertions.assertEquals("operation", valid.operation());
+ });
+ }
+
+ @Test
+ void missingValueTest() {
+ Element noValue = document.createElement("test");
+ noValue.setAttribute("operation", "operation");
+ ValidElement invalid1 = new ValidElement(noValue);
+ Assertions.assertThrows(RuntimeException.class, () -> Assertions.assertEquals("test", invalid1.tag()));
+ }
+
+ @Test
+ void missingOperationTest() {
+ Element noValue = document.createElement("test");
+ noValue.setAttribute("value", "value");
+ ValidElement invalid1 = new ValidElement(noValue);
+ Assertions.assertThrows(RuntimeException.class, () -> Assertions.assertEquals("test", invalid1.tag()));
+ }
+
+ @Test
+ void equalityTest() {
+ Element element = document.createElement("test");
+ element.setAttribute("value", "value");
+ element.setAttribute("operation", "operation");
+ ValidElement eq1 = new ValidElement(element);
+ ValidElement eq2 = new ValidElement(element);
+ Assertions.assertEquals(eq1, eq2);
+ Assertions.assertEquals(eq2, eq1);
+ }
+
+ @Test
+ void notEqualValueTest() {
+ Element element1 = document.createElement("test");
+ element1.setAttribute("value", "value");
+ element1.setAttribute("operation", "operation");
+ Element element2 = document.createElement("test");
+ element2.setAttribute("value", "notValue");
+ element2.setAttribute("operation", "operation");
+ ValidElement eq1 = new ValidElement(element1);
+ ValidElement eq2 = new ValidElement(element2);
+ Assertions.assertNotEquals(eq1, eq2);
+ Assertions.assertNotEquals(eq2, eq1);
+ }
+
+ @Test
+ void notEqualOperationTest() {
+ Element element1 = document.createElement("test");
+ element1.setAttribute("value", "value");
+ element1.setAttribute("operation", "operation");
+ Element element2 = document.createElement("test");
+ element2.setAttribute("value", "value");
+ element2.setAttribute("operation", "notOperation");
+ ValidElement eq1 = new ValidElement(element1);
+ ValidElement eq2 = new ValidElement(element2);
+ Assertions.assertNotEquals(eq1, eq2);
+ Assertions.assertNotEquals(eq2, eq1);
+ }
+}
diff --git a/src/test/java/com/teragrep/pth_06/walker/ConditionWalkerTest.java b/src/test/java/com/teragrep/pth_06/walker/ConditionWalkerTest.java
new file mode 100644
index 00000000..5473e3e7
--- /dev/null
+++ b/src/test/java/com/teragrep/pth_06/walker/ConditionWalkerTest.java
@@ -0,0 +1,274 @@
+/*
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ *
+ * Additional permission under GNU Affero General Public License version 3
+ * section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining it
+ * with other code, such other code is not for that reason alone subject to any
+ * of the requirements of the GNU Affero GPL version 3 as long as this Program
+ * is the same Program as licensed from Suomen Kanuuna Oy without any additional
+ * modifications.
+ *
+ * Supplemented terms under GNU Affero General Public License version 3
+ * section 7
+ *
+ * Origin of the software must be attributed to Suomen Kanuuna Oy. Any modified
+ * versions must be marked as "Modified version of" The Program.
+ *
+ * Names of the licensors and authors may not be used for publicity purposes.
+ *
+ * No rights are granted for use of trade names, trademarks, or service marks
+ * which are in The Program if any.
+ *
+ * Licensee must indemnify licensors and authors for any liability that these
+ * contractual assumptions impose on licensors and authors.
+ *
+ * To the extent this program is licensed as part of the Commercial versions of
+ * Teragrep, the applicable Commercial License may apply to this file if you as
+ * a licensee so wish it.
+ */
+package com.teragrep.pth_06.walker;
+
+import com.teragrep.pth_06.planner.walker.ConditionWalker;
+import org.apache.spark.util.sketch.BloomFilter;
+import org.jooq.Condition;
+import org.jooq.impl.DSL;
+import org.junit.jupiter.api.*;
+
+import java.io.ByteArrayOutputStream;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class ConditionWalkerTest {
+
+ final String url = "jdbc:h2:mem:test;MODE=MariaDB;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE";
+ final String userName = "sa";
+ final String password = "";
+ final List patternList = new ArrayList<>(
+ Arrays
+ .asList(
+ "(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}",
+ "(\\b25[0-5]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
+ )
+ );
+ final Connection conn = Assertions.assertDoesNotThrow(() -> DriverManager.getConnection(url, userName, password));
+
+ @BeforeAll
+ void setup() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS filtertype").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip").execute();
+ conn.prepareStatement("DROP TABLE IF EXISTS pattern_test_ip255").execute();
+ String filtertype = "CREATE TABLE`filtertype`" + "("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `expectedElements` bigint(20) unsigned NOT NULL,"
+ + " `targetFpp` DOUBLE(2) unsigned NOT NULL,"
+ + " `pattern` VARCHAR(2048) NOT NULL,"
+ + " UNIQUE KEY (`expectedElements`, `targetFpp`, `pattern`)" + ")";
+ String ip = "CREATE TABLE `pattern_test_ip`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ String ip255 = "CREATE TABLE `pattern_test_ip255`("
+ + " `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,"
+ + " `partition_id` bigint(20) unsigned NOT NULL UNIQUE,"
+ + " `filter_type_id` bigint(20) unsigned NOT NULL,"
+ + " `filter` longblob NOT NULL)";
+ conn.prepareStatement(filtertype).execute();
+ conn.prepareStatement(ip).execute();
+ conn.prepareStatement(ip255).execute();
+ String typeSQL = "INSERT INTO `filtertype` (`id`,`expectedElements`, `targetFpp`, `pattern`) VALUES (?,?,?,?)";
+ int id = 1;
+ for (String pattern : patternList) {
+ PreparedStatement filterType = conn.prepareStatement(typeSQL);
+ filterType.setInt(1, id);
+ filterType.setInt(2, 1000);
+ filterType.setDouble(3, 0.01);
+ filterType.setString(4, pattern);
+ filterType.executeUpdate();
+ id++;
+ }
+ writeFilter("pattern_test_ip", 1);
+ writeFilter("pattern_test_ip255", 2);
+ });
+ }
+
+ @AfterAll
+ void tearDown() {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("DROP ALL OBJECTS").execute(); //h2 clear database
+ conn.close();
+ });
+ }
+
+ @Test
+ void bloomNoMatchTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "\"getArchivedObjects_filter_table\".\"directory\" like 'haproxy'";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, false));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(0, walker.patternMatchTables().size());
+ }
+
+ @Test
+ void bloomNoMatchStreamQueryTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "\"streamdb\".\"stream\".\"directory\" like 'haproxy'";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, true));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(0, walker.patternMatchTables().size());
+ }
+
+ @Test
+ void singleTablePatternMatchStreamQueryTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "\"streamdb\".\"stream\".\"directory\" like 'haproxy'";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, true));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(0, walker.patternMatchTables().size());
+ }
+
+ @Test
+ void singleTablePatternMatchTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "(\n" + " \"getArchivedObjects_filter_table\".\"directory\" like 'haproxy'\n" + " and (\n"
+ + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_0_pattern_test_ip\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n"
+ + " or \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n" + " )\n" + ")";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, false));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(1, walker.patternMatchTables().size());
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip")));
+ }
+
+ @Test
+ void twoTablePatternMatchTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "(\n" + " \"getArchivedObjects_filter_table\".\"directory\" like 'haproxy'\n" + " and (\n"
+ + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_0_pattern_test_ip\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " bloommatch(\n" + " (\n" + " select \"term_0_pattern_test_ip255\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip255\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip255\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip255\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is null\n" + " )\n" + " )\n" + ")";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, false));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(2, walker.patternMatchTables().size());
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip")));
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip255")));
+ }
+
+ @Test
+ void multipleSearchTermTestOneMatchTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "(\n" + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_1_pattern_test_ip\".\"filter\"\n" + " from \"term_1_pattern_test_ip\"\n"
+ + " where (\n" + " term_id = 1\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n"
+ + " or \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n" + ")";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, false));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(1, walker.patternMatchTables().size());
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip")));
+ }
+
+ @Test
+ void multipleSearchTermTwoAndOneMatchTest() {
+ ConditionWalker walker = new ConditionWalker(DSL.using(conn), true);
+ String q = "";
+ String e = "(\n" + " (\n" + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_0_pattern_test_ip\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " bloommatch(\n" + " (\n" + " select \"term_0_pattern_test_ip255\".\"filter\"\n"
+ + " from \"term_0_pattern_test_ip255\"\n" + " where (\n" + " term_id = 0\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip255\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip255\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is not null\n" + " )\n" + " or (\n"
+ + " \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n"
+ + " and \"bloomdb\".\"pattern_test_ip255\".\"filter\" is null\n" + " )\n" + " )\n"
+ + " and (\n" + " (\n" + " bloommatch(\n" + " (\n"
+ + " select \"term_1_pattern_test_ip\".\"filter\"\n"
+ + " from \"term_1_pattern_test_ip\"\n" + " where (\n" + " term_id = 1\n"
+ + " and type_id = \"bloomdb\".\"pattern_test_ip\".\"filter_type_id\"\n" + " )\n"
+ + " ),\n" + " \"bloomdb\".\"pattern_test_ip\".\"filter\"\n" + " ) = true\n"
+ + " and \"bloomdb\".\"pattern_test_ip\".\"filter\" is not null\n" + " )\n"
+ + " or \"bloomdb\".\"pattern_test_ip\".\"filter\" is null\n" + " )\n" + ")";
+ Condition cond = Assertions.assertDoesNotThrow(() -> walker.fromString(q, false));
+ Assertions.assertEquals(e, cond.toString());
+ Assertions.assertEquals(2, walker.patternMatchTables().size());
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip")));
+ Assertions
+ .assertTrue(walker.patternMatchTables().stream().anyMatch(t -> t.getName().equals("pattern_test_ip255")));
+ }
+
+ private void writeFilter(String tableName, int filterId) {
+ Assertions.assertDoesNotThrow(() -> {
+ conn.prepareStatement("CREATE SCHEMA IF NOT EXISTS BLOOMDB").execute();
+ conn.prepareStatement("USE BLOOMDB").execute();
+ String sql = "INSERT INTO `" + tableName + "` (`partition_id`, `filter_type_id`, `filter`) "
+ + "VALUES (?, (SELECT `id` FROM `filtertype` WHERE id=?), ?)";
+ PreparedStatement stmt = conn.prepareStatement(sql);
+ BloomFilter filter = BloomFilter.create(1000, 0.01);
+ final ByteArrayOutputStream filterBAOS = new ByteArrayOutputStream();
+ Assertions.assertDoesNotThrow(() -> {
+ filter.writeTo(filterBAOS);
+ filterBAOS.close();
+ });
+ stmt.setInt(1, 1);
+ stmt.setInt(2, filterId);
+ stmt.setBytes(3, filterBAOS.toByteArray());
+ stmt.executeUpdate();
+ });
+ }
+}
From d9730aeb912a527045f808f21d1351b6e749587e Mon Sep 17 00:00:00 2001
From: elliVM <47@teragrep.com>
Date: Thu, 12 Sep 2024 10:59:38 +0300
Subject: [PATCH 02/20] run jooq generate with new settings and spotless
---
.../com/teragrep/pth_06/jooq/generated/DefaultCatalog.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/bloomdb/Bloomdb.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/bloomdb/Indexes.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/bloomdb/Keys.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/bloomdb/Tables.java | 6 +++---
.../pth_06/jooq/generated/bloomdb/tables/Filtertype.java | 6 +++---
.../generated/bloomdb/tables/records/FiltertypeRecord.java | 6 +++---
.../teragrep/pth_06/jooq/generated/journaldb/Indexes.java | 6 +++---
.../teragrep/pth_06/jooq/generated/journaldb/Journaldb.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/journaldb/Keys.java | 6 +++---
.../teragrep/pth_06/jooq/generated/journaldb/Tables.java | 6 +++---
.../pth_06/jooq/generated/journaldb/tables/Bucket.java | 6 +++---
.../pth_06/jooq/generated/journaldb/tables/Host.java | 6 +++---
.../pth_06/jooq/generated/journaldb/tables/Logfile.java | 6 +++---
.../generated/journaldb/tables/records/BucketRecord.java | 6 +++---
.../jooq/generated/journaldb/tables/records/HostRecord.java | 6 +++---
.../generated/journaldb/tables/records/LogfileRecord.java | 6 +++---
.../teragrep/pth_06/jooq/generated/streamdb/Indexes.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/streamdb/Keys.java | 6 +++---
.../teragrep/pth_06/jooq/generated/streamdb/Streamdb.java | 6 +++---
.../com/teragrep/pth_06/jooq/generated/streamdb/Tables.java | 6 +++---
.../pth_06/jooq/generated/streamdb/tables/Host.java | 6 +++---
.../pth_06/jooq/generated/streamdb/tables/LogGroup.java | 6 +++---
.../pth_06/jooq/generated/streamdb/tables/Stream.java | 6 +++---
.../jooq/generated/streamdb/tables/records/HostRecord.java | 6 +++---
.../generated/streamdb/tables/records/LogGroupRecord.java | 6 +++---
.../generated/streamdb/tables/records/StreamRecord.java | 6 +++---
27 files changed, 81 insertions(+), 81 deletions(-)
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/DefaultCatalog.java b/src/main/java/com/teragrep/pth_06/jooq/generated/DefaultCatalog.java
index c311b38b..2bbd4cbf 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/DefaultCatalog.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/DefaultCatalog.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Bloomdb.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Bloomdb.java
index 1dd155d4..8905a5fb 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Bloomdb.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Bloomdb.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Indexes.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Indexes.java
index 3a795632..5decdcba 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Indexes.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Indexes.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Keys.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Keys.java
index 13f2b35a..52344ed8 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Keys.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Keys.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Tables.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Tables.java
index 146d9e63..364c28c9 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Tables.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/Tables.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/Filtertype.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/Filtertype.java
index c60c2332..f347f11a 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/Filtertype.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/Filtertype.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/records/FiltertypeRecord.java b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/records/FiltertypeRecord.java
index bd782920..494c5771 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/records/FiltertypeRecord.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/bloomdb/tables/records/FiltertypeRecord.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Indexes.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Indexes.java
index 0042fe65..c3a7aee0 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Indexes.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Indexes.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Journaldb.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Journaldb.java
index 8e9d4029..fc6582ca 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Journaldb.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Journaldb.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Keys.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Keys.java
index a6654596..74db3c37 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Keys.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Keys.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Tables.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Tables.java
index 77b2593b..90829a5b 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Tables.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/Tables.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Bucket.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Bucket.java
index 67652d22..8de40279 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Bucket.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Bucket.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Host.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Host.java
index 46fc699a..53ae48ad 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Host.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Host.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*
*
* Additional permission under GNU Affero General Public License version 3
diff --git a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Logfile.java b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Logfile.java
index 0fd87e38..7aa46c88 100644
--- a/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Logfile.java
+++ b/src/main/java/com/teragrep/pth_06/jooq/generated/journaldb/tables/Logfile.java
@@ -1,6 +1,6 @@
/*
- * This program handles user requests that require archive access.
- * Copyright (C) 2022, 2023, 2024 Suomen Kanuuna Oy
+ * Teragrep Archive Datasource (pth_06)
+ * Copyright (C) 2021-2024 Suomen Kanuuna Oy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -13,7 +13,7 @@
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see