Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor create_database_table into Connection.create_table and select_into_database_table, implement Set. #6925

Merged
merged 25 commits into from
Jun 6, 2023
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@
- [Added execution control to `Table.write` and various bug fixes.][6835]
- [Implemented `Table.add_row_number`.][6890]
- [Handling edge cases in rounding.][6922]
- [Split `Table.create_database_table` into `Connection.create_table` and
`Table.select_into_database_table`.][6925]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -693,6 +695,7 @@
[6835]: https://github.com/enso-org/enso/pull/6835
[6890]: https://github.com/enso-org/enso/pull/6890
[6922]: https://github.com/enso-org/enso/pull/6922
[6925]: https://github.com/enso-org/enso/pull/6925

#### Enso Compiler

Expand Down
18 changes: 9 additions & 9 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Map.enso
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,24 @@ type Map key value

Arguments:
- vec: A vector of key-value pairs (2 element vectors).
- allow_duplicates: A flag which specifies if duplicate keys on the input
vector are allowed. By default, set to `False`, meaning that if two
entries in the vector share the same key, an `Illegal_Argument.Error`
will be thrown. If set to `True`, the last entry with a given key will
be kept.
- error_on_duplicates: A flag which specifies if duplicate keys on the
input vector should result in an error. By default, set to `True`,
meaning that if two entries in the vector share the same key, an
`Illegal_Argument` error is raised. If set to `False`, the last entry
with a given key will be kept.

> Example
Building a map containing two key-value pairs.

import Standard.Base.Data.Map.Map

example_from_vector = Map.from_vector [["A", 1], ["B", 2]]
from_vector : Vector Any -> Boolean -> Map
from_vector vec allow_duplicates=False =
from_vector : Vector Any -> Boolean -> Map ! Illegal_Argument
from_vector vec error_on_duplicates=True =
vec.fold Map.empty m-> el-> if el.length != 2 then Error.throw (Illegal_Argument.Error "`Map.from_vector` encountered an invalid value. Each value in the vector has to be a key-value pair - it must have exactly 2 elements.") else
key = el.at 0
value = el.at 1
if allow_duplicates || (m.contains_key key . not) then m.insert key value else
if error_on_duplicates.not || (m.contains_key key . not) then m.insert key value else
Error.throw (Illegal_Argument.Error "`Map.from_vector` encountered duplicate key: "+key.to_display_text)

## Returns True iff the Map is empty, i.e., does not have any entries.
Expand Down Expand Up @@ -264,7 +264,7 @@ type Map key value
transform self function =
func_pairs = p -> function (p.at 0) (p.at 1)
vec_transformed = self.to_vector.map func_pairs
new_map = Map.from_vector vec_transformed allow_duplicates=False
new_map = Map.from_vector vec_transformed error_on_duplicates=True
new_map.catch Illegal_Argument error->
case error.message.starts_with "`Map.from_vector` encountered duplicate key" of
True ->
Expand Down
109 changes: 109 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Set.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import project.Any.Any
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Ordering.Comparable
import project.Data.Ordering.Ordering
import project.Data.Vector.Vector
import project.Data.Text.Extensions
import project.Data.Text.Text
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Nothing.Nothing
import project.Panic.Panic

from project.Data.Boolean import Boolean, True, False

## UNSTABLE
An unordered collection of unique values.
type Set
## PRIVATE
Value (underlying_map : Map Any Nothing)

## Constructs a new set from a vector.

Arguments:
- vector: the vector of elements to add to the set.
- error_on_duplicates: specifies if duplicate elements in the input
should result in an error. Defaults to `False`, meaning that the last
occurrence of each duplicated element is retained in the set. If set to
`True` it will raise an `Illegal_Argument` if duplicate elements are
encountered.
from_vector : Vector Any -> Boolean -> Set ! Illegal_Argument
from_vector (vector : Vector) (error_on_duplicates : Boolean = False) =
pairs_array = Array_Proxy.new vector.length (i-> [vector.at i, Nothing])
pairs = Vector.from_polyglot_array pairs_array
map = Map.from_vector pairs error_on_duplicates=error_on_duplicates
Set.Value map

## Constructs an empty set.
empty : Set
empty = Set.Value Map.empty

## Returns a vector containing all elements of this set.
to_vector : Vector
to_vector self = self.underlying_map.keys

## Returns the number of elements in this set.
size : Integer
size self = self.underlying_map.size

## Checks if the set is empty.
is_empty : Boolean
is_empty self = self.underlying_map.is_empty

## Checks if the set is not empty.
not_empty : Boolean
not_empty self = self.underlying_map.not_empty

## Checks if this set contains a given value.
contains : Any -> Boolean
contains self value = self.underlying_map.contains_key value

## ALIAS Add
Adds a value to this set.
insert : Any -> Set
insert self value =
new_map = self.underlying_map.insert value Nothing
Set.Value new_map

## Creates a union of the two sets.
union : Set -> Set
radeusgd marked this conversation as resolved.
Show resolved Hide resolved
union self (other : Set) =
start_map = self.underlying_map
new_map = other.to_vector.fold start_map m-> el-> m.insert el Nothing
Set.Value new_map

## Creates an intersection of the two sets.
intersection : Set -> Set
intersection self (other : Set) =
other_map = other.underlying_map
new_map = self.underlying_map.keys.fold Map.empty m-> el->
if other_map.contains_key el then m.insert el Nothing else m
Set.Value new_map

## Computes a set difference.

Returns the set that contains all elements of this set that are not in
the other set.
difference : Set -> Set
difference self (other : Set) =
other_map = other.underlying_map
new_map = self.underlying_map.keys.fold Map.empty m-> el->
if other_map.contains_key el then m else m.insert el Nothing
Set.Value new_map

## PRIVATE
to_text : Text
to_text self = self.to_vector.join ", " "Set{" "}"

## PRIVATE
type Set_Comparator
## PRIVATE
compare x y =
if x.size != y.size then Nothing else
if (x.difference y).is_empty then Ordering.Equal else Nothing

## PRIVATE
hash x =
vec = x.to_vector.sort . remove_warnings
Comparable.from vec . hash vec
2 changes: 2 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import project.Data.Boolean
import project.Data.List.List
import project.Data.Numbers
import project.Data.Map.Map
import project.Data.Set.Set
import project.Data.Text.Text
import project.Data.Vector.Vector
import project.Error.Error
Expand Down Expand Up @@ -36,6 +37,7 @@ export project.Any.Any
export project.Data.Array.Array
export project.Data.List.List
export project.Data.Map.Map
export project.Data.Set.Set
export project.Data.Text.Text
export project.Data.Vector.Vector
export project.Error.Error
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ from Standard.Base.Metadata.Choice import Option
import Standard.Base.Metadata.Display

import Standard.Table.Data.Table.Table as Materialized_Table
import Standard.Table.Data.Type.Value_Type.Value_Type

import project.Data.SQL_Query.SQL_Query
import project.Data.SQL_Statement.SQL_Statement
import project.Data.SQL_Type.SQL_Type
import project.Data.Table.Table
import project.Data.Table.Table as Database_Table
import project.Data.Table as Database_Table_Module
import project.Internal.IR.Context.Context
import project.Internal.IR.SQL_Expression.SQL_Expression
Expand All @@ -23,7 +24,8 @@ import project.Internal.Statement_Setter.Statement_Setter

from project.Internal.Result_Set import read_column, result_set_to_table
from project.Internal.JDBC_Connection import handle_sql_errors
from project.Errors import SQL_Error, Table_Not_Found
from project.Errors import SQL_Error, Table_Not_Found, Table_Already_Exists
from project.Internal.Upload_Table import create_table_structure

polyglot java import java.lang.UnsupportedOperationException
polyglot java import java.util.UUID
Expand Down Expand Up @@ -152,7 +154,7 @@ type Connection
- If provided with a `Table_Name` or a text short-hand and the table is
not found, a `Table_Not_Found` error is raised.
@query make_table_name_selector
query : Text | SQL_Query -> Text -> Table ! Table_Not_Found | SQL_Error
query : Text | SQL_Query -> Text -> Database_Table ! Table_Not_Found | SQL_Error
query self query alias="" = case query of
_ : Text ->
result = self.query alias=alias <|
Expand All @@ -162,7 +164,7 @@ type Connection
case self.dialect.is_probably_a_query query of
True -> result
False ->
Error.throw (Table_Not_Found.Error query sql_error treated_as_query=True)
Error.throw (Table_Not_Found.Error query sql_error treated_as_query=True extra_message="")
SQL_Query.Raw_SQL raw_sql -> handle_sql_errors <|
self.jdbc_connection.ensure_query_has_no_holes raw_sql . if_not_error <|
columns = self.fetch_columns raw_sql Statement_Setter.null
Expand All @@ -177,7 +179,7 @@ type Connection
columns = self.fetch_columns statement statement_setter
Database_Table_Module.make_table self name columns ctx
result.catch SQL_Error sql_error->
Error.throw (Table_Not_Found.Error name sql_error treated_as_query=False)
Error.throw (Table_Not_Found.Error name sql_error treated_as_query=False extra_message="")

## PRIVATE
Execute the query and load the results into memory as a Table.
Expand All @@ -187,10 +189,53 @@ type Connection
If supplied as `Text`, the name is checked against the `tables` list to determine if it is a table or a query.
- limit: the maximum number of rows to return.
@query make_table_name_selector
read : Text | SQL_Query -> Integer | Nothing -> Materialized_Table
read : Text | SQL_Query -> Integer | Nothing -> Materialized_Table ! Table_Not_Found
read self query limit=Nothing =
self.query query . read max_rows=limit

## PRIVATE
Creates a new empty table in the database and returns a query referencing
the new table.

Arguments:
- table_name: the name of the table to create. If not provided, a random
name will be generated for temporary tables. If `temporary=False`, then
a name must be provided.
- structure: the structure of the table. This can be provided as a vector
of pairs of column names and types or an existing `Table` to copy the
structure from it. Note that if a `Table` is provided, only its column
structure is inherited - no table content is copied.
- primary_key: the names of the columns to use as the primary key. The
first column from the table is used by default. If it is set to
`Nothing` or an empty vector, no primary key will be created.
- temporary: if set to `True`, the table will be temporary, meaning that
it will be dropped once the `connection` is closed. Defaults to
`False`.
- allow_existing: Defaults to `False`, meaning that if the table with the
provided name already exists, an error will be raised. If set to `True`,
the existing table will be returned instead. Note that the existing
table is not guaranteed to have the same structure as the one provided.
- on_problems: the behavior to use when encountering non-fatal problems.
Defaults to reporting them as warning.

! Error Conditions

- If a table with the given name already exists, then a
`Table_Already_Exists` error is raised.
- If a column type is not supported and is coerced to a similar
supported type, an `Inexact_Type_Coercion` problem is reported
according to the `on_problems` setting.
- If a column type is not supported and there is no replacement (e.g.
native Enso types), an `Unsupported_Type` error is raised.
- If the provided primary key columns are not present in table
structure provided, `Missing_Input_Columns` error is raised.
- An `SQL_Error` may be reported if there is a failure on the database
side.
create_table : Text|Nothing -> Vector (Pair Text Value_Type) | Database_Table | Materialized_Table -> Vector Text | Nothing -> Boolean -> Boolean -> Problem_Behavior -> Database_Table ! Table_Already_Exists
create_table self (table_name : Text | Nothing = Nothing) (structure : Vector | Database_Table | Materialized_Table) (primary_key : (Vector Text | Nothing) = [first_column_in_structure structure]) (temporary : Boolean = False) (allow_existing : Boolean = False) (on_problems:Problem_Behavior = Problem_Behavior.Report_Warning) =
created_table_name = create_table_structure self table_name structure primary_key temporary allow_existing on_problems
self.query (SQL_Query.Table_Name created_table_name)

## PRIVATE
Internal read function for a statement with optional types.

Expand Down Expand Up @@ -260,3 +305,8 @@ make_table_name_selector : Connection -> Widget
make_table_name_selector connection =
tables_to_display = connection.tables.at "Name" . to_vector
Single_Choice display=Display.Always values=(tables_to_display.map t-> Option t t.pretty)

## PRIVATE
first_column_in_structure structure = case structure of
_ : Vector -> structure.first.first
_ -> structure.column_names.first
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type Update_Action
## Records are appended but if cause a primary key clash will fail.
Insert

## Just update the existing records. Unmatched columns are left unchanged.
Errors if any record is not matched in the target table.
Update

## Append the records to the new table if not found.
Updates existing records to the new values. Unmatched columns are left unchanged.
Update_Or_Insert

## Appends new records, updates existing records, removes records not in the target table
Align_Records
17 changes: 13 additions & 4 deletions distribution/lib/Standard/Database/0.0.0-dev/src/Errors.enso
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,23 @@ type Table_Not_Found
for the table was executed.
- treated_as_query: Whether the table name was treated as a raw query
string.
Error (name:Text) (related_query_error:SQL_Error) (treated_as_query:Boolean)
- extra_message: An extra message to append.
Error (name:Text) (related_query_error:SQL_Error|Nothing) (treated_as_query:Boolean) (extra_message:Text)

## PRIVATE
Pretty print the table not found error.
to_display_text : Text
to_display_text self = case self.treated_as_query of
True -> "The name " + self.name + " was treated as a query, but the query failed with the following error: " + self.related_query_error.to_display_text + "; if you want to force to use that as a table name, wrap it in `SQL_Query.Table_Name`."
False -> "Table " + self.name + " was not found in the database."
to_display_text self =
base_repr = case self.treated_as_query of
True -> "The name " + self.name + " was treated as a query, but the query failed with the following error: " + self.related_query_error.to_display_text + "; if you want to force to use that as a table name, wrap it in `SQL_Query.Table_Name`."
False -> "Table " + self.name + " was not found in the database."
base_repr + self.extra_message

## PRIVATE
Creates a copy of this error with a changed `extra_message`.
with_changed_extra_message : Table_Not_Found
with_changed_extra_message self new_extra_message =
Table_Not_Found.Error self.name self.related_query_error self.treated_as_query new_extra_message

type Table_Already_Exists
## PRIVATE
Expand Down
Loading