Skip to content

Commit 66ab45e

Browse files
martin-haffner-bysfc-gh-mkellerihor-mozil-by
authored
Version 2.4.5: Additional custom commands and improvements (#224)
Co-authored-by: Mark Keller <[email protected]> Co-authored-by: Martin Haffner <[email protected]> Co-authored-by: Ihor Mozil (croh/40023) <[email protected]> Co-authored-by: Mark Keller <[email protected]>
1 parent 730f9bb commit 66ab45e

File tree

8 files changed

+652
-94
lines changed

8 files changed

+652
-94
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,5 @@ parameters.py
102102
wss-*agent.config
103103
wss-unified-agent.jar
104104
whitesource/
105+
.idea
106+
Python

__init__.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,6 @@
44
# Copyright (c) 2012-2019 Snowflake Computing Inc. All right reserved.
55
#
66

7-
from . import base
8-
from . import snowdialect
9-
from .custom_commands import (
10-
MergeInto,
11-
CSVFormatter,
12-
JSONFormatter,
13-
PARQUETFormatter,
14-
CopyIntoStorage,
15-
AWSBucket,
16-
AzureContainer,
17-
ExternalStage
18-
)
19-
from .util import _url as URL
20-
from .version import VERSION
217
from sqlalchemy.types import (
228
BIGINT,
239
BINARY,
@@ -35,24 +21,41 @@
3521
TIMESTAMP,
3622
VARCHAR,
3723
)
24+
25+
from . import base, snowdialect
26+
from .custom_commands import (
27+
AWSBucket,
28+
AzureContainer,
29+
CopyFormatter,
30+
CopyIntoStorage,
31+
CreateFileFormat,
32+
CreateStage,
33+
CSVFormatter,
34+
ExternalStage,
35+
JSONFormatter,
36+
MergeInto,
37+
PARQUETFormatter,
38+
)
3839
from .custom_types import (
3940
ARRAY,
4041
BYTEINT,
4142
CHARACTER,
4243
DEC,
4344
DOUBLE,
4445
FIXED,
45-
OBJECT,
4646
NUMBER,
47+
OBJECT,
4748
STRING,
4849
TEXT,
4950
TIMESTAMP_LTZ,
50-
TIMESTAMP_TZ,
5151
TIMESTAMP_NTZ,
52+
TIMESTAMP_TZ,
5253
TINYINT,
5354
VARBINARY,
5455
VARIANT,
5556
)
57+
from .util import _url as URL
58+
from .version import VERSION
5659

5760
SNOWFLAKE_CONNECTOR_VERSION = '.'.join(str(v) for v in VERSION[0:3])
5861

@@ -94,13 +97,15 @@
9497
'TINYINT',
9598
'VARBINARY',
9699
'VARIANT',
97-
98100
'MergeInto',
99101
'CSVFormatter',
100102
'JSONFormatter',
101103
'PARQUETFormatter',
104+
'CopyFormatter',
102105
'CopyIntoStorage',
103106
'AWSBucket',
104107
'AzureContainer',
105108
'ExternalStage',
109+
'CreateStage',
110+
'CreateFileFormat',
106111
)

base.py

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from sqlalchemy.sql.elements import quoted_name
1515
from sqlalchemy.util.compat import string_types
1616

17-
from .custom_commands import AWSBucket, AzureContainer
17+
from .custom_commands import AWSBucket, AzureContainer, ExternalStage
1818

1919
RESERVED_WORDS = frozenset([
2020
"ALL", # ANSI Reserved words
@@ -180,14 +180,23 @@ def visit_merge_into_clause(self, merge_into_clause, **kw):
180180
" SET %s" % sets if merge_into_clause.set else "")
181181

182182
def visit_copy_into(self, copy_into, **kw):
183-
formatter = copy_into.formatter._compiler_dispatch(self, **kw)
183+
if hasattr(copy_into, "formatter") and copy_into.formatter is not None:
184+
formatter = copy_into.formatter._compiler_dispatch(self, **kw)
185+
else:
186+
formatter = ""
184187
into = (copy_into.into if isinstance(copy_into.into, Table)
185188
else copy_into.into._compiler_dispatch(self, **kw))
186189
from_ = None
187190
if isinstance(copy_into.from_, Table):
188191
from_ = copy_into.from_
189192
# this is intended to catch AWSBucket and AzureContainer
190-
elif isinstance(copy_into.from_, AWSBucket) or isinstance(copy_into.from_, AzureContainer):
193+
elif isinstance(
194+
copy_into.from_, AWSBucket
195+
) or isinstance(
196+
copy_into.from_, AzureContainer
197+
) or isinstance(
198+
copy_into.from_, ExternalStage
199+
):
191200
from_ = copy_into.from_._compiler_dispatch(self, **kw)
192201
# everything else (selects, etc.)
193202
else:
@@ -214,18 +223,21 @@ def visit_copy_formatter(self, formatter, **kw):
214223
options_list = list(formatter.options.items())
215224
if kw.get('deterministic', False):
216225
options_list.sort(key=operator.itemgetter(0))
217-
return 'FILE_FORMAT=(TYPE={}{})'.format(formatter.file_format,
218-
(' ' + ' '.join([("{}='{}'" if isinstance(value, str)
219-
else "{}={}").format(
220-
name,
221-
value._compiler_dispatch(self, **kw) if getattr(value,
222-
'_compiler_dispatch',
223-
False) else str(
224-
value))
225-
for name, value in options_list])) if formatter.options else "")
226-
227-
def visit_external_stage(self, stage, **kw):
228-
return "@{}{}{}".format(stage.namespace, stage.name, stage.path)
226+
if "format_name" in formatter.options:
227+
return f"FILE_FORMAT=(format_name = {formatter.options['format_name']})"
228+
return 'FILE_FORMAT=(TYPE={}{})'.format(
229+
formatter.file_format,
230+
' ' + ' '.join(
231+
[
232+
"{}={}".format(
233+
name,
234+
value._compiler_dispatch(self, **kw)
235+
if hasattr(value, '_compiler_dispatch')
236+
else formatter.value_repr(name, value)
237+
) for name, value in options_list
238+
]
239+
) if formatter.options else ""
240+
)
229241

230242
def visit_aws_bucket(self, aws_bucket, **kw):
231243
credentials_list = list(aws_bucket.credentials_used.items())
@@ -266,6 +278,16 @@ def visit_azure_container(self, azure_container, **kw):
266278
credentials if azure_container.credentials_used else '',
267279
encryption if azure_container.encryption_used else '')
268280

281+
def visit_external_stage(self, external_stage, **kw):
282+
if external_stage.file_format is None:
283+
return "@{}{}{}".format(external_stage.namespace,
284+
external_stage.name,
285+
external_stage.path)
286+
return "@{}{}{} (file_format => {})".format(external_stage.namespace,
287+
external_stage.name,
288+
external_stage.path,
289+
external_stage.file_format)
290+
269291
def delete_extra_from_clause(self, delete_stmt, from_table,
270292
extra_froms, from_hints, **kw):
271293
return "USING " + ', '.join(
@@ -377,6 +399,31 @@ def post_create_table(self, table):
377399
", ".join(self.denormalize_column_name(key) for key in cluster))
378400
return text
379401

402+
def visit_create_stage(self, create_stage, **kw):
403+
"""
404+
This visitor will create the SQL representation for a CREATE STAGE command.
405+
"""
406+
return "CREATE {}STAGE {}{} URL={}".format(
407+
"OR REPLACE " if create_stage.replace_if_exists else "",
408+
create_stage.stage.namespace,
409+
create_stage.stage.name,
410+
repr(create_stage.container))
411+
412+
def visit_create_file_format(self, file_format, **kw):
413+
"""
414+
This visitor will create the SQL representation for a CREATE FILE FORMAT
415+
command.
416+
"""
417+
return "CREATE {}FILE FORMAT {} TYPE='{}' {}".format(
418+
"OR REPLACE " if file_format.replace_if_exists else "",
419+
file_format.format_name,
420+
file_format.formatter.file_format,
421+
" ".join(
422+
["{} = {}".format(name, file_format.formatter.value_repr(name, value))
423+
for name, value
424+
in file_format.formatter.options.items()])
425+
)
426+
380427

381428
class SnowflakeTypeCompiler(compiler.GenericTypeCompiler):
382429
def visit_BYTEINT(self, type_, **kw):

0 commit comments

Comments
 (0)