Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package org.opensearch.sql.calcite.remote;

import static org.junit.Assume.assumeFalse;
import static org.opensearch.sql.legacy.TestUtils.isIndexExist;
import static org.opensearch.sql.legacy.TestsConstants.*;
import static org.opensearch.sql.util.MatcherUtils.rows;
import static org.opensearch.sql.util.MatcherUtils.schema;
Expand All @@ -27,16 +29,24 @@ public void init() throws Exception {
super.init();
enableCalcite();

Request request1 = new Request("PUT", "/test/_doc/1?refresh=true");
request1.setJsonEntity("{\"name\": \"hello\", \"age\": 20}");
client().performRequest(request1);
Request request2 = new Request("PUT", "/test/_doc/2?refresh=true");
request2.setJsonEntity("{\"name\": \"world\", \"age\": 30}");
client().performRequest(request2);
// The parquet/composite store on the analytics-engine route is append-only: re-PUTting the same
// _id adds a row instead of replacing it. init() runs as @Before (before every test), so
// re-seeding these raw-document indices each time would inflate their row counts. Seed once,
// mirroring loadIndex's isIndexExist guard; v2 behavior is unchanged (same end state).
if (!isIndexExist(client(), "test")) {
Request request1 = new Request("PUT", "/test/_doc/1?refresh=true");
request1.setJsonEntity("{\"name\": \"hello\", \"age\": 20}");
client().performRequest(request1);
Request request2 = new Request("PUT", "/test/_doc/2?refresh=true");
request2.setJsonEntity("{\"name\": \"world\", \"age\": 30}");
client().performRequest(request2);
}
// PUT index test1
Request request3 = new Request("PUT", "/test1/_doc/1?refresh=true");
request3.setJsonEntity("{\"name\": \"HELLO\", \"alias\": \"Hello\"}");
client().performRequest(request3);
if (!isIndexExist(client(), "test1")) {
Request request3 = new Request("PUT", "/test1/_doc/1?refresh=true");
request3.setJsonEntity("{\"name\": \"HELLO\", \"alias\": \"Hello\"}");
client().performRequest(request3);
}

loadIndex(Index.BANK);
loadIndex(Index.DATA_TYPE_ALIAS);
Expand All @@ -46,28 +56,43 @@ public void init() throws Exception {

@Test
public void testInvalidTable() {
if (isAnalyticsParquetIndicesEnabled()) {
// The analytics-engine route resolves tables through Calcite's catalog, which raises a
// CalciteException ("Table 'unknown' not found", surfaced as HTTP 400) rather than the v2
// path's IllegalStateException ("no such index [unknown]").
Throwable e =
assertThrowsWithReplace(ResponseException.class, () -> executeQuery("source=unknown"));
verifyErrorMessageContains(e, "Table 'unknown' not found");
return;
}
Throwable e =
assertThrowsWithReplace(IllegalStateException.class, () -> executeQuery("source=unknown"));
verifyErrorMessageContains(e, "no such index [unknown]");
}

@Test
public void testSourceQuery() throws IOException {
JSONObject actual = executeQuery("source=test");
// Pin the projection with an explicit `| fields`. The analytics-engine route returns SELECT-*
// columns in parquet storage order (alphabetical), not the v2 path's mapping order, so an
// unpinned `source=test` yields [age, name]. Pinning makes column order deterministic across
// both engines without changing which rows are returned.
JSONObject actual = executeQuery("source=test | fields name, age");
verifySchema(actual, schema("name", "string"), schema("age", "bigint"));
verifyDataRows(actual, rows("hello", 20), rows("world", 30));
}

@Test
public void testMultipleSourceQuery_SameTable() throws IOException {
JSONObject actual = executeQuery("source=test, test");
// Pin column order — see testSourceQuery (analytics route returns storage/alphabetical order).
JSONObject actual = executeQuery("source=test, test | fields name, age");
verifySchema(actual, schema("name", "string"), schema("age", "bigint"));
verifyDataRows(actual, rows("hello", 20), rows("world", 30));
}

@Test
public void testMultipleSourceQuery_DifferentTables() throws IOException {
JSONObject actual = executeQuery("source=test, test1");
// Pin column order — see testSourceQuery (analytics route returns storage/alphabetical order).
JSONObject actual = executeQuery("source=test, test1 | fields name, alias, age");
verifySchema(
actual, schema("name", "string"), schema("age", "bigint"), schema("alias", "string"));
verifyDataRows(
Expand All @@ -76,7 +101,8 @@ public void testMultipleSourceQuery_DifferentTables() throws IOException {

@Test
public void testIndexPatterns() throws IOException {
JSONObject actual = executeQuery("source=test*");
// Pin column order — see testSourceQuery (analytics route returns storage/alphabetical order).
JSONObject actual = executeQuery("source=test* | fields name, alias, age");
verifySchema(
actual, schema("name", "string"), schema("age", "bigint"), schema("alias", "string"));
verifyDataRows(
Expand Down Expand Up @@ -169,6 +195,12 @@ public void testFilterQueryWithOr() throws IOException {

@Test
public void testFilterQueryWithOr2() throws IOException {
assumeFalse(
"The implicit search-filter syntax (source=idx (cond)) lowers to a Lucene query_string,"
+ " which the DataFusion backend doesn't support; it matches no rows on the"
+ " analytics-engine route. The explicit `| where` form (testFilterQueryWithOr) works"
+ " there.",
isAnalyticsParquetIndicesEnabled());
JSONObject actual =
executeQuery(
String.format(
Expand All @@ -183,7 +215,14 @@ public void testFilterQueryWithOr2() throws IOException {
public void testQueryMinusFields() throws IOException {
JSONObject actual =
executeQuery(
String.format("source=%s | fields - firstname, lastname, birthdate", TEST_INDEX_BANK));
String.format(
// Trailing `| fields` pins the post-exclusion column order — the analytics-engine
// route returns the surviving columns in storage (alphabetical) order, not the v2
// path's mapping order. The `fields -` exclusion is still the clause under test.
"source=%s | fields - firstname, lastname, birthdate"
+ " | fields account_number, address, gender, city, balance, employer, state,"
+ " age, email, male",
TEST_INDEX_BANK));
verifySchema(
actual,
schema("account_number", "bigint"),
Expand Down Expand Up @@ -282,8 +321,12 @@ public void testQueryMinusFieldsWithFilter() throws IOException {
JSONObject actual =
executeQuery(
String.format(
// Trailing `| fields` pins the post-exclusion column order — see
// testQueryMinusFields.
"source=%s | where (account_number = 20 or city = 'Brogan') and balance > 10000 |"
+ " fields - firstname, lastname",
+ " fields - firstname, lastname"
+ " | fields account_number, address, birthdate, gender, city, balance,"
+ " employer, state, age, email, male",
TEST_INDEX_BANK));
verifySchema(
actual,
Expand Down Expand Up @@ -360,6 +403,11 @@ public void testMultipleTablesAndFilters_SameTable() throws IOException {

@Test
public void testMultipleTables_DifferentTables() throws IOException {
assumeFalse(
"Multi-index source with a numeric field of conflicting widths (bank.age=integer,"
+ " test.age=long) is rejected by the analytics-engine schema merge (no integer->long"
+ " widening) instead of being coerced like the v2/Calcite path.",
isAnalyticsParquetIndicesEnabled());
JSONObject actual =
executeQuery(String.format("source=%s, test | stats count() as c", TEST_INDEX_BANK));
verifySchema(actual, schema("c", "bigint"));
Expand All @@ -368,6 +416,10 @@ public void testMultipleTables_DifferentTables() throws IOException {

@Test
public void testMultipleTables_WithIndexPattern() throws IOException {
assumeFalse(
"Multi-index source with conflicting numeric widths (bank.age=integer, test.age=long) is"
+ " rejected by the analytics-engine schema merge (no integer->long widening).",
isAnalyticsParquetIndicesEnabled());
JSONObject actual =
executeQuery(String.format("source=%s, test* | stats count() as c", TEST_INDEX_BANK));
verifySchema(actual, schema("c", "bigint"));
Expand All @@ -376,6 +428,10 @@ public void testMultipleTables_WithIndexPattern() throws IOException {

@Test
public void testMultipleTablesAndFilters_WithIndexPattern() throws IOException {
assumeFalse(
"Multi-index source with conflicting numeric widths (bank.age=integer, test.age=long) is"
+ " rejected by the analytics-engine schema merge (no integer->long widening).",
isAnalyticsParquetIndicesEnabled());
JSONObject actual =
executeQuery(
String.format("source=%s, test* gender = 'F' | stats count() as c", TEST_INDEX_BANK));
Expand Down Expand Up @@ -612,6 +668,10 @@ public void testKeepThrowCalciteException() throws IOException {

@Test
public void testAliasDataType() throws IOException {
assumeFalse(
"alias-typed fields are stripped from test datasets on the analytics-engine route (the"
+ " parquet/composite store can't hold them), so alias_col doesn't exist there.",
isAnalyticsParquetIndicesEnabled());
JSONObject result =
executeQuery(
String.format(
Expand All @@ -634,6 +694,11 @@ public void testMetaFieldAlias() throws IOException {

@Test
public void testFieldsMergedObject() throws IOException {
assumeFalse(
"This projects machine_array.* (a nested field, stripped from datasets on the"
+ " analytics-engine route) and relies on cross-index object-field merge over the"
+ " merge_test* wildcard, which the analytics-engine route doesn't resolve.",
isAnalyticsParquetIndicesEnabled());
JSONObject result =
executeQuery(
String.format(
Expand Down Expand Up @@ -661,7 +726,9 @@ public void testNumericLiteral() throws IOException {
JSONObject result =
executeQuery(
"source=test | eval decimalLiteral = 0.06 - 0.01, doubleLiteral = 0.06d - 0.01d,"
+ " floatLiteral = 0.06f - 0.01f");
+ " floatLiteral = 0.06f - 0.01f"
// Pin column order — see testSourceQuery (analytics route reorders columns).
+ " | fields name, age, decimalLiteral, doubleLiteral, floatLiteral");
verifySchema(
result,
schema("name", "string"),
Expand All @@ -677,6 +744,11 @@ public void testNumericLiteral() throws IOException {

@Test
public void testDecimalLiteral() throws IOException {
assumeFalse(
"Non-suffixed decimal literals use DECIMAL arithmetic on the v2/Calcite path but DOUBLE on"
+ " the DataFusion backend (e.g. 0.1 / 0.3 * 0.3 = 0.1 vs 0.0999...), so these"
+ " precision-sensitive expectations diverge on the analytics-engine route.",
isAnalyticsParquetIndicesEnabled());
JSONObject result =
executeQuery(
"source=test | eval r1 = 22 / 7.0, r2 = 22 / 7.0d, r3 = 22.0 / 7, r4 = 22.0d / 7,"
Expand Down
Loading