Skip to content

Commit 3581fd8

Browse files
authored
Make sqlserver temp tables work (#10781)
* Temp table support * Fix * Add query * Clean up * Clean up * Fix * Code review changes * Added comment * Code review changes * Add execute
1 parent 8de4b8c commit 3581fd8

File tree

16 files changed

+170
-31
lines changed

16 files changed

+170
-31
lines changed

distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Standard.Database.SQL.SQL_Builder
2626
import Standard.Database.SQL_Statement.SQL_Statement
2727
import Standard.Database.SQL_Type.SQL_Type
2828
from Standard.Database.Errors import SQL_Error, Unsupported_Database_Operation
29+
from Standard.Database.Dialect import Temp_Table_Style
2930

3031
import project.Database.Redshift.Internal.Redshift_Error_Mapper.Redshift_Error_Mapper
3132

@@ -146,9 +147,9 @@ type Redshift_Dialect
146147
supports_float_round_decimal_places self = False
147148

148149
## PRIVATE
149-
Specifies whether the Database supports CREATE TEMPORARY TABLE syntax.
150-
suppports_temporary_table_syntax : Boolean
151-
suppports_temporary_table_syntax self = True
150+
Specifies how the database creates temp tables.
151+
temp_table_style : Temp_Table_Style
152+
temp_table_style self = Temp_Table_Style.Temporary_Table
152153

153154
## PRIVATE
154155
adapt_unified_column : Internal_Column -> Value_Type -> (SQL_Expression -> SQL_Type_Reference) -> Internal_Column

distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Connection.enso

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,24 @@ type Connection
385385
SQL_Warning_Helper.process_warnings stmt <|
386386
result
387387

388+
## PRIVATE
389+
ADVANCED
390+
391+
Executes a raw query. If the query was inserting, updating or
392+
deleting rows, the number of affected rows is returned; otherwise it
393+
returns 0 for other types of queries (like creating or altering tables).
394+
395+
Arguments:
396+
- query: either raw SQL code as Text or an instance of SQL_Statement
397+
representing the query to execute.
398+
execute : Text | SQL_Statement -> Integer
399+
execute self query =
400+
Execution_Context.Output.if_enabled disabled_message="Executing update queries is forbidden as the Output context is disabled." panic=False <|
401+
result = self.jdbc_connection.execute query
402+
stmt = result.second
403+
check_statement_is_allowed self stmt
404+
stmt.getUpdateCount
405+
388406
## PRIVATE
389407
Drops a table.
390408

distribution/lib/Standard/Database/0.0.0-dev/src/Dialect.enso

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ type Dialect
164164
Unimplemented.throw "This is an interface only."
165165

166166
## PRIVATE
167-
Specifies whether the Database supports CREATE TEMPORARY TABLE syntax.
168-
suppports_temporary_table_syntax : Boolean
169-
suppports_temporary_table_syntax self =
167+
Specifies how the database creates temp tables.
168+
temp_table_style : Temp_Table_Style
169+
temp_table_style self =
170170
Unimplemented.throw "This is an interface only."
171171

172172
## PRIVATE
@@ -293,3 +293,13 @@ default_fetch_primary_key connection table_name =
293293
selected = keys_table.select_columns ["COLUMN_NAME", "KEY_SEQ"] case_sensitivity=Case_Sensitivity.Insensitive reorder=True
294294
key_column_names = selected.sort 1 . at 0 . to_vector
295295
if key_column_names.is_empty then Nothing else key_column_names
296+
297+
## PRIVATE
298+
type Temp_Table_Style
299+
## PRIVATE
300+
The temporary table is created using a create table statement.
301+
Temporary_Table
302+
303+
## PRIVATE
304+
The temporary table is created using a # table name.
305+
Hash_Prefix

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Base_Generator.enso

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import project.Internal.IR.SQL_Expression.SQL_Expression
1212
import project.Internal.IR.SQL_Join_Kind.SQL_Join_Kind
1313
import project.SQL.SQL_Builder
1414
from project.Errors import Unsupported_Database_Operation
15+
from project.Dialect import Temp_Table_Style
1516
from project.Internal.IR.Operation_Metadata import Row_Number_Metadata
1617

1718
type Dialect_Operations
@@ -587,7 +588,7 @@ generate_create_table dialect name columns primary_key temporary =
587588
column_definitions = columns.map (generate_column_description dialect)
588589
modifiers = if primary_key.is_nothing then [] else
589590
[SQL_Builder.code ", PRIMARY KEY (" ++ SQL_Builder.join ", " (primary_key.map dialect.wrap_identifier) ++ ")"]
590-
table_type = if temporary && dialect.suppports_temporary_table_syntax then "TEMPORARY TABLE" else "TABLE"
591+
table_type = if temporary && dialect.temp_table_style == Temp_Table_Style.Temporary_Table then "TEMPORARY TABLE" else "TABLE"
591592
create_prefix = SQL_Builder.code ("CREATE "+table_type+" ") ++ dialect.wrap_identifier name
592593
create_body = (SQL_Builder.join ", " column_definitions) ++ (SQL_Builder.join "" modifiers)
593594
create_prefix ++ " (" ++ create_body ++ ")"

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/JDBC_Connection.enso

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,29 @@ type JDBC_Connection
117117
compiled = query.prepare
118118
go compiled.first compiled.second
119119

120+
## PRIVATE
121+
Executes the provided SQL.
122+
Typically this shouldn't be used and with_prepared_statement should be preferred.
123+
It is needed for SQLServer temp table creation where we need to execute a raw SQL statement
124+
outside of a stored procedure.
125+
execute : Text | SQL_Statement -> Any
126+
execute self query:(Text | SQL_Statement) = self.synchronized <| profile_sql_if_enabled self query.to_text <|
127+
compiled_query = case query of
128+
_ : Text -> query
129+
SQL_Statement.Value _ -> query.prepare.first
130+
self.with_connection java_connection->
131+
stmt = java_connection.createStatement
132+
handle_illegal_state caught_panic =
133+
Error.throw (Illegal_Argument.Error caught_panic.payload.message)
134+
handle_any caught_panic =
135+
stmt.close
136+
Panic.throw caught_panic
137+
result = Panic.catch Illegal_State handler=handle_illegal_state <|
138+
Panic.catch Any handler=handle_any <|
139+
stmt.execute compiled_query
140+
result.if_not_error <|
141+
[result, stmt]
142+
120143
## PRIVATE
121144
Given a prepared statement, gets the column names and types for the
122145
result set.

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Connection.enso

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,22 @@ type Postgres_Connection
245245
execute_update self query =
246246
self.connection.execute_update query
247247

248+
## PRIVATE
249+
ADVANCED
250+
GROUP Standard.Base.Output
251+
ICON data_output
252+
253+
Executes a raw query. If the query was inserting, updating or
254+
deleting rows, the number of affected rows is returned; otherwise it
255+
returns 0 for other types of queries (like creating or altering tables).
256+
257+
Arguments:
258+
- query: either raw SQL code as Text or an instance of SQL_Statement
259+
representing the query to execute.
260+
execute : Text | SQL_Statement -> Integer
261+
execute self query =
262+
self.connection.execute query
263+
248264
## PRIVATE
249265
Access the dialect.
250266
dialect self = self.connection.dialect

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import project.SQL.SQL_Fragment
3838
import project.SQL_Statement.SQL_Statement
3939
import project.SQL_Type.SQL_Type
4040
from project.Errors import SQL_Error, Unsupported_Database_Operation
41+
from project.Dialect import Temp_Table_Style
4142
from project.Internal.IR.Operation_Metadata import Date_Period_Metadata
4243

4344
polyglot java import java.sql.Types
@@ -183,9 +184,9 @@ type Postgres_Dialect
183184
supports_float_round_decimal_places self = False
184185

185186
## PRIVATE
186-
Specifies whether the Database supports CREATE TEMPORARY TABLE syntax.
187-
suppports_temporary_table_syntax : Boolean
188-
suppports_temporary_table_syntax self = True
187+
Specifies how the database creates temp tables.
188+
temp_table_style : Temp_Table_Style
189+
temp_table_style self = Temp_Table_Style.Temporary_Table
189190

190191
## PRIVATE
191192
There is a bug in Postgres type inference, where if we unify two

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Connection.enso

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,22 @@ type SQLite_Connection
231231
execute_update self query =
232232
self.connection.execute_update query
233233

234+
## PRIVATE
235+
ADVANCED
236+
GROUP Standard.Base.Output
237+
ICON data_output
238+
239+
Executes a raw query. If the query was inserting, updating or
240+
deleting rows, the number of affected rows is returned; otherwise it
241+
returns 0 for other types of queries (like creating or altering tables).
242+
243+
Arguments:
244+
- query: either raw SQL code as Text or an instance of SQL_Statement
245+
representing the query to execute.
246+
execute : Text | SQL_Statement -> Integer
247+
execute self query =
248+
self.connection.execute query
249+
234250
## PRIVATE
235251
Access the dialect.
236252
dialect self = self.connection.dialect

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import project.SQL.SQL_Builder
3232
import project.SQL_Statement.SQL_Statement
3333
import project.SQL_Type.SQL_Type
3434
from project.Errors import SQL_Error, Unsupported_Database_Operation
35+
from project.Dialect import Temp_Table_Style
3536

3637
## PRIVATE
3738

@@ -193,9 +194,9 @@ type SQLite_Dialect
193194
supports_float_round_decimal_places self = True
194195

195196
## PRIVATE
196-
Specifies whether the Database supports CREATE TEMPORARY TABLE syntax.
197-
suppports_temporary_table_syntax : Boolean
198-
suppports_temporary_table_syntax self = True
197+
Specifies how the database creates temp tables.
198+
temp_table_style : Temp_Table_Style
199+
temp_table_style self = Temp_Table_Style.Temporary_Table
199200

200201
## PRIVATE
201202
SQLite allows mixed type columns, but we want our columns to be uniform.

distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload/Operations/Internal_Core.enso

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import project.Internal.In_Transaction.In_Transaction
1414
import project.Internal.IR.Query.Query
1515
import project.SQL_Query.SQL_Query
1616
from project.Errors import SQL_Error, Table_Already_Exists, Unsupported_Database_Operation
17+
from project.Dialect import Temp_Table_Style
1718
from project.Internal.Upload.Helpers.Argument_Checks import resolve_primary_key
1819
from project.Internal.Upload.Helpers.Constants import default_batch_size
1920
from project.Internal.Upload.Helpers.Error_Helpers import handle_upload_errors, internal_translate_known_upload_errors
@@ -29,7 +30,7 @@ internal_create_table_structure connection table_name structure primary_key temp
2930
validate_structure connection.base_connection.column_naming_helper aligned_structure <|
3031
create_table_statement = prepare_create_table_statement connection table_name aligned_structure resolved_primary_key temporary on_problems
3132
update_result = create_table_statement.if_not_error <|
32-
connection.execute_update create_table_statement
33+
connection.execute create_table_statement
3334
final_result = update_result.if_not_error table_name
3435
final_result.catch SQL_Error sql_error->
3536
if connection.dialect.get_error_mapper.is_table_already_exists_error sql_error then Error.throw (Table_Already_Exists.Error table_name) else final_result
@@ -62,11 +63,12 @@ type Table_Upload_Operation
6263
- row_limit: if set, only the first `row_limit` rows will be uploaded.
6364
internal_upload_table : DB_Table | Table -> Connection -> Text -> Nothing | Vector Text -> Boolean -> Boolean -> Nothing | Vector Column_Description -> Problem_Behavior -> Integer | Nothing -> Table_Upload_Operation
6465
internal_upload_table source_table connection (table_name : Text) (primary_key : Nothing | Vector Text) (temporary : Boolean) (remove_after_transaction : Boolean = False) structure_hint=Nothing (on_problems:Problem_Behavior=..Report_Error) (row_limit : Integer | Nothing = Nothing) -> Table_Upload_Operation =
66+
resolved_table_name = resolve_temp_table_name connection temporary table_name
6567
case source_table of
6668
_ : Table ->
67-
internal_upload_in_memory_table source_table connection table_name primary_key temporary remove_after_transaction structure_hint on_problems row_limit
69+
internal_upload_in_memory_table source_table connection resolved_table_name primary_key temporary remove_after_transaction structure_hint on_problems row_limit
6870
_ : DB_Table ->
69-
internal_upload_database_table source_table connection table_name primary_key temporary remove_after_transaction structure_hint on_problems row_limit
71+
internal_upload_database_table source_table connection resolved_table_name primary_key temporary remove_after_transaction structure_hint on_problems row_limit
7072
_ ->
7173
Panic.throw <| Illegal_Argument.Error ("Unsupported table type: " + Meta.get_qualified_type_name source_table)
7274

@@ -128,3 +130,15 @@ internal_upload_database_table (source_table : DB_Table) connection table_name p
128130
check_outside_transaction =
129131
if In_Transaction.is_in_transaction then
130132
Panic.throw (Illegal_State.Error "Preparing Table_Upload_Operation should itself be called outside of transaction. This is a bug in the Database library.")
133+
134+
## PRIVATE
135+
resolve_temp_table_name connection temporary:Boolean table_name:Text -> Text =
136+
case temporary of
137+
False -> case table_name.starts_with "#" of
138+
True -> Error.throw <| Illegal_Argument.Error ("Table name cannot start with '#': " + table_name)
139+
False -> table_name
140+
True -> case connection.dialect.temp_table_style of
141+
Temp_Table_Style.Temporary_Table -> table_name
142+
Temp_Table_Style.Hash_Prefix -> case table_name.starts_with "#" of
143+
True -> table_name
144+
False -> "#" + table_name

0 commit comments

Comments
 (0)