@@ -95,6 +95,94 @@ void insertWithSET() {
9595 assertThat (((Number ) row .getProperty ("cpu" )).doubleValue ()).isEqualTo (75.5 );
9696 }
9797
98+ /**
99+ * Regression test for issue #4128: combining "=" and LIKE on a TAG column with OR
100+ * was returning 0 results because the time-series planner pushed down the equality
101+ * branch as if it were a global AND condition, dropping rows that should have matched
102+ * any of the LIKE branches.
103+ */
104+ @ Test
105+ void selectTagsEqualityOrLikeCsv () {
106+ database .command ("sql" ,
107+ "CREATE TIMESERIES TYPE TaggedMetric TIMESTAMP ts TAGS (tags STRING) FIELDS (value DOUBLE)" );
108+
109+ database .transaction (() -> {
110+ database .command ("sql" , "INSERT INTO TaggedMetric SET ts = 1000, tags = 'CI', value = 1.0" );
111+ database .command ("sql" , "INSERT INTO TaggedMetric SET ts = 2000, tags = 'CI,slow', value = 2.0" );
112+ database .command ("sql" , "INSERT INTO TaggedMetric SET ts = 3000, tags = '1m,CI,fast', value = 3.0" );
113+ database .command ("sql" , "INSERT INTO TaggedMetric SET ts = 4000, tags = 'fast,CI', value = 4.0" );
114+ database .command ("sql" , "INSERT INTO TaggedMetric SET ts = 5000, tags = 'other', value = 5.0" );
115+ });
116+
117+ // Mixing = and LIKE in OR must return all 4 rows tagged with CI in any position.
118+ final ResultSet rs = database .query ("sql" ,
119+ "SELECT FROM TaggedMetric WHERE ts BETWEEN 0 AND 10000 AND ("
120+ + "tags = 'CI' OR tags LIKE 'CI,%' OR tags LIKE '%,CI' OR tags LIKE '%,CI,%')" );
121+ final List <Result > results = new ArrayList <>();
122+ while (rs .hasNext ())
123+ results .add (rs .next ());
124+
125+ assertThat (results ).hasSize (4 );
126+
127+ // Only-LIKE form (the workaround in the bug report) should also return 4 rows.
128+ final ResultSet rs2 = database .query ("sql" ,
129+ "SELECT FROM TaggedMetric WHERE ts BETWEEN 0 AND 10000 AND ("
130+ + "tags LIKE 'CI' OR tags LIKE 'CI,%' OR tags LIKE '%,CI' OR tags LIKE '%,CI,%')" );
131+ final List <Result > results2 = new ArrayList <>();
132+ while (rs2 .hasNext ())
133+ results2 .add (rs2 .next ());
134+
135+ assertThat (results2 ).hasSize (4 );
136+ }
137+
138+ /**
139+ * Push-down sanity: when the WHERE has a single equality on a tag, the engine
140+ * still uses the optimized tag filter and returns the correct row.
141+ */
142+ @ Test
143+ void selectSingleTagEqualityPushDown () {
144+ database .command ("sql" ,
145+ "CREATE TIMESERIES TYPE PushDownMetric TIMESTAMP ts TAGS (host STRING) FIELDS (value DOUBLE)" );
146+
147+ database .transaction (() -> {
148+ database .command ("sql" , "INSERT INTO PushDownMetric SET ts = 1000, host = 'h1', value = 1.0" );
149+ database .command ("sql" , "INSERT INTO PushDownMetric SET ts = 2000, host = 'h2', value = 2.0" );
150+ database .command ("sql" , "INSERT INTO PushDownMetric SET ts = 3000, host = 'h1', value = 3.0" );
151+ });
152+
153+ final ResultSet rs = database .query ("sql" ,
154+ "SELECT FROM PushDownMetric WHERE ts BETWEEN 0 AND 10000 AND host = 'h1'" );
155+ final List <Result > results = new ArrayList <>();
156+ while (rs .hasNext ())
157+ results .add (rs .next ());
158+
159+ assertThat (results ).hasSize (2 );
160+ }
161+
162+ /**
163+ * Push-down for OR of equalities on the same tag column: must return the union.
164+ */
165+ @ Test
166+ void selectTagOrEqualitiesPushDown () {
167+ database .command ("sql" ,
168+ "CREATE TIMESERIES TYPE OrEqMetric TIMESTAMP ts TAGS (host STRING) FIELDS (value DOUBLE)" );
169+
170+ database .transaction (() -> {
171+ database .command ("sql" , "INSERT INTO OrEqMetric SET ts = 1000, host = 'h1', value = 1.0" );
172+ database .command ("sql" , "INSERT INTO OrEqMetric SET ts = 2000, host = 'h2', value = 2.0" );
173+ database .command ("sql" , "INSERT INTO OrEqMetric SET ts = 3000, host = 'h3', value = 3.0" );
174+ database .command ("sql" , "INSERT INTO OrEqMetric SET ts = 4000, host = 'h1', value = 4.0" );
175+ });
176+
177+ final ResultSet rs = database .query ("sql" ,
178+ "SELECT FROM OrEqMetric WHERE ts BETWEEN 0 AND 10000 AND (host = 'h1' OR host = 'h2')" );
179+ final List <Result > results = new ArrayList <>();
180+ while (rs .hasNext ())
181+ results .add (rs .next ());
182+
183+ assertThat (results ).hasSize (3 );
184+ }
185+
98186 @ Test
99187 void timeBucketFunction () {
100188 database .command ("sql" ,
0 commit comments