diff --git a/distribution/std-lib/Standard/src/Base/Data/Json.enso b/distribution/std-lib/Standard/src/Base/Data/Json.enso index 9fd40a067f3b..b403e5d9640a 100644 --- a/distribution/std-lib/Standard/src/Base/Data/Json.enso +++ b/distribution/std-lib/Standard/src/Base/Data/Json.enso @@ -117,8 +117,8 @@ type Marshalling_Error to_display_text : Text to_display_text = case this of Type_Mismatch_Error json format -> - json_text = Meta.type_to_display_text json - format_text = Meta.type_to_display_text format + json_text = Meta.get_simple_type_name json + format_text = Meta.get_simple_type_name format "Type mismatch error: the json with type `" + json_text + "` did not match the format `" + format_text + "`." Missing_Field_Error _ field _ -> "Missing field in Json: the field `" + field.to_text "` was missing in the json." diff --git a/distribution/std-lib/Standard/src/Base/Meta.enso b/distribution/std-lib/Standard/src/Base/Meta.enso index 36341e442b5c..38c25d9eb14c 100644 --- a/distribution/std-lib/Standard/src/Base/Meta.enso +++ b/distribution/std-lib/Standard/src/Base/Meta.enso @@ -148,9 +148,20 @@ get_source_location : Integer -> Text get_source_location skip_frames = Builtins.Meta.get_source_location skip_frames+1 -## Displays the type of the provided value as text. +## PRIVATE + + Displays the type of the provided value as text. Arguments: - value: The value for which to display the type. -type_to_display_text : Any -> Text -type_to_display_text value = Builtins.Meta.type_to_display_text value +get_simple_type_name : Any -> Text +get_simple_type_name value = Builtins.Meta.get_simple_type_name value + +## PRIVATE + + Returns the fully qualified type name of the given value. + + Arguments: + - value: the value to get the type of. +get_qualified_type_name : Any -> Text +get_qualified_type_name value = Builtins.Meta.get_qualified_type_name value diff --git a/distribution/std-lib/Standard/src/Database/Connection/Connection.enso b/distribution/std-lib/Standard/src/Database/Connection/Connection.enso index 21af2ad5b18b..fae4fa852ec3 100644 --- a/distribution/std-lib/Standard/src/Database/Connection/Connection.enso +++ b/distribution/std-lib/Standard/src/Database/Connection/Connection.enso @@ -69,7 +69,10 @@ type Connection ncols = metadata.getColumnCount column_names = Vector.new ncols ix-> metadata.getColumnName ix+1 column_types = if expected_types.is_nothing.not then expected_types else - Vector.new ncols (ix -> Sql_Type <| metadata.getColumnType ix+1) + Vector.new ncols ix-> + typeid = metadata.getColumnType ix+1 + name = metadata.getColumnTypeName ix+1 + Sql_Type typeid name column_builders = column_types.map typ-> here.create_builder typ go has_next = if has_next.not then Nothing else @@ -137,8 +140,9 @@ type Connection ncols = metadata.getColumnCount resolve_column ix = name = metadata.getColumnName ix+1 - typ = metadata.getColumnType ix+1 - [name, Sql_Type typ] + typeid = metadata.getColumnType ix+1 + typename = metadata.getColumnTypeName ix+1 + [name, Sql_Type typeid typename] Vector.new ncols resolve_column ## PRIVATE diff --git a/distribution/std-lib/Standard/src/Database/Data/Column.enso b/distribution/std-lib/Standard/src/Database/Data/Column.enso index 465eb9eec036..43505257ac13 100644 --- a/distribution/std-lib/Standard/src/Database/Data/Column.enso +++ b/distribution/std-lib/Standard/src/Database/Data/Column.enso @@ -459,6 +459,14 @@ type Aggregate_Column mean name_suffix='_mean' = here.make_aggregate this "AVG" name_suffix + ## PRIVATE + + Helper that returns the underlying column from before grouping. + ungrouped : Column + ungrouped = + new_ctx = this.context.set_groups [] + Column this.name this.connection this.sql_type this.expression new_ctx + ## PRIVATE A helper method for creating an aggregated column by applying some diff --git a/distribution/std-lib/Standard/src/Database/Data/Sql.enso b/distribution/std-lib/Standard/src/Database/Data/Sql.enso index 702aa4b93155..cb77b3a7442b 100644 --- a/distribution/std-lib/Standard/src/Database/Data/Sql.enso +++ b/distribution/std-lib/Standard/src/Database/Data/Sql.enso @@ -10,23 +10,28 @@ type Sql_Type Arguments: - typeid: a numerical type id, as defined in `java.sql.Types`. - type Sql_Type typeid + - name: a database-specific type name, used for pretty printing. + type Sql_Type typeid name ## The SQL representation of `Boolean` type. boolean : Sql_Type - boolean = Sql_Type Types.BOOLEAN + boolean = Sql_Type Types.BOOLEAN "BOOLEAN" ## The SQL representation of `Integer` type. integer : Sql_Type - integer = Sql_Type Types.INTEGER + integer = Sql_Type Types.INTEGER "INTEGER" ## The SQL type representing decimal numbers. decimal : Sql_Type - decimal = Sql_Type Types.DECIMAL + decimal = Sql_Type Types.DECIMAL "DECIMAL" ## The SQL type representing a general numeric type. numeric : Sql_Type - numeric = Sql_Type Types.NUMERIC + numeric = Sql_Type Types.NUMERIC "NUMERIC" + + ## The SQL type representing one of the suppported textual types. + varchar : Sql_Type + varchar = Sql_Type Types.VARCHAR "VARCHAR" ## PRIVATE @@ -42,7 +47,7 @@ type Sql_Type standard types so it may return false negatives for non-standard ones. is_definitely_boolean : Boolean is_definitely_boolean = - this.typeid == Types.BOOLEAN + [Types.BOOLEAN, Types.BIT].contains this.typeid ## PRIVATE @@ -53,6 +58,12 @@ type Sql_Type is_definitely_double = [Types.FLOAT, Types.DOUBLE, Types.REAL].contains this.typeid + ## PRIVATE + Returns True if this type represents a Text. + is_definitely_text : Boolean + is_definitely_text = + [Types.VARCHAR, Types.LONGVARCHAR, Types.NVARCHAR, Types.LONGNVARCHAR].contains this.typeid + ## UNSTABLE A fragment of a SQL query. @@ -137,8 +148,8 @@ type Statement to_json = jsonify fragment = case fragment of Sql_Code_Part code -> Json.from_pairs [["sql_code", code]] - Sql_Interpolation (Sql_Type typeid) obj -> - inner = Json.from_pairs [["value", obj.to_json], ["typeid", typeid]] + Sql_Interpolation typ obj -> + inner = Json.from_pairs [["value", obj], ["expected_sql_type", typ.name]] Json.from_pairs [["sql_interpolation", inner]] fragments = Json.Array (this.internal_fragments.map jsonify) Json.from_pairs [["query", fragments]] diff --git a/distribution/std-lib/Standard/src/Database/Data/Table.enso b/distribution/std-lib/Standard/src/Database/Data/Table.enso index 8097deb16f2e..d22777941f1f 100644 --- a/distribution/std-lib/Standard/src/Database/Data/Table.enso +++ b/distribution/std-lib/Standard/src/Database/Data/Table.enso @@ -165,13 +165,22 @@ type Table turned_into_index.not this.updated_context new_ctx . updated_columns new_cols - ## Returns the index (or indexes) of this table, as a column (indexed by itself). - Returns `Nothing` if there is no index set. - index : Column | Vector Column | Nothing + ## UNSTABLE + + Returns the (possibly empty) list of indices for this table. + indices : Vector Column + indices = + this.context.meta_index.map this.make_column + + ## UNSTABLE + + Returns the index (or indexes) of this table, as a column (indexed by itself). + Throws `No_Index_Set_Error` if there is no index set. + index : Column | Vector Column ! Materialized_Table.No_Index_Set_Error index = - ixes = this.context.meta_index.map this.make_column - len = this.context.meta_index.length - if len == 0 then Nothing else + ixes = this.indices + len = ixes.length + if len == 0 then Error.throw Materialized_Table.No_Index_Set_Error else if len == 1 then ixes.at 0 else ixes ## UNSTABLE @@ -446,10 +455,7 @@ type Table count_columns = cols.map c-> IR.Internal_Column c.name Sql.Sql_Type.integer (IR.Operation "COUNT" [c.expression]) count_table = this.updated_columns count_columns . to_dataframe counts = count_table.columns.map c-> c.at 0 - column_type_as_text col = - id = col.sql_type.typeid - JDBCType.valueOf id . getName - types = cols.map column_type_as_text + types = cols.map c-> c.sql_type.name Materialized_Table.new [["Column", cols.map .name], ["Items Count", counts], ["SQL Type", types]] . set_index "Column" ## PRIVATE @@ -540,6 +546,14 @@ type Aggregate_Table make_column internal = Aggregate_Column internal.name this.connection internal.sql_type internal.expression this.context + ## PRIVATE + + Helper that returns the underlying table from before grouping. + ungrouped : Table + ungrouped = + new_ctx = this.context.set_groups [] + Table this.name this.connection this.internal_columns new_ctx + type Integrity_Error ## UNSTABLE diff --git a/distribution/std-lib/Standard/src/Table/Data/Column.enso b/distribution/std-lib/Standard/src/Table/Data/Column.enso index 1547bf334810..1b2d274aaff0 100644 --- a/distribution/std-lib/Standard/src/Table/Data/Column.enso +++ b/distribution/std-lib/Standard/src/Table/Data/Column.enso @@ -222,10 +222,10 @@ type Column count = this.length - this.count_missing ## Returns the index of this column, as a column (indexed by itself). - Returns `Nothing` if there is no index set. - index : Column | Nothing + Throws `No_Index_Set_Error` if there is no index set. + index : Column ! Table.No_Index_Set_Error index = case this.java_column.getIndex.toColumn of - Nothing -> Nothing + Nothing -> Error.throw Table.No_Index_Set_Error i -> Column i ## Returns the item contained in this column at the given index. @@ -378,6 +378,13 @@ type Column new_col = this.java_column.applyMask mask Column new_col + ## UNSTABLE + + Returns a column truncated to at most `max_rows` rows. + take_start : Integer -> Column + take_start max_rows = + Column (this.java_column.slice 0 max_rows) + ## Creates a new column given a name and a vector of elements. from_vector : Text -> Vector -> Column from_vector name items = Column (Java_Column.fromItems name items.to_array) diff --git a/distribution/std-lib/Standard/src/Table/Data/Table.enso b/distribution/std-lib/Standard/src/Table/Data/Table.enso index 65e88f882ca9..48d67cfd2adc 100644 --- a/distribution/std-lib/Standard/src/Table/Data/Table.enso +++ b/distribution/std-lib/Standard/src/Table/Data/Table.enso @@ -12,6 +12,9 @@ polyglot java import org.enso.table.operations.OrderBuilder ## An error returned when a non-existent column is being looked up. type No_Such_Column_Error column_name +## An error returned when getting an index but no index is set for that table. +type No_Index_Set_Error + ## Represents a column-oriented table data structure. type Table type Table java_table @@ -108,10 +111,10 @@ type Table Table (this.java_table.indexFromColumn index) ## Returns the index of this table, as a column (indexed by itself). - Returns `Nothing` if there is no index set. - index : Column.Column | Nothing + Throws `No_Index_Set_Error` if there is no index set. + index : Column.Column ! No_Index_Set_Error index = case this.java_table.getIndex.toColumn of - Nothing -> Nothing + Nothing -> Error.throw No_Index_Set_Error i -> Column.Column i ## Selects a subset of columns from this table by name. @@ -306,6 +309,13 @@ type Table concat other = Table (this.java_table.concat other.java_table) + ## UNSTABLE + + Returns a table truncated to at most `max_rows` rows. + take_start : Integer -> Table + take_start max_rows = + Table (this.java_table.slice 0 max_rows) + ## PRIVATE comparator_to_java cmp x y = cmp x y . to_sign diff --git a/distribution/std-lib/Standard/src/Test.enso b/distribution/std-lib/Standard/src/Test.enso index dcd4422d704f..e3c2a9241cb2 100644 --- a/distribution/std-lib/Standard/src/Test.enso +++ b/distribution/std-lib/Standard/src/Test.enso @@ -93,20 +93,25 @@ Error.should_equal _ = Panic.throw (Matched_On_Error this) Decimal.should_equal that (epsilon = 0) = case this.equals that epsilon of True -> Success False -> - msg = this.to_text + " did not equal " + that.to_text + "." + loc = Meta.get_source_location 2 + msg = this.to_text + " did not equal " + that.to_text + " (at " + loc + ")." Panic.throw (Failure msg) ## Asserts that the given `Boolean` is `True` Boolean.should_be_true = case this of True -> Success - False -> Panic.throw (Failure "Expected False to be True.") + False -> + loc = Meta.get_source_location 2 + Panic.throw (Failure "Expected False to be True (at "+loc+").") ## Asserts that the given `Boolean` is `True`. Error.should_be_true = Panic.throw (Matched_On_Error this) ## Asserts that the given `Boolean` is `False` Boolean.should_be_false = case this of - True -> Panic.throw (Failure "Expected True to be False.") + True -> + loc = Meta.get_source_location 2 + Panic.throw (Failure "Expected True to be False (at "+loc+").") False -> Success ## Asserts that the given `Boolean` is `False` diff --git a/distribution/std-lib/Standard/src/Visualization/Helpers.enso b/distribution/std-lib/Standard/src/Visualization/Helpers.enso new file mode 100644 index 000000000000..612572b6d58a --- /dev/null +++ b/distribution/std-lib/Standard/src/Visualization/Helpers.enso @@ -0,0 +1,8 @@ +from Standard.Base import all + +## PRIVATE +recover_errors ~body = + result = Panic.recover body + result.catch err-> + Json.from_pairs [["error", err.to_display_text]] . to_text + diff --git a/distribution/std-lib/Standard/src/Visualization/Sql/Visualization.enso b/distribution/std-lib/Standard/src/Visualization/Sql/Visualization.enso new file mode 100644 index 000000000000..a1ca4f77e141 --- /dev/null +++ b/distribution/std-lib/Standard/src/Visualization/Sql/Visualization.enso @@ -0,0 +1,39 @@ +from Standard.Base import all + +import Standard.Visualization.Helpers + +## PRIVATE + + Prepares the query for visualization. + + For each interpolation it provides its value, its actual type name, its + expected SQL type name and if it was possible to infer it, its expected Enso + typename. + + Expected Enso types are inferred based on known SQL types and their mapping + to Enso types. +prepare_visualization x = Helpers.recover_errors <| + prepared = x.to_sql.prepare + code = prepared.first + interpolations = prepared.second + mapped = interpolations.map e-> + value = e.first + actual_type = Meta.get_qualified_type_name value + expected_sql_type = e.second.name + expected_enso_type = here.find_expected_enso_type_for_sql e.second + Json.from_pairs [["value", value], ["actual_type", actual_type], ["expected_sql_type", expected_sql_type], ["expected_enso_type", expected_enso_type]] + dialect = x.connection.dialect.name + Json.from_pairs [["dialect", dialect], ["code", code], ["interpolations", mapped]] . to_text + +## PRIVATE + + Return an expected Enso type for an SQL type. + + Expected Enso types are only inferred for some known SQL types. For unknown + types it will return `Nothing`. +find_expected_enso_type_for_sql sql_type = + if sql_type.is_definitely_integer then "Builtins.Main.Integer" else + if sql_type.is_definitely_double then "Builtins.Main.Decimal" else + if sql_type.is_definitely_text then "Builtins.Main.Text" else + if sql_type.is_definitely_boolean then "Builtins.Main.Boolean" else + Nothing diff --git a/distribution/std-lib/Standard/src/Visualization/Table/Visualization.enso b/distribution/std-lib/Standard/src/Visualization/Table/Visualization.enso new file mode 100644 index 000000000000..f0bf949ac67e --- /dev/null +++ b/distribution/std-lib/Standard/src/Visualization/Table/Visualization.enso @@ -0,0 +1,72 @@ +from Standard.Base import all + +import Standard.Table.Data.Table as Dataframe_Table +import Standard.Table.Data.Column as Dataframe_Column +import Standard.Database.Data.Table as Database_Table +import Standard.Database.Data.Column as Database_Column +import Standard.Visualization.Helpers + +# TODO add an initial offset to fully support lazy visualizations +## PRIVATE + + Prepares a table or column for visualization. + + In case of Database backed data, it materializes a fragment of the data. +prepare_visualization x max_rows = Helpers.recover_errors <| case x of + Dataframe_Table.Table _ -> + dataframe = x.take_start max_rows + all_rows_count = x.row_count + indices = [dataframe.index] . catch _-> [] + here.make_json dataframe indices all_rows_count + + Database_Table.Table _ _ _ _ -> + # Materialize a table with indices as normal columns (because dataframe does not support multi-indexing). + df = x.reset_index.to_dataframe max_rows + # Then split into actual columns and indices. + vis_df = df.select (x.columns.map .name) + indices = df.select (x.indices.map .name) . columns + all_rows_count = x.row_count + here.make_json vis_df indices all_rows_count + + # We display columns as 1-column tables. + Dataframe_Column.Column _ -> + here.prepare_visualization x.to_table max_rows + Database_Column.Column _ _ _ _ _ -> + here.prepare_visualization x.to_table max_rows + + # We display aggregates as their ungrouped counterparts. + Dataframe_Table.Aggregate_Table _ -> + ungrouped = Dataframe_Table.Table x.java_table.getUnderlyingTable + here.prepare_visualization ungrouped max_rows + Dataframe_Column.Aggregate_Column _ -> + ungrouped = Dataframe_Column.Column x.java_column.getColumn + here.prepare_visualization ungrouped.to_table max_rows + Database_Table.Aggregate_Table _ _ _ _ -> + here.prepare_visualization x.ungrouped max_rows + Database_Column.Aggregate_Column _ _ _ _ _ -> + here.prepare_visualization x.ungrouped.to_table max_rows + + # Anything else will be visualized with the JSON or matrix visualization + _ -> + Json.from_pairs [["json", x]] . to_text + +## PRIVATE + Creates a JSON representation for the visualizations. + + Arguments: + - dataframe: the dataframe containing (possibly just a fragment of) the data + to display. + - indices: a vector of dataframe columns that should be displayed as indices; + it can be empty, they should have the same amount of rows as the + `dataframe`. + - all_rows_count: the number of all rows in the underlying data, useful if + only a fragment is displayed. +make_json dataframe indices all_rows_count = + columns = dataframe.columns + header = ["header", columns.map .name] + data = ["data", columns.map .to_vector] + all_rows = ["all_rows_count", all_rows_count] + ixes = ["indices", indices.map .to_vector] + ixes_header = ["indices_header", indices.map .name] + pairs = [header, data, all_rows, ixes, ixes_header] + Json.from_pairs pairs . to_text diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java new file mode 100644 index 000000000000..ac1a61dfeb28 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java @@ -0,0 +1,17 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.type.Types; + +@BuiltinMethod( + type = "Meta", + name = "get_qualified_type_name", + description = "Returns a qualified type name of the given value.") +public class GetQualifiedTypeNameNode extends Node { + Text execute(Object _this, Object value) { + var typeName = Types.getName(value); + return Text.create(typeName); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/DisplayTypeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetSimpleTypeNameNode.java similarity index 83% rename from engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/DisplayTypeNode.java rename to engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetSimpleTypeNameNode.java index 295bbf9b66b3..0bd95c7bbe61 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/DisplayTypeNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetSimpleTypeNameNode.java @@ -6,8 +6,8 @@ import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode; import org.enso.interpreter.runtime.data.text.Text; -@BuiltinMethod(type = "Meta", name = "type_to_display_text", description = "Pretty prints a type.") -public class DisplayTypeNode extends Node { +@BuiltinMethod(type = "Meta", name = "get_simple_type_name", description = "Pretty prints a type.") +public class GetSimpleTypeNameNode extends Node { @Child @CompilationFinal TypeToDisplayTextNode displayTypeNode = TypeToDisplayTextNode.build(); Text execute(Object _this, Object value) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Meta.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Meta.java index ebdd280f9db4..c56fddfcb2b5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Meta.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Meta.java @@ -18,7 +18,7 @@ public Meta(Language language, ModuleScope scope) { AtomConstructor meta = new AtomConstructor("Meta", scope).initializeFields(); scope.registerConstructor(meta); - scope.registerMethod(meta, "type_to_display_text", DisplayTypeMethodGen.makeFunction(language)); + scope.registerMethod(meta, "get_simple_type_name", GetSimpleTypeNameMethodGen.makeFunction(language)); scope.registerMethod( meta, "is_unresolved_symbol", IsUnresolvedSymbolMethodGen.makeFunction(language)); scope.registerMethod( @@ -53,5 +53,7 @@ public Meta(Language language, ModuleScope scope) { scope.registerMethod(meta, "is_same_object", IsSameObjectMethodGen.makeFunction(language)); scope.registerMethod( meta, "get_source_location", GetSourceLocationMethodGen.makeFunction(language)); + scope.registerMethod( + meta, "get_qualified_type_name", GetQualifiedTypeNameMethodGen.makeFunction(language)); } } diff --git a/engine/runtime/src/main/resources/Builtins.enso b/engine/runtime/src/main/resources/Builtins.enso index 14a1024f21da..5a21860e1c19 100644 --- a/engine/runtime/src/main/resources/Builtins.enso +++ b/engine/runtime/src/main/resources/Builtins.enso @@ -731,13 +731,24 @@ type Meta get_source_location : Integer -> Text get_source_location frames_to_skip = @Builtin_Method "Meta.get_source_location" - ## Pretty-prints the type of the provided value using the interpreter's + ## PRIVATE + + Pretty-prints the type of the provided value using the interpreter's internal logic for printing types. Arguments: - value: The value whose type should be printed. - type_to_display_text : Any -> Text - type_to_display_text value = @Builtin_Method "Meta.display_type" + get_simple_type_name : Any -> Text + get_simple_type_name value = @Builtin_Method "Meta.get_simple_type_name" + + ## PRIVATE + + Returns the fully qualified type name of the given value. + + Arguments: + - value: the value to get the type of. + get_qualified_type_name : Any -> Text + get_qualified_type_name value = @Builtin_Method "Meta.get_qualified_type_name" ## Utilities for working with primitive arrays. type Array diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/BoolStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/BoolStorage.java index bb15ef675e8f..3169ae5cf5f7 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/BoolStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/BoolStorage.java @@ -2,7 +2,6 @@ import java.util.BitSet; import java.util.Comparator; - import org.enso.table.data.column.operation.map.MapOpStorage; import org.enso.table.data.column.operation.map.MapOperation; import org.enso.table.data.column.operation.map.UnaryMapOperation; @@ -307,4 +306,14 @@ public static BitSet toMask(BoolStorage storage) { public Comparator getDefaultComparator() { return Comparator.naturalOrder(); } + + @Override + public BoolStorage slice(int offset, int limit) { + int newSize = Math.min(size - offset, limit); + return new BoolStorage( + values.get(offset, offset + limit), + isMissing.get(offset, offset + limit), + newSize, + negated); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java index ccaf20546b95..9d67d3da8635 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/DoubleStorage.java @@ -256,4 +256,13 @@ public Storage run(DoubleStorage storage) { }); return ops; } + + @Override + public DoubleStorage slice(int offset, int limit) { + int newSize = Math.min(size - offset, limit); + long[] newData = new long[newSize]; + System.arraycopy(data, offset, newData, 0, newSize); + BitSet newMask = isMissing.get(offset, offset + limit); + return new DoubleStorage(newData, newSize, newMask); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/LongStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/LongStorage.java index 05cc967cdd2c..e82ac862e75a 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/LongStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/LongStorage.java @@ -370,4 +370,13 @@ public Storage run(LongStorage storage) { }); return ops; } + + @Override + public LongStorage slice(int offset, int limit) { + int newSize = Math.min(size - offset, limit); + long[] newData = new long[newSize]; + System.arraycopy(data, offset, newData, 0, newSize); + BitSet newMask = isMissing.get(offset, offset + limit); + return new LongStorage(newData, newSize, newMask); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/NumericStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/NumericStorage.java index 57bef25a4e7d..9e8ee2c17bcb 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/NumericStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/NumericStorage.java @@ -1,10 +1,9 @@ package org.enso.table.data.column.storage; +import java.util.stream.DoubleStream; import org.enso.table.data.column.operation.aggregate.Aggregator; import org.enso.table.data.column.operation.aggregate.numeric.NumericAggregator; -import java.util.stream.DoubleStream; - /** A storage containing items representable as a {@code double}. */ public abstract class NumericStorage extends Storage { /** diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/ObjectStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/ObjectStorage.java index 38369a5f245c..17ea310a79fa 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/ObjectStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/ObjectStorage.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.storage; +import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; @@ -145,4 +146,12 @@ protected Storage run(ObjectStorage storage) { }); return ops; } + + @Override + public ObjectStorage slice(int offset, int limit) { + int newSize = Math.min(size - offset, limit); + Object[] newData = new Object[newSize]; + System.arraycopy(data, offset, newData, 0, newSize); + return new ObjectStorage(newData, newSize); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/Storage.java index e30967b9b882..565a2d6de9b7 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -252,6 +252,9 @@ protected final Storage fillMissingHelper(Object arg, Builder builder) { */ public abstract Comparator getDefaultComparator(); + /** @return a copy of the storage containing a slice of the original data */ + public abstract Storage slice(int offset, int limit); + public List toList() { return new StorageListView(this); } diff --git a/std-bits/src/main/java/org/enso/table/data/column/storage/StringStorage.java b/std-bits/src/main/java/org/enso/table/data/column/storage/StringStorage.java index 125ba0ddf30e..815148c81763 100644 --- a/std-bits/src/main/java/org/enso/table/data/column/storage/StringStorage.java +++ b/std-bits/src/main/java/org/enso/table/data/column/storage/StringStorage.java @@ -138,4 +138,10 @@ protected boolean doString(String a, String b) { }); return t; } + + @Override + public StringStorage slice(int offset, int limit) { + ObjectStorage storage = super.slice(offset, limit); + return new StringStorage(storage.getData(), storage.size()); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/index/DefaultIndex.java b/std-bits/src/main/java/org/enso/table/data/index/DefaultIndex.java index 5b5d5ebe9edb..c7c016f91b61 100644 --- a/std-bits/src/main/java/org/enso/table/data/index/DefaultIndex.java +++ b/std-bits/src/main/java/org/enso/table/data/index/DefaultIndex.java @@ -73,4 +73,9 @@ public int size() { public Index applyMask(OrderMask mask) { return this; } + + @Override + public DefaultIndex slice(int offset, int limit) { + return new DefaultIndex(Math.min(size, limit)); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/index/HashIndex.java b/std-bits/src/main/java/org/enso/table/data/index/HashIndex.java index 4bba30a25ef3..acdf351f61ac 100644 --- a/std-bits/src/main/java/org/enso/table/data/index/HashIndex.java +++ b/std-bits/src/main/java/org/enso/table/data/index/HashIndex.java @@ -93,4 +93,10 @@ public Index unique() { public int size() { return items.size(); } + + @Override + public HashIndex slice(int offset, int limit) { + var newStorage = items.slice(offset, limit); + return new HashIndex(name, newStorage, newStorage.size()); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/index/Index.java b/std-bits/src/main/java/org/enso/table/data/index/Index.java index d47eed7aba90..fb6d76b4e001 100644 --- a/std-bits/src/main/java/org/enso/table/data/index/Index.java +++ b/std-bits/src/main/java/org/enso/table/data/index/Index.java @@ -83,4 +83,7 @@ public abstract class Index { /** @return the number of elements in this index. */ public abstract int size(); + + /** @return a copy of the index containing a slice of the original data */ + public abstract Index slice(int offset, int limit); } diff --git a/std-bits/src/main/java/org/enso/table/data/table/Column.java b/std-bits/src/main/java/org/enso/table/data/table/Column.java index cf0f86f06e54..fd2477f6b3f1 100644 --- a/std-bits/src/main/java/org/enso/table/data/table/Column.java +++ b/std-bits/src/main/java/org/enso/table/data/table/Column.java @@ -4,7 +4,6 @@ import java.util.List; import java.util.function.Function; import java.util.stream.IntStream; - import org.enso.table.data.column.builder.object.InferredBuilder; import org.enso.table.data.column.operation.aggregate.Aggregator; import org.enso.table.data.column.storage.BoolStorage; @@ -167,4 +166,9 @@ public Column applyMask(OrderMask mask) { Storage newStorage = storage.applyMask(mask); return new Column(name, newIndex, newStorage); } + + /** @return a copy of the Column containing a slice of the original data */ + public Column slice(int offset, int limit) { + return new Column(name, index.slice(offset, limit), storage.slice(offset, limit)); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/table/Table.java b/std-bits/src/main/java/org/enso/table/data/table/Table.java index 0daad9ce1140..9921c11ea40d 100644 --- a/std-bits/src/main/java/org/enso/table/data/table/Table.java +++ b/std-bits/src/main/java/org/enso/table/data/table/Table.java @@ -376,4 +376,13 @@ public AggregateTable group(String by) { Table t = by == null ? this : indexFromColumn(by); return new AggregateTable(t); } + + /** @return a copy of the Column containing a slice of the original data */ + public Table slice(int offset, int limit) { + Column[] newColumns = new Column[columns.length]; + for (int i = 0; i < columns.length; i++) { + newColumns[i] = columns[i].slice(offset, limit); + } + return new Table(newColumns, index.slice(offset, limit)); + } } diff --git a/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateColumn.java b/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateColumn.java index 65ae59f87899..d96db1adfd18 100644 --- a/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateColumn.java +++ b/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateColumn.java @@ -53,7 +53,7 @@ public Column aggregate( return new Column(column.getName() + outSuffix, uniqueIndex, aggregator.seal()); } - /** @return the underlying (ungroupped) column. */ + /** @return the underlying (ungrouped) column. */ public Column getColumn() { return column; } diff --git a/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateTable.java b/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateTable.java index 9e9b74633b45..229345013bcc 100644 --- a/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateTable.java +++ b/std-bits/src/main/java/org/enso/table/data/table/aggregate/AggregateTable.java @@ -51,4 +51,9 @@ public AggregateColumn[] getColumns() { .map(c -> new AggregateColumn(uniqueIndex, c)) .toArray(AggregateColumn[]::new); } + + /** @return the underlying (ungrouped) table. */ + public Table getUnderlyingTable() { + return table; + } } diff --git a/test/Database_Tests/src/Codegen_Spec.enso b/test/Database_Tests/src/Codegen_Spec.enso index ccfe5faaf9f8..e07215ef41a9 100644 --- a/test/Database_Tests/src/Codegen_Spec.enso +++ b/test/Database_Tests/src/Codegen_Spec.enso @@ -11,7 +11,7 @@ from Standard.Table import No_Such_Column_Error, Order_Rule spec = int = Sql_Type.integer bool = Sql_Type.boolean - str = Sql_Type 424242 + str = Sql_Type.varchar test_connection = table1 = ["T1", [["A", int], ["B", str], ["C", bool]]] table2 = ["T2", [["D", int], ["E", int], ["F", bool]]] @@ -23,7 +23,7 @@ spec = Test.specify "should serialize Tables and Columns to their SQL representation" <| q1 = t1.where (t1.at "A" == 42) . to_json part1 = Json.from_pairs [["sql_code", 'SELECT "T1"."A" AS "A", "T1"."B" AS "B", "T1"."C" AS "C" FROM "T1" AS "T1" WHERE ("T1"."A" = ']] - interp = Json.from_pairs [["value", 42], ["typeid", int.typeid]] + interp = Json.from_pairs [["value", 42], ["expected_sql_type", "INTEGER"]] part2 = Json.from_pairs [["sql_interpolation", interp]] part3 = Json.from_pairs [["sql_code", ")"]] expected = Json.from_pairs [["query", Json.Array [part1, part2, part3]]] diff --git a/test/Database_Tests/src/Postgresql_Spec.enso b/test/Database_Tests/src/Postgresql_Spec.enso index b9de4d4edf54..54fadfddb9af 100644 --- a/test/Database_Tests/src/Postgresql_Spec.enso +++ b/test/Database_Tests/src/Postgresql_Spec.enso @@ -8,16 +8,21 @@ import Database_Tests.Common_Spec postgres_specific_spec connection pending = Test.group "[PostgreSQL] Info" pending=pending <| + connection.execute_update 'CREATE TABLE "Tinfo" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' + t = connection.access_table "Tinfo" + t.insert ["a", Nothing, False, 1.2] + t.insert ["abc", Nothing, Nothing, 1.3] + t.insert ["def", 42, True, 1.4] Test.specify "should return Table information" <| - connection.execute_update 'CREATE TABLE "Tinfo" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' - t = connection.access_table "Tinfo" - t.insert ["a", Nothing, False, 1.2] - t.insert ["abc", Nothing, Nothing, 1.3] - t.insert ["def", 42, True, 1.4] i = t.info i.index . to_vector . should_equal ["strs", "ints", "bools", "reals"] i.at "Items Count" . to_vector . should_equal [3, 1, 2, 3] - i.at "SQL Type" . to_vector . should_equal ["VARCHAR", "INTEGER", "BIT", "REAL"] + i.at "SQL Type" . to_vector . should_equal ["varchar", "int4", "bool", "float4"] + Test.specify "should infer standard types correctly" <| + t.at "strs" . sql_type . is_definitely_text . should_be_true + t.at "ints" . sql_type . is_definitely_integer . should_be_true + t.at "bools" . sql_type . is_definitely_boolean . should_be_true + t.at "reals" . sql_type . is_definitely_double . should_be_true connection.execute_update 'DROP TABLE "Tinfo"' Test.group "[PostgreSQL] Materialization" pending=pending <| Test.specify "should return DECIMAL columns as BigDecimal" <| diff --git a/test/Database_Tests/src/Sqlite_Spec.enso b/test/Database_Tests/src/Sqlite_Spec.enso index 9ab43378bf9d..b863c594b9c3 100644 --- a/test/Database_Tests/src/Sqlite_Spec.enso +++ b/test/Database_Tests/src/Sqlite_Spec.enso @@ -15,17 +15,22 @@ sqlite_specific_spec connection = action . should_fail_with Sql_Error action.catch.to_text . should_equal "[SQLITE_ERROR] SQL error or missing database (no such table: undefined_table)" - Test.group "[SQLite] Info" <| + Test.group "[SQLite] Metadata" <| + connection.execute_update 'CREATE TABLE "Tinfo" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "reals" REAL)' + t = connection.access_table "Tinfo" + t.insert ["a", Nothing, False, 1.2] + t.insert ["abc", Nothing, Nothing, 1.3] + t.insert ["def", 42, True, 1.4] Test.specify "should return Table information" <| - connection.execute_update 'CREATE TABLE "Tinfo" ("strs" VARCHAR, "ints" INTEGER, "bools" BOOLEAN, "decimals" DECIMAL)' - t = connection.access_table "Tinfo" - t.insert ["a", Nothing, False, 1.2] - t.insert ["abc", Nothing, Nothing, 1.3] - t.insert ["def", 42, True, 1.4] i = t.info - i.index . to_vector . should_equal ["strs", "ints", "bools", "decimals"] + i.index . to_vector . should_equal ["strs", "ints", "bools", "reals"] i.at "Items Count" . to_vector . should_equal [3, 1, 2, 3] - i.at "SQL Type" . to_vector . should_equal ["VARCHAR", "INTEGER", "BOOLEAN", "DECIMAL"] + i.at "SQL Type" . to_vector . should_equal ["VARCHAR", "INTEGER", "BOOLEAN", "REAL"] + Test.specify "should infer standard types correctly" <| + t.at "strs" . sql_type . is_definitely_text . should_be_true + t.at "ints" . sql_type . is_definitely_integer . should_be_true + t.at "bools" . sql_type . is_definitely_boolean . should_be_true + t.at "reals" . sql_type . is_definitely_double . should_be_true connection.execute_update 'DROP TABLE "Tinfo"' spec = diff --git a/test/Table_Tests/src/Table_Spec.enso b/test/Table_Tests/src/Table_Spec.enso index 567915974ce7..5a6ad66e26ca 100644 --- a/test/Table_Tests/src/Table_Spec.enso +++ b/test/Table_Tests/src/Table_Spec.enso @@ -501,3 +501,22 @@ spec = r = t_1.concat t_2 r.index.to_vector.should_equal [1, 2, 3, False, True] + + Test.group "Slicing Tables" <| + Test.specify 'should allow to take first N elements' <| + i_1 = ['ix', [1, 2, 3]] + c_1 = ['col', [5, 6, 7]] + c_2 = ['col2', ["a", Nothing, "c"]] + c_3 = ['col3', [False, True, Nothing]] + t_1 = Table.new [i_1, c_1, c_2, c_3] . set_index 'ix' + + t_1.take_start 10 . at 'col' . to_vector . should_equal (t_1.at 'col' . to_vector) + t_1.at 'col' . take_start 10 . to_vector . should_equal (t_1.at 'col' . to_vector) + + t_2 = t_1.take_start 2 + t_2.index.to_vector . should_equal (t_1.index.to_vector . take_start 2) + t_2.at 'col' . to_vector . should_equal (t_1.at 'col' . to_vector . take_start 2) + t_2.at 'col2' . to_vector . should_equal (t_1.at 'col2' . to_vector . take_start 2) + t_2.at 'col3' . to_vector . should_equal (t_1.at 'col3' . to_vector . take_start 2) + + t_1.at 'col' . take_start 2 . to_vector . should_equal (t_1.at 'col' . to_vector . take_start 2) diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index cef8f3611b16..3f8b62005447 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -82,3 +82,9 @@ spec = Test.group "Meta-Value Manipulation" <| src = Meta.get_source_location 0 loc = "Meta_Spec.enso:82:15-40" src.take_last loc.length . should_equal loc + + Test.specify "should allow to get qualified type names of values" <| + x = 42 + y = My_Type 1 2 3 + Meta.get_qualified_type_name x . should_equal "Builtins.Main.Integer" + Meta.get_qualified_type_name y . should_equal "Tests.Semantic.Meta_Spec.My_Type"