1414from sqlalchemy .sql .elements import quoted_name
1515from sqlalchemy .util .compat import string_types
1616
17- from .custom_commands import AWSBucket , AzureContainer
17+ from .custom_commands import AWSBucket , AzureContainer , ExternalStage
1818
1919RESERVED_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
381428class SnowflakeTypeCompiler (compiler .GenericTypeCompiler ):
382429 def visit_BYTEINT (self , type_ , ** kw ):
0 commit comments