diff --git a/CHANGELOG.md b/CHANGELOG.md index d124b99813ed..2ea20ed74027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,23 +9,25 @@ #### Enso Standard Library - [Multiline if_then_else][14522]. -- [Using dual JVM mode for Standard.Microsoft][14476]. +- [Using dual JVM mode for `Standard.Microsoft`][14476]. - [Standard.Test pending field is lazy][14536]. -- [Using dual JVM mode for Standard.AWS][14568]. +- [Using dual JVM mode for `Standard.AWS`][14568]. +- [Running `Standard.Tableau` in dual JVM mode][14607]. - [Polishing Standard.Test API][14599]. [14522]: https://github.com/enso-org/enso/pull/14522 [14476]: https://github.com/enso-org/enso/pull/14476 [14536]: https://github.com/enso-org/enso/pull/14536 [14568]: https://github.com/enso-org/enso/pull/14568 +[14607]: https://github.com/enso-org/enso/pull/14607 [14599]: https://github.com/enso-org/enso/pull/14599 #### Enso Language & Runtime - [`Panic.rethrow` keeps original location][14480] - [Use `State.get if_missing` to avoid too frequent `Panic.throw`][14490] -- [Lazily initialized local variables with Ref.memoize][14554]. -- [Flush system caches via Runtime.gc][14557] +- [Lazily initialized local variables with `Ref.new lazy=True`][14554]. +- [Flush system caches via `Runtime.gc`][14557] [14480]: https://github.com/enso-org/enso/pull/14480 [14490]: https://github.com/enso-org/enso/pull/14490 diff --git a/build.sbt b/build.sbt index ff80b9a5b8ce..5c3074644a59 100644 --- a/build.sbt +++ b/build.sbt @@ -3828,9 +3828,6 @@ lazy val `engine-runner` = project `database-polyglot-root` .listFiles("*.jar") .map(_.getAbsolutePath()) ++ - `std-tableau-polyglot-root` - .listFiles("*.jar") - .map(_.getAbsolutePath()) ++ `std-duckdb-polyglot-root` .listFiles("*.jar") .map(_.getAbsolutePath()) diff --git a/distribution/lib/Standard/Table/0.0.0-dev/docs/api/In_Memory_Table.md b/distribution/lib/Standard/Table/0.0.0-dev/docs/api/In_Memory_Table.md index 6edf27bd2a64..e836b1310473 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/docs/api/In_Memory_Table.md +++ b/distribution/lib/Standard/Table/0.0.0-dev/docs/api/In_Memory_Table.md @@ -4,5 +4,5 @@ - new columns:(Standard.Base.Data.Vector.Vector Standard.Base.Any.Any) -> (Standard.Table.Table.Table|Standard.Base.Any.Any) - to_delimited self delimiter:(Standard.Table.Delimited.Delimited_Format.Delimited_Format|Standard.Base.Data.Text.Text)= quote_style:Standard.Table.Delimited.Quote_Style.Quote_Style= headers:Standard.Table.Headers.Headers= value_formatter:(Standard.Table.Data_Formatter.Data_Formatter|Standard.Base.Nothing.Nothing)= line_endings:(Standard.Base.Data.Text.Line_Ending_Style.Line_Ending_Style|Standard.Base.System.File_Format.Infer)= -> Standard.Base.Data.Text.Text - from_java_table java_table:Standard.Base.Any.Any -> Standard.Base.Any.Any -- to_java_table table:Standard.Table.In_Memory_Table.In_Memory_Table -> Standard.Base.Any.Any +- to_java_table table:Standard.Table.In_Memory_Table.In_Memory_Table table_class:Standard.Base.Any.Any= column_class:Standard.Base.Any.Any= -> Standard.Base.Any.Any - Standard.Table.Table.Table.from that:Standard.Table.In_Memory_Table.In_Memory_Table -> Standard.Table.Table.Table diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/In_Memory_Table.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/In_Memory_Table.enso index ecc8d9e4a7c4..d755b7fbb5ca 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/In_Memory_Table.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/In_Memory_Table.enso @@ -109,8 +109,8 @@ from_java_table java_table = private: true --- Helper method for internal use to make a Java Table from a Table. -to_java_table table:In_Memory_Table = - table.java_table +to_java_table table:In_Memory_Table table_class=Java_Table column_class=Java_Column = + table.java_table table_class column_class ## --- private: true diff --git a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_File.enso b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_File.enso index c804141485d2..cbd74cb4c8ed 100644 --- a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_File.enso +++ b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_File.enso @@ -13,6 +13,16 @@ import project.Hyper_Table.Hyper_Table import project.Internal.Telemetry polyglot java import org.enso.tableau.HyperFormat +polyglot java import java.io.File as Java_File +polyglot java import java.io.FileNotFoundException +polyglot java import java.io.IOException +polyglot java import java.io.UncheckedIOException +polyglot java import java.nio.file.AccessDeniedException +polyglot java import java.nio.file.DirectoryNotEmptyException +polyglot java import java.nio.file.FileAlreadyExistsException +polyglot java import java.nio.file.FileSystemException +polyglot java import java.nio.file.NoSuchFileException +polyglot java import java.nio.file.NotDirectoryException ## Represents a Tableau Hyper Extract file. type Hyper_File @@ -41,7 +51,7 @@ type Hyper_File Returns the list of schemas for the connection within the current database (or catalog). schemas : Vector Text - schemas self = File_Error.handle_java_exceptions self.file <| + schemas self = handle_java_exceptions self.file <| array = HyperFormat.readSchemas self.file.path Vector.from_polyglot_array array @@ -76,7 +86,7 @@ type Hyper_File @schema (hyper -> make_schema_selector hyper True) tables : Text -> Vector Hyper_Table tables self schema:Text=self.schema = if schema == "" then self.tables self.schema else - File_Error.handle_java_exceptions self.file <| + handle_java_exceptions self.file <| array = case schema of "*" -> HyperFormat.listTablesAllSchemas self.file.path _ -> HyperFormat.listTables self.file.path schema @@ -134,3 +144,33 @@ Table_Viz_Data.from (that:Hyper_File) = tables = that.tables.map t->t.table.to_text if schemas.distinct.length <= 1 then (Table_Viz_Data.GenericGrid [Table_Viz_Header.Link "Table" "Table" "read {{@Table}}"] [tables]) else (Table_Viz_Data.GenericGrid [Table_Viz_Header.Label "Schema", Table_Viz_Header.Link "Table" "Table" "read {{@Table}} {{@Schema}}"] [schemas, tables]) + +private wrap_io_exception (file : File | Nothing) io_exception = + associated_file = case io_exception of + _ : FileSystemException -> + path_from_exception = io_exception.getFile + if path_from_exception.is_nothing then file else + File.new path_from_exception + _ -> file + + ## If the file is not known, all we can do is throw a generic IO error. + This will only usually matter on stream operations, where there is no relevant file - + and so the exceptions like `NoSuchFileException` should not occur in such context. + But instead of risking a Type_Error, we just throw the more generic IO_Error. + if associated_file.is_nothing then Error.throw (File_Error.IO_Error Nothing "An IO error has occurred: "+io_exception.to_text) else case io_exception of + _ : FileNotFoundException -> Error.throw (File_Error.Not_Found associated_file) + _ : NoSuchFileException -> Error.throw (File_Error.Not_Found associated_file) + _ : FileAlreadyExistsException -> Error.throw (File_Error.Already_Exists associated_file) + _ : AccessDeniedException -> File_Error.access_denied associated_file + _ : DirectoryNotEmptyException -> Error.throw (File_Error.Directory_Not_Empty associated_file) + _ : NotDirectoryException -> Error.throw (File_Error.Not_A_Directory associated_file) + _ -> Error.throw (File_Error.IO_Error associated_file "An IO error has occurred: "+io_exception.to_text) + +private handle_java_exceptions (file : File | Nothing) ~action = + handle_io_exception caught_panic = + wrap_io_exception file caught_panic.payload + handle_unchecked_io_exception caught_panic = + wrap_io_exception file caught_panic.payload.getCause + Panic.catch IOException handler=handle_io_exception <| + Panic.catch UncheckedIOException handler=handle_unchecked_io_exception <| + action \ No newline at end of file diff --git a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_Table.enso b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_Table.enso index 976e0084fb68..52dc76741e79 100644 --- a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_Table.enso +++ b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Hyper_Table.enso @@ -13,6 +13,7 @@ import project.Hyper_File.Hyper_File polyglot java import java.sql.Types polyglot java import org.enso.tableau.HyperFormat polyglot java import org.enso.tableau.HyperTableColumn +polyglot java import org.enso.table.problems.ProblemAggregator ## An Enso representation of a Tableau Hyper Table. type Hyper_Table @@ -108,7 +109,7 @@ type Hyper_Table @max_rows Rows_To_Read.default_widget read : Rows_To_Read -> Table read self (max_rows : Rows_To_Read = ..All_Rows) = File_Error.handle_java_exceptions self.file.file <| Hyper_Errors.handle_java_exceptions <| - Java_Problems.with_problem_aggregator Problem_Behavior.Report_Warning java_problem_aggregator-> + Java_Problems.with_problem_aggregator problem_aggregator=ProblemAggregator Problem_Behavior.Report_Warning java_problem_aggregator-> row_count = if max_rows == Rows_To_Read.All_Rows then Nothing else max_rows.rows java_columns = HyperFormat.readTable self.file.file.path self.schema self.table row_count java_problem_aggregator enso_columns = java_columns.map Java_Exports.make_column_from_java_column diff --git a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Tableau_Format.enso b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Tableau_Format.enso index b22a4b5afcee..ee7699f6ce46 100644 --- a/distribution/lib/Standard/Tableau/0.0.0-dev/src/Tableau_Format.enso +++ b/distribution/lib/Standard/Tableau/0.0.0-dev/src/Tableau_Format.enso @@ -21,6 +21,8 @@ import project.Internal.Telemetry from project.Hyper_Errors import Hyper_Unsupported_Type polyglot java import org.enso.tableau.HyperFormat +polyglot java import org.enso.table.data.table.Column as Java_Column +polyglot java import org.enso.table.data.table.Table as Java_Table ## Read the file to a `Hyper_File` object. type Tableau_Format @@ -127,7 +129,8 @@ type Tableau_Format Tableau_Format.Hyper_Table _ t -> t _ -> "Extract" warning_unmatched_columns = Hyper_Errors.handle_java_exceptions <| - HyperFormat.writeTable resolved_file resolved_schema resolved_table (to_java_table table:In_Memory_Table) (on_existing_file == Existing_File_Behavior.Append) (match_columns == Match_Columns.By_Name) (on_problems == Problem_Behavior.Report_Error) + java_table = to_java_table table:In_Memory_Table Java_Table Java_Column + HyperFormat.writeTable resolved_file resolved_schema resolved_table java_table (on_existing_file == Existing_File_Behavior.Append) (match_columns == Match_Columns.By_Name) (on_problems == Problem_Behavior.Report_Error) problems = if warning_unmatched_columns.length > 0 then [Unmatched_Columns.Error warning_unmatched_columns.to_vector] else [] on_problems.attach_problems_before problems <| File.new resolved_file diff --git a/std-bits/tableau/src/main/java/org/enso/tableau/HyperFormat.java b/std-bits/tableau/src/main/java/org/enso/tableau/HyperFormat.java index 44ff2bc092a5..73d485669731 100644 --- a/std-bits/tableau/src/main/java/org/enso/tableau/HyperFormat.java +++ b/std-bits/tableau/src/main/java/org/enso/tableau/HyperFormat.java @@ -17,6 +17,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Proxy; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URI; @@ -54,6 +55,7 @@ import org.enso.table.data.table.Table; import org.enso.table.problems.ProblemAggregator; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -168,7 +170,7 @@ public URL getResource(String name) { dotIdx == -1 ? name.substring(libIdx + 1) : name.substring(libIdx + 1, dotIdx); // Windows libs don't have `lib` prefix. var libName = osLibName.startsWith("lib") ? osLibName.substring(3) : osLibName; - var bindings = Context.getCurrent().getBindings("enso"); + var bindings = getBindings(); var found = bindings.invokeMember("find_native_library", libName); try { if (found == null || found.asString() == null) { @@ -195,7 +197,7 @@ public InputStream getResourceAsStream(String name) { dotIdx == -1 ? name.substring(libIdx + 1) : name.substring(libIdx + 1, dotIdx); // Windows libs don't have `lib` prefix. var libName = osLibName.startsWith("lib") ? osLibName.substring(3) : osLibName; - var bindings = Context.getCurrent().getBindings("enso"); + var bindings = getBindings(); var found = bindings.invokeMember("find_native_library", libName); try { if (found == null || found.asString() == null) { @@ -399,8 +401,18 @@ private static TableDefinition createTable( return tableDef; } + @SuppressWarnings("unchecked") + private static StorageType toLocal(StorageType type) { + if (Proxy.isProxyClass(type.getClass())) { + var local = StorageType.fromTypeCharAndSize(type.typeChar(), type.size()); + return (StorageType) local; + } else { + return type; + } + } + private static SqlType mapEnsoTypeToSqlType(StorageType type) { - return switch (type) { + return switch (toLocal(type)) { case TextType t -> SqlType.text(); case IntegerType t -> SqlType.bigInt(); case FloatType t -> SqlType.doublePrecision(); @@ -504,7 +516,39 @@ private static void addValueToInserter(Inserter inserter, ColumnStorage stora case LocalTime lt -> inserter.add(lt); case ZonedDateTime zdt -> inserter.add(zdt); case BigDecimal bd -> inserter.add(bd); - default -> throw new HyperUnsupportedTypeError(value.toString()); + default -> { + var v = Value.asValue(value); + if (v.isDate() && v.isTime() && v.isTimeZone()) { + inserter.add(ZonedDateTime.of(v.asDate(), v.asTime(), v.asTimeZone())); + } else if (v.isDate()) { + inserter.add(v.asDate()); + } else if (v.isTime()) { + inserter.add(v.asTime()); + } else if (v.fitsInLong()) { + inserter.add(v.asLong()); + } else if (v.fitsInDouble()) { + inserter.add(v.asDouble()); + } else if (v.isBoolean()) { + inserter.add(v.asBoolean()); + } else { + var type = value.getClass().getName(); + if (v.getMetaObject() instanceof Value meta) { + type = meta.getMetaQualifiedName(); + } + if ("java.math.BigDecimal".equals(type)) { + var unscaledValue = v.invokeMember("unscaledValue"); + var scale = v.invokeMember("scale"); + if (unscaledValue != null + && unscaledValue.fitsInBigInteger() + && scale != null + && scale.fitsInInt()) { + inserter.add(new BigDecimal(unscaledValue.asBigInteger(), scale.asInt())); + return; + } + } + throw new HyperUnsupportedTypeError(value.toString() + " type: " + type); + } + } } } } @@ -588,4 +632,14 @@ private static void validateTypesMatch(ColumnStorage[] storages, TableDefinit } } } + + private static Value getBindings() { + var ctx = Context.getCurrent(); + var bindings = ctx.getPolyglotBindings().getMember("ensoBindings"); + if (bindings != null) { + return bindings.execute("enso"); + } else { + return ctx.getBindings("enso"); + } + } } diff --git a/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/reflect-config.json b/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/reflect-config.json deleted file mode 100644 index d3be9008822c..000000000000 --- a/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/reflect-config.json +++ /dev/null @@ -1,160 +0,0 @@ -[ - { - "name": "java.util.OptionalInt", - "methods": [ - { - "name": "empty", - "parameterTypes": [] - }, - { - "name": "isEmpty", - "parameterTypes": [] - }, - { - "name": "isPresent", - "parameterTypes": [] - }, - { - "name": "getAsInt", - "parameterTypes": [] - } - ] - }, -{ - "name":"com.sun.jna.CallbackProxy", - "methods":[{"name":"callback","parameterTypes":["java.lang.Object[]"] }] -}, -{ - "name":"com.sun.jna.Pointer", - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}] -}, -{ - "name":"com.sun.jna.Structure$FFIType", - "allDeclaredFields":true, - "queryAllPublicConstructors":true, - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}], - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.sun.jna.Structure$FFIType$size_t", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.sun.jna.Union", - "allDeclaredFields":true -}, -{ - "name":"com.sun.jna.Native", - "allPublicFields":true, - "allPublicMethods":true -}, -{ - "name":"com.sun.jna.ptr.IntByReference", - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}], - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.sun.jna.ptr.LongByReference", - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}], - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.sun.jna.ptr.PointerByReference", - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}], - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.tableau.hyperapi.HyperAPI", - "allPublicFields":true, - "allPublicConstructors":true, - "queryAllDeclaredMethods":true -}, -{ - "name":"com.tableau.hyperapi.HyperAPI$hyper_error_field_value", - "allDeclaredFields":true -}, -{ - "name":"com.tableau.hyperapi.HyperAPI$hyper_error_field_value$ByReference", - "allDeclaredFields":true -}, -{ - "name":"com.tableau.hyperapi.HyperAPI$hyper_error_field_value$ByValue", - "allDeclaredFields":true, - "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.tableau.hyperapi.HyperAPI$hyper_error_field_value_union", - "allDeclaredFields":true, - "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "name":"com.tableau.hyperapi.impl.BoolByReference", - "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}], - "methods":[{"name":"","parameterTypes":[] }] -}, - { - "name":"org.enso.tableau.HyperQueryError" - }, - { - "name":"org.enso.tableau.HyperReader" - }, - { - "name":"org.enso.tableau.HyperTableColumn" - }, - { - "name":"org.enso.tableau.HyperTableNotFound" - }, - { - "name": "org.enso.tableau.HyperTable", - "methods": [ - { - "name": "schema", - "parameterTypes": [] - }, - { - "name": "name", - "parameterTypes": [] - } - ] - }, - { - "name": "org.enso.tableau.HyperTableColumn", - "methods": [ - { - "name": "index", - "parameterTypes": [] - }, - { - "name": "name", - "parameterTypes": [] - }, - { - "name": "typeID", - "parameterTypes": [] - }, - { - "name": "nullable", - "parameterTypes": [] - }, - { - "name": "length", - "parameterTypes": [] - }, - { - "name": "precision", - "parameterTypes": [] - }, - { - "name": "scale", - "parameterTypes": [] - }, - { - "name": "fromHyperColumn", - "parameterTypes": ["int", "com.tableau.hyperapi.TableDefinition.Column"] - } - - ] - } -] diff --git a/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/resource-config.json b/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/resource-config.json deleted file mode 100644 index 59f77b9cf096..000000000000 --- a/std-bits/tableau/src/main/resources/META-INF/native-image/org/enso/tableau/resource-config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "resources": { - "includes": [ - { - "pattern": "\\QMETA-INF/services/com.tableau.hyperapi.impl.SharedLibraryProvider\\E" - } - ] - }, - "bundles": [] -}