Skip to content
Merged
Show file tree
Hide file tree
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
18 changes: 17 additions & 1 deletion crates/sqllib/src/map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Functions for manipulating maps

use crate::{ConcatSemigroup, Semigroup, Weight};
use crate::{Array, ConcatSemigroup, Semigroup, Weight};
use dbsp::utils::Tup2;
use std::collections::BTreeMap;
use std::sync::Arc;
Expand Down Expand Up @@ -274,3 +274,19 @@ where
let key = key?;
Some(map_contains_key__(value, key))
}

#[doc(hidden)]
pub fn map_keys_<I, T>(value: Map<I, T>) -> Array<I>
where
I: Ord + Clone,
{
Arc::new(value.keys().cloned().collect())
}

#[doc(hidden)]
pub fn map_keysN<I, T>(value: Option<Map<I, T>>) -> Option<Array<I>>
where
I: Ord + Clone,
{
value.map(|value| map_keys_(value))
}
1 change: 1 addition & 0 deletions docs.feldera.com/docs/sql/function-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
* `MAP` (aggregate): [aggregates](aggregates.md#map)
* `MAP`: [map](map.md#map-literals)
* `MAP_CONTAINS_KEY`: [map](map.md#map_contains_key)
* `MAP_KEYS`: [map](map.md#map_keys)
* `MAX` (aggregate): [aggregates](aggregates.md#max), [aggregates](aggregates.md#window-max)
* `MD5`: [string](string.md#md5), [binary](binary.md#md5)
* `MIN` (aggregate): [aggregates](aggregates.md#min), [aggregates](aggregates.md#window-min)
Expand Down
1 change: 1 addition & 0 deletions docs.feldera.com/docs/sql/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Comparison operations (`=`, `<>`, `!=`, `>`, `<`, `>=`, `<=`) can be applied to
| _map_`[`_key_`]` | Returns the element in the map with the specified key. If there is no such key, the result is `NULL`. | `MAP['x', 4, 'y', 3]['x']` => 4 |
| <a id="cardinality"></a>`CARDINALITY`(map) | Returns the number of key-value pairs in the map. | `CARDINALITY(MAP['x', 4])` => 1 |
| <a id="map_contains_key"></a>`MAP_CONTAINS_KEY`(map, key) | Returns true when the map has an item with the specified key; `NULL` if any argument is `NULL`. | `MAP_CONTAINS_KEY(MAP['x', 4], 'x')` => `true` |
| <a id="map_keys"></a>`MAP_KEYS`(map) | Returns a sorted ARRAY of the appropriate type with all the keys of the MAP. | `MAP_KEYS(MAP['x', 4, 'y', 5])` => `['x', 'y']` |

## The `UNNEST` Operator

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,10 @@ List<ParsedStatement> runParser() {
for (SqlStatements stat : this.toCompile) {
try {
if (stat.many) {
if (stat.statement.isEmpty())
if (stat.statement.isEmpty()) {
this.sqlToRelCompiler.emptyStatement();
continue;
}
parsed.addAll(this.sqlToRelCompiler.parseStatements(stat.statement, stat.visible));
} else {
SqlNode node = this.sqlToRelCompiler.parse(stat.statement, stat.visible);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1295,7 +1295,11 @@ public VisitDecision preorder(DBSPCastExpression expression) {
if (sourceType.is(DBSPTypeVariant.class)) {
// Cast variant to map
functionName = "cast_to_map" + destType.nullableSuffix() + "_" + sourceType.baseTypeWithSuffix();
this.builder.append(functionName).append("(").increase();
this.builder.append(functionName).append("::<");
destMap.getKeyType().accept(this);
this.builder.append(", ");
destMap.getValueType().accept(this);
this.builder.append(">(").increase();
expression.source.accept(this);
this.builder.decrease().append(")");
this.pop(expression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,11 @@ else if (arg0Type.is(DBSPTypeMap.class))
}
return next;
}
case MAP_KEYS:
validateArgCount(node, operationName, ops.size(), 1);
DBSPExpression arg0 = ops.get(0);
String method = getArrayOrMapCallName(call, arg0);
return new DBSPApplyExpression(node, method, type, arg0);
case DOT:
default:
throw new UnimplementedException("Function " + Utilities.singleQuote(call.getOperator().toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ public DBSPType convertType(SourcePositionRange context, RelDataType dt, boolean
case MAP: {
RelDataType kt = Objects.requireNonNull(dt.getKeyType());
DBSPType keyType = this.convertType(context, kt, asStruct);
if (keyType.code == DBSPTypeCode.NULL) {
throw new CompilationError("MAP key type cannot be NULL");
}
RelDataType vt = Objects.requireNonNull(dt.getValueType());
DBSPType valueType = this.convertType(context, vt, asStruct);
return new DBSPTypeMap(keyType, valueType, dt.isNullable());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,9 @@ record Func(SqlOperator function, String functionName, SqlLibrary library,
new Func(SqlLibraryOperators.MD5, "MD5", SqlLibrary.SPARK,
"string#md5,binary#md5", false),
new Func(SqlLibraryOperators.IFNULL, "IFNULL", SqlLibrary.BIG_QUERY,
"comparisons#ifnull", false)

"comparisons#ifnull", false),
new Func(SqlLibraryOperators.MAP_KEYS, "MAP_KEYS", SqlLibrary.SPARK,
"map#map_keys", false)
// new Func(SqlLibraryOperators.SAFE_ORDINAL, "SAFE_ORDINAL", SqlLibrary.BIG_QUERY, "array", false),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ public int getDefaultPrecision(SqlTypeName typeName) {
public boolean shouldConvertRaggedUnionTypesToVarying() { return true; }
};

public void emptyStatement() {
this.newlines.append("\n");
}

/** A TypeFactory that knows about RelStruct, our representation of user-defined types */
public static class CustomTypeFactory extends SqlTypeFactoryImpl {
static int currentId = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.dbsp.sqlCompiler.compiler.sql.tools.Change;
import org.dbsp.sqlCompiler.compiler.sql.tools.InputOutputChange;
import org.dbsp.sqlCompiler.compiler.sql.tools.InputOutputChangeStream;
import org.dbsp.sqlCompiler.ir.expression.DBSPArrayExpression;
import org.dbsp.sqlCompiler.ir.expression.DBSPTupleExpression;
import org.dbsp.sqlCompiler.ir.expression.literal.DBSPI32Literal;
import org.dbsp.sqlCompiler.ir.expression.DBSPMapExpression;
Expand Down Expand Up @@ -163,4 +164,47 @@ public void testUnnestMapFields() {
this.testQuery("", sql, new InputOutputChangeStream()
.addPair(new Change(), new Change("V", result)));
}

@Test
public void nullMapKey() {
this.statementsFailingInCompilation("CREATE VIEW V AS SELECT MAP[NULL, NULL]",
"MAP key type cannot be NULL");
}

@Test
public void testMapKeys() {
String sql = "SELECT map_keys(map['foo', 1, 'bar', 2])";
DBSPZSetExpression result = new DBSPZSetExpression(
new DBSPTupleExpression(new DBSPArrayExpression(
false,
new DBSPStringLiteral("bar"),
new DBSPStringLiteral("foo"))));
this.testQuery("", sql, new InputOutputChangeStream()
.addPair(new Change(), new Change("V", result)));
}

@Test
public void mapVariant() {
var ccs = this.getCCS("""
create table j(j VARCHAR);

create LOCAL view user_props AS
SELECT PARSE_JSON(j) AS contacts FROM j;

create view abc as
WITH ref_profile AS (
SELECT cast(contacts as MAP<varchar, variant>) contacts
FROM user_props
) SELECT key
FROM ref_profile profile_0, UNNEST(MAP_KEYS(profile_0.contacts)) AS t(key)""");
ccs.step("""
INSERT INTO j VALUES('{ "a": "1", "b": 2, "c": [1, 2, 3], "d": null, "e": { "f": 1 } }');""", """
key | weight
------------------------
a| 1
b| 1
c| 1
d| 1
e| 1""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1169,5 +1169,35 @@ SELECT cast(contacts as MAP<varchar, variant>) contacts
d| null|1
e| {"f":1}|1""");
}

@Test
public void issue4890() {
this.getCC("""
CREATE TABLE tbl(
roww ROW(i1 INT, v1 VARCHAR NULL));

CREATE MATERIALIZED VIEW v1 AS SELECT
roww IN (ROW(4,'cat')) AS roww
FROM tbl;

CREATE MATERIALIZED VIEW v2 AS SELECT
roww IN (ROW(4,'cat')) AS roww
FROM tbl;""");
}

@Test
public void issue4891() {
this.getCC("""
CREATE TABLE tbl(
roww ROW(i1 INT, v1 VARCHAR NULL));

CREATE MATERIALIZED VIEW v1 AS SELECT
roww IN (roww) AS roww
FROM tbl;

CREATE MATERIALIZED VIEW v2 AS SELECT
roww NOT IN (roww) AS roww
FROM tbl;""");
}
}

2 changes: 1 addition & 1 deletion sql-to-dbsp-compiler/calcite_version.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ CALCITE_REPO="https://github.com/apache/calcite.git"
#CALCITE_REPO="https://github.com/mihaibudiu/calcite"
CALCITE_BRANCH="main"
CALCITE_CURRENT="1.40.0"
CALCITE_NEXT_COMMIT="0af5284d9f004fd9e38e2d62abd8c04de004be5c"
CALCITE_NEXT_COMMIT="5b3cf922657dcdd72eed7d45093db82cc80d0efc"
CALCITE_NEXT="1.41.0"