Skip to content

Commit

Permalink
Split extensions into 2 files to avoid renaming.
Browse files Browse the repository at this point in the history
  • Loading branch information
radeusgd committed Jun 5, 2023
1 parent 34ebe1d commit fc87e96
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument

import Standard.Table.Internal.Widget_Helpers
from Standard.Table.Errors import all
from Standard.Table import Column_Selector

import project.Connection.Connection.Connection
import project.Data.Table.Table
import project.Data.Update_Action.Update_Action
from project.Errors import all
from project.Internal.Result_Set import result_set_to_table
from project.Internal.Upload_Table import all

## Creates a new database table from this table.

Arguments:
- connection: the database connection to use. The table will be created in
the database and schema associated with this connection.
- 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.
- 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`.
- 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 the source table,
`Missing_Input_Columns` error is raised.
- If the selected primary key columns are not unique, a
`Non_Unique_Primary_Key` error is raised.
- An `SQL_Error` may be reported if there is a failure on the database
side.

If an error has been raised, the table is not created (that may not always
apply to `SQL_Error`).
@primary_key Widget_Helpers.make_column_name_vector_selector
Table.select_into_database_table : Connection -> Text|Nothing -> Vector Text | Nothing -> Boolean -> Problem_Behavior -> Table ! Table_Already_Exists | Inexact_Type_Coercion | Missing_Input_Columns | Non_Unique_Primary_Key | SQL_Error | Illegal_Argument
Table.select_into_database_table self connection table_name=Nothing primary_key=[self.columns.first.name] temporary=False on_problems=Problem_Behavior.Report_Warning = Panic.recover SQL_Error <|
Panic.recover SQL_Error <|
connection.jdbc_connection.run_within_transaction <|
upload_database_table self connection table_name primary_key temporary on_problems

## Updates the target table with the contents of this table.

Arguments:
- connection: the database connection of the target table.
- table_name: the name of the table to update.
- update_action: specifies the update strategy - how to handle existing new
and missing rows.
- key_columns: the names of the columns to use identify correlate rows from
the source table with rows in the target table. This key is used to
determine if a row from the source table exists in the target or is a new
one.
- error_on_missing_columns: if set to `False` (the default), any columns
missing from the source table will be left unchanged or initialized with
the default value if inserting. If a missing column has no default value,
this will trigger a `SQL_Error`. If set to `True`, any columns missing from
the source will cause an error.
- on_problems: the behavior to use when encountering non-fatal problems.

! Error Conditions

- If `key_columns` are not present in either the source or target tables, a
`Missing_Input_Columns` error is raised.
- If the target table does not exist, a `Table_Not_Found` error is raised.
- If `error_on_missing_columns` is set to `True` and a column is missing
from the source table, a `Missing_Input_Columns` error is raised.
- If the source table contains columns that are not present in the target
table, an `Unmatched_Columns` error is raised.
- If a column in the source table has a type that cannot be trivially
widened to the corresponding column in the target table, a
`Column_Type_Mismatch` error is raised.

If any error was raised, the data in the target table is not modified.
Table.update_database_table : Connection -> Text -> Update_Action -> Vector Text-> Boolean -> Table ! Table_Not_Found | Unmatched_Columns | Missing_Input_Columns | Column_Type_Mismatch | SQL_Error | Illegal_Argument
Table.update_database_table connection (table_name : Text) (update_action : Update_Action = Update_Action.Update_Or_Insert) (key_columns : Vector = default_key_columns connection table_name) (error_on_missing_columns : Boolean = False) =
common_update_table self connection table_name update_action key_columns error_on_missing_columns
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument

from Standard.Table import Column_Selector

## PRIVATE
default_key_columns connection table_name =
keys = get_primary_key connection table_name
keys.catch Any _->
Error.throw (Illegal_Argument.Error "Could not determine the primary key for table "+table_name+". Please provide it explicitly.")

## PRIVATE

This method may not work correctly with temporary tables, possibly resulting
in `SQL_Error` as such tables may not be found.

! Temporary Tables in SQLite

The temporary tables in SQLite live in a `temp` database. There is a bug in
how JDBC retrieves primary keys - it only queries the `sqlite_schema` table
which contains schemas of only permanent tables.

Ideally, we should provide a custom implementation for SQLite that will
UNION both `sqlite_schema` and `temp.sqlite_schema` tables to get results
for both temporary and permanent tables.

TODO [RW] fix keys for SQLite temporary tables and test it
get_primary_key connection table_name =
connection.jdbc_connection.with_connection java_connection->
rs = java_connection.getMetaData.getPrimaryKeys Nothing Nothing table_name
keys_table = result_set_to_table rs connection.dialect.make_column_fetcher_for_type
# The names of the columns are sometimes lowercase and sometimes uppercase, so we do a case insensitive select first.
selected = keys_table.select_columns [Column_Selector.By_Name "COLUMN_NAME", Column_Selector.By_Name "KEY_SEQ"] reorder=True
key_column_names = selected.order_by 1 . at 0 . to_vector
if key_column_names.is_empty then Nothing else key_column_names
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument

import Standard.Table.Data.Table.Table as In_Memory_Table
import Standard.Table.Data.Table.Table
import Standard.Table.Internal.Widget_Helpers
from Standard.Table.Errors import all
from Standard.Table import Column_Selector
Expand Down Expand Up @@ -48,8 +48,8 @@ from project.Internal.Upload_Table import all
If an error has been raised, the table is not created (that may not always
apply to `SQL_Error`).
@primary_key Widget_Helpers.make_column_name_vector_selector
In_Memory_Table.select_into_database_table : Connection -> Text|Nothing -> Vector Text | Nothing -> Boolean -> Problem_Behavior -> Database_Table ! Table_Already_Exists | Inexact_Type_Coercion | Missing_Input_Columns | Non_Unique_Primary_Key | SQL_Error | Illegal_Argument
In_Memory_Table.select_into_database_table self connection table_name=Nothing primary_key=[self.columns.first.name] temporary=False on_problems=Problem_Behavior.Report_Warning =
Table.select_into_database_table : Connection -> Text|Nothing -> Vector Text | Nothing -> Boolean -> Problem_Behavior -> Database_Table ! Table_Already_Exists | Inexact_Type_Coercion | Missing_Input_Columns | Non_Unique_Primary_Key | SQL_Error | Illegal_Argument
Table.select_into_database_table self connection table_name=Nothing primary_key=[self.columns.first.name] temporary=False on_problems=Problem_Behavior.Report_Warning =
Panic.recover SQL_Error <|
connection.jdbc_connection.run_within_transaction <|
upload_in_memory_table self connection table_name primary_key temporary on_problems
Expand Down Expand Up @@ -131,8 +131,8 @@ Database_Table.select_into_database_table self connection table_name=Nothing pri
`Column_Type_Mismatch` error is raised.

If any error was raised, the data in the target table is not modified.
In_Memory_Table.update_database_table : Connection -> Text -> Update_Action -> Vector Text-> Boolean -> Database_Table ! Table_Not_Found | Unmatched_Columns | Missing_Input_Columns | Column_Type_Mismatch | SQL_Error | Illegal_Argument
In_Memory_Table.update_database_table connection (table_name : Text) (update_action : Update_Action = Update_Action.Update_Or_Insert) (key_columns : Vector = default_key_columns connection table_name) (error_on_missing_columns : Boolean = False) =
Table.update_database_table : Connection -> Text -> Update_Action -> Vector Text-> Boolean -> Database_Table ! Table_Not_Found | Unmatched_Columns | Missing_Input_Columns | Column_Type_Mismatch | SQL_Error | Illegal_Argument
Table.update_database_table connection (table_name : Text) (update_action : Update_Action = Update_Action.Update_Or_Insert) (key_columns : Vector = default_key_columns connection table_name) (error_on_missing_columns : Boolean = False) =
common_update_table self connection table_name update_action key_columns error_on_missing_columns

## Updates the target table with the contents of this table.
Expand Down Expand Up @@ -170,34 +170,3 @@ In_Memory_Table.update_database_table connection (table_name : Text) (update_act
Database_Table.update_database_table : Connection -> Text -> Update_Action -> Vector Text-> Boolean -> Database_Table ! Table_Not_Found | Unmatched_Columns | Missing_Input_Columns | Column_Type_Mismatch | SQL_Error | Illegal_Argument
Database_Table.update_database_table connection (table_name : Text) (update_action : Update_Action = Update_Action.Update_Or_Insert) (key_columns : Vector = default_key_columns connection table_name) (error_on_missing_columns : Boolean = False) =
common_update_table self connection table_name update_action key_columns error_on_missing_columns

## PRIVATE
default_key_columns connection table_name =
keys = get_primary_key connection table_name
keys.catch Any _->
Error.throw (Illegal_Argument.Error "Could not determine the primary key for table "+table_name+". Please provide it explicitly.")

## PRIVATE

This method may not work correctly with temporary tables, possibly resulting
in `SQL_Error` as such tables may not be found.

! Temporary Tables in SQLite

The temporary tables in SQLite live in a `temp` database. There is a bug in
how JDBC retrieves primary keys - it only queries the `sqlite_schema` table
which contains schemas of only permanent tables.

Ideally, we should provide a custom implementation for SQLite that will
UNION both `sqlite_schema` and `temp.sqlite_schema` tables to get results
for both temporary and permanent tables.

TODO [RW] fix keys for SQLite temporary tables and test it
get_primary_key connection table_name =
connection.jdbc_connection.with_connection java_connection->
rs = java_connection.getMetaData.getPrimaryKeys Nothing Nothing table_name
keys_table = result_set_to_table rs connection.dialect.make_column_fetcher_for_type
# The names of the columns are sometimes lowercase and sometimes uppercase, so we do a case insensitive select first.
selected = keys_table.select_columns [Column_Selector.By_Name "COLUMN_NAME", Column_Selector.By_Name "KEY_SEQ"] reorder=True
key_column_names = selected.order_by 1 . at 0 . to_vector
if key_column_names.is_empty then Nothing else key_column_names
6 changes: 4 additions & 2 deletions distribution/lib/Standard/Database/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import project.Connection.SQLite_Details.In_Memory
import project.Connection.SQLite_Format.SQLite_Format
import project.Connection.SSL_Mode.SSL_Mode
import project.Data.SQL_Query.SQL_Query
import project.Extensions.Upload_Table
import project.Extensions.Upload_Database_Table
import project.Extensions.Upload_In_Memory_Table

from project.Connection.Postgres_Details.Postgres_Details import Postgres
from project.Connection.SQLite_Details.SQLite_Details import SQLite
Expand All @@ -25,7 +26,8 @@ export project.Connection.SQLite_Details.In_Memory
export project.Connection.SQLite_Format.SQLite_Format
export project.Connection.SSL_Mode.SSL_Mode
export project.Data.SQL_Query.SQL_Query
export project.Extensions.Upload_Table
export project.Extensions.Upload_Database_Table
export project.Extensions.Upload_In_Memory_Table

from project.Connection.Postgres_Details.Postgres_Details export Postgres
from project.Connection.SQLite_Details.SQLite_Details export SQLite
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ from Standard.Table import Aggregate_Column, Value_Type, Table
from Standard.Table.Errors import Invalid_Value_Type, Inexact_Type_Coercion

import Standard.Database.Data.Dialect
import Standard.Database.Extensions.Upload_Table
import Standard.Database.Extensions.Upload_Database_Table
import Standard.Database.Extensions.Upload_In_Memory_Table
import Standard.Database.Internal.SQLite.SQLite_Type_Mapping
from Standard.Database import Database, SQLite, In_Memory, SQL_Query
from Standard.Database.Errors import Unsupported_Database_Operation
Expand Down
2 changes: 1 addition & 1 deletion test/Table_Tests/src/Database/Upload_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from Standard.Table.Errors import Missing_Input_Columns
from Standard.Database import all
from Standard.Database.Errors import all
from Standard.Database.Internal.Result_Set import result_set_to_table
from Standard.Database.Extensions.Upload_Table import get_primary_key
from Standard.Database.Extensions.Upload_Default_Helpers import get_primary_key

from Standard.Test import Test, Test_Suite, Problems
import Standard.Test.Extensions
Expand Down
3 changes: 2 additions & 1 deletion test/Table_Tests/src/In_Memory/Table_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ from Standard.Table.Errors import Invalid_Output_Column_Names, Duplicate_Output_

import Standard.Visualization

import Standard.Database.Extensions.Upload_Table
import Standard.Database.Extensions.Upload_Database_Table
import Standard.Database.Extensions.Upload_In_Memory_Table
from Standard.Database import Database, SQLite, In_Memory

from Standard.Test import Test, Test_Suite, Problems
Expand Down

0 comments on commit fc87e96

Please sign in to comment.