apache-airflow-providers-teradata 1.0.11__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of apache-airflow-providers-teradata might be problematic. Click here for more details.
- airflow/providers/teradata/LICENSE +253 -0
- airflow/providers/teradata/__init__.py +26 -1
- airflow/providers/teradata/get_provider_info.py +43 -24
- airflow/providers/teradata/hooks/teradata.py +202 -0
- airflow/providers/teradata/operators/teradata.py +64 -0
- airflow/providers/teradata/transfers/teradata_to_teradata.py +101 -0
- apache_airflow_providers_teradata-2.0.0.dist-info/METADATA +137 -0
- apache_airflow_providers_teradata-2.0.0.dist-info/RECORD +13 -0
- {apache_airflow_providers_teradata-1.0.11.dist-info → apache_airflow_providers_teradata-2.0.0.dist-info}/WHEEL +1 -2
- apache_airflow_providers_teradata-2.0.0.dist-info/entry_points.txt +3 -0
- airflow/providers/teradata/example_dags/example_execute_bteq.py +0 -51
- airflow/providers/teradata/example_dags/example_export_csv.py +0 -48
- airflow/providers/teradata/example_dags/example_load_csv.py +0 -69
- airflow/providers/teradata/hooks/ttu.py +0 -394
- airflow/providers/teradata/operators/bteq.py +0 -70
- airflow/providers/teradata/operators/fastexport.py +0 -77
- airflow/providers/teradata/operators/fastload.py +0 -105
- apache_airflow_providers_teradata-1.0.11.dist-info/AUTHORS.rst +0 -13
- apache_airflow_providers_teradata-1.0.11.dist-info/LICENSE +0 -22
- apache_airflow_providers_teradata-1.0.11.dist-info/METADATA +0 -53
- apache_airflow_providers_teradata-1.0.11.dist-info/RECORD +0 -19
- apache_airflow_providers_teradata-1.0.11.dist-info/entry_points.txt +0 -3
- apache_airflow_providers_teradata-1.0.11.dist-info/top_level.txt +0 -1
- /airflow/providers/teradata/{example_dags → transfers}/__init__.py +0 -0
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
# or more contributor license agreements. See the NOTICE file
|
|
4
|
-
# distributed with this work for additional information
|
|
5
|
-
# regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
# to you under the Apache License, Version 2.0 (the
|
|
7
|
-
# "License"); you may not use this file except in compliance
|
|
8
|
-
# with the License. You may obtain a copy of the License at
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing,
|
|
13
|
-
# software distributed under the License is distributed on an
|
|
14
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
# KIND, either express or implied. See the License for the
|
|
16
|
-
# specific language governing permissions and limitations
|
|
17
|
-
# under the License.
|
|
18
|
-
|
|
19
|
-
import os
|
|
20
|
-
import uuid
|
|
21
|
-
import subprocess
|
|
22
|
-
from typing import Any, Dict, Iterator, List, Optional, Union
|
|
23
|
-
from tempfile import gettempdir, NamedTemporaryFile, TemporaryDirectory
|
|
24
|
-
|
|
25
|
-
from airflow import configuration as conf
|
|
26
|
-
from airflow.exceptions import AirflowException
|
|
27
|
-
from airflow.utils.log.logging_mixin import LoggingMixin
|
|
28
|
-
from airflow.hooks.base import BaseHook
|
|
29
|
-
|
|
30
|
-
class TtuHook(BaseHook, LoggingMixin):
|
|
31
|
-
"""
|
|
32
|
-
Interact with Teradata using Teradata Tools and Utilities (TTU) binaries.
|
|
33
|
-
Note: it is required that TTU previously installed and configured propertly.
|
|
34
|
-
|
|
35
|
-
extras example: ``{"bteq_quit_zero":true, "bteq_session_encoding";"UTF8"}``
|
|
36
|
-
"""
|
|
37
|
-
conn_name_attr = 'ttu_conn_id'
|
|
38
|
-
default_conn_name = 'ttu_default'
|
|
39
|
-
conn_type = 'ttu'
|
|
40
|
-
hook_name = 'TTU'
|
|
41
|
-
|
|
42
|
-
def __init__(self, ttu_conn_id: str = 'ttu_default') -> None:
|
|
43
|
-
super().__init__()
|
|
44
|
-
self.ttu_conn_id = ttu_conn_id
|
|
45
|
-
self.conn = None
|
|
46
|
-
|
|
47
|
-
def __enter__(self):
|
|
48
|
-
return self
|
|
49
|
-
|
|
50
|
-
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
51
|
-
if self.conn is not None:
|
|
52
|
-
self.close_conn()
|
|
53
|
-
|
|
54
|
-
def get_conn(self) -> dict:
|
|
55
|
-
if not self.conn:
|
|
56
|
-
connection = self.get_connection(self.ttu_conn_id)
|
|
57
|
-
extras = connection.extra_dejson
|
|
58
|
-
self.conn = dict(
|
|
59
|
-
login=connection.login,
|
|
60
|
-
password=connection.password,
|
|
61
|
-
host=connection.host,
|
|
62
|
-
ttu_log_folder=extras.get('ttu_log_folder', '/tmp'),
|
|
63
|
-
console_output_encoding=extras.get('console_output_encoding', 'utf-8'),
|
|
64
|
-
bteq_session_encoding=extras.get('bteq_session_encoding', 'ASCII'),
|
|
65
|
-
bteq_output_width=extras.get('bteq_output_width', 65531),
|
|
66
|
-
bteq_quit_zero=extras.get('bteq_quit_zero', False),
|
|
67
|
-
sp = None
|
|
68
|
-
)
|
|
69
|
-
return self.conn
|
|
70
|
-
|
|
71
|
-
def close_conn(self):
|
|
72
|
-
"""
|
|
73
|
-
Closes the connection. An error will occur if the
|
|
74
|
-
connection wasn't ever opened.
|
|
75
|
-
"""
|
|
76
|
-
self.conn = None
|
|
77
|
-
|
|
78
|
-
def execute_bteq(self, bteq, xcom_push_flag=False):
|
|
79
|
-
"""
|
|
80
|
-
Executes BTEQ sentences using BTEQ binary.
|
|
81
|
-
:param bteq: string of BTEQ sentences
|
|
82
|
-
:param xcom_push_flag: Flag for pushing last line of BTEQ Log to XCom
|
|
83
|
-
"""
|
|
84
|
-
conn = self.get_conn()
|
|
85
|
-
self.log.info("Executing BTEQ sentences...")
|
|
86
|
-
with TemporaryDirectory(prefix='airflowtmp_ttu_bteq_') as tmpdir:
|
|
87
|
-
with NamedTemporaryFile(dir=tmpdir, mode='wb') as tmpfile:
|
|
88
|
-
bteq_file = self._prepare_bteq_script(bteq,
|
|
89
|
-
conn['host'],
|
|
90
|
-
conn['login'],
|
|
91
|
-
conn['password'],
|
|
92
|
-
conn['bteq_output_width'],
|
|
93
|
-
conn['bteq_session_encoding'],
|
|
94
|
-
conn['bteq_quit_zero']
|
|
95
|
-
)
|
|
96
|
-
self.log.debug(bteq_file)
|
|
97
|
-
tmpfile.write(bytes(bteq_file,'UTF8'))
|
|
98
|
-
tmpfile.flush()
|
|
99
|
-
tmpfile.seek(0)
|
|
100
|
-
|
|
101
|
-
conn['sp'] = subprocess.Popen(['bteq'],
|
|
102
|
-
stdin=tmpfile,
|
|
103
|
-
stdout=subprocess.PIPE,
|
|
104
|
-
stderr=subprocess.STDOUT,
|
|
105
|
-
cwd=tmpdir,
|
|
106
|
-
preexec_fn=os.setsid)
|
|
107
|
-
|
|
108
|
-
line = ''
|
|
109
|
-
failure_line = 'unknown reasons. Please see full BTEQ Output for more details.'
|
|
110
|
-
self.log.info("Output:")
|
|
111
|
-
for line in iter(conn['sp'].stdout.readline, b''):
|
|
112
|
-
line = line.decode(conn['console_output_encoding']).strip()
|
|
113
|
-
self.log.info(line)
|
|
114
|
-
if "Failure" in line:
|
|
115
|
-
#Just save the last failure
|
|
116
|
-
failure_line = line
|
|
117
|
-
conn['sp'].wait()
|
|
118
|
-
|
|
119
|
-
self.log.info("BTEQ command exited with return code {0}".format(conn['sp'].returncode))
|
|
120
|
-
|
|
121
|
-
if conn['sp'].returncode:
|
|
122
|
-
raise AirflowException("BTEQ command exited with return code " + str(conn['sp'].returncode) + ' because of ' +
|
|
123
|
-
failure_line)
|
|
124
|
-
if xcom_push_flag:
|
|
125
|
-
return line
|
|
126
|
-
|
|
127
|
-
def execute_tdload(self, input_file, table, delimiter=';', working_database=None, encoding='UTF8', xcom_push_flag=False, raise_on_rows_error=False, raise_on_rows_duplicated=False, debug=False, max_sessions=1, restart_limit=0):
|
|
128
|
-
"""
|
|
129
|
-
Load a CSV file to Teradata Table (previously created) using tdload binary.
|
|
130
|
-
Note: You need to strip header of the CSV. tdload only accepts rows, not header.
|
|
131
|
-
:param input_file : file to load
|
|
132
|
-
:param table : output table
|
|
133
|
-
:param delimeter : separator of the file to load
|
|
134
|
-
:param encoding : encoding of the file to load
|
|
135
|
-
:param working_database : teradata working database to use for staging data
|
|
136
|
-
:param xcom_push_flag: Flag for pushing last line of BTEQ Log to XCom
|
|
137
|
-
:raise_on_rows_error: if true, raise an error when found error loading some rows.
|
|
138
|
-
:raise_on_rows_duplicated= if true, raise an error when found duplicated rows.
|
|
139
|
-
|
|
140
|
-
"""
|
|
141
|
-
conn = self.get_conn()
|
|
142
|
-
fload_out_path = conn['ttu_log_folder'] + '/tdload/out'
|
|
143
|
-
if not os.path.exists(fload_out_path):
|
|
144
|
-
self.log.debug('Creating directory ' + fload_out_path)
|
|
145
|
-
os.makedirs(fload_out_path)
|
|
146
|
-
|
|
147
|
-
fload_checkpoint_path = conn['ttu_log_folder'] + '/tdload/checkpoint'
|
|
148
|
-
if not os.path.exists(fload_checkpoint_path):
|
|
149
|
-
self.log.debug('Creating directory ' + fload_checkpoint_path)
|
|
150
|
-
os.makedirs(fload_checkpoint_path)
|
|
151
|
-
self.log.info('Loading file ' + input_file + ' into table ' + table + '')
|
|
152
|
-
conn['sp'] = subprocess.Popen(self._prepare_tdload_command(
|
|
153
|
-
input_file,
|
|
154
|
-
conn['host'],
|
|
155
|
-
conn['login'],
|
|
156
|
-
conn['password'],
|
|
157
|
-
encoding,
|
|
158
|
-
table,
|
|
159
|
-
delimiter,
|
|
160
|
-
fload_out_path,
|
|
161
|
-
fload_checkpoint_path,
|
|
162
|
-
max_sessions,
|
|
163
|
-
working_database,
|
|
164
|
-
debug,
|
|
165
|
-
restart_limit
|
|
166
|
-
),
|
|
167
|
-
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,preexec_fn=os.setsid)
|
|
168
|
-
line = ''
|
|
169
|
-
failure_line = 'unknown reasons. Please see full TPT FastLoad Output for more details.'
|
|
170
|
-
rows_in_error_tables_line = 'Total Rows in Error Table'
|
|
171
|
-
rows_duplicated_line = 'Total Duplicate Rows'
|
|
172
|
-
rows_error=0
|
|
173
|
-
rows_duplicated=0
|
|
174
|
-
for line in iter(conn['sp'].stdout.readline, b''):
|
|
175
|
-
line = line.decode(conn['console_output_encoding']).strip()
|
|
176
|
-
self.log.info(line)
|
|
177
|
-
if rows_in_error_tables_line in line:
|
|
178
|
-
# check if we have error rows
|
|
179
|
-
rows_error+=int(line.split(':')[-1].strip())
|
|
180
|
-
if rows_duplicated_line in line:
|
|
181
|
-
# check if we have duplicated rows
|
|
182
|
-
rows_duplicated+=int(line.split(':')[-1].strip())
|
|
183
|
-
if "error" in line:
|
|
184
|
-
# get the last failure
|
|
185
|
-
failure_line = line
|
|
186
|
-
|
|
187
|
-
conn['sp'].wait()
|
|
188
|
-
self.log.info("TPT FastLoad exited with "
|
|
189
|
-
"return code {0}".format(conn['sp'].returncode))
|
|
190
|
-
|
|
191
|
-
if conn['sp'].returncode:
|
|
192
|
-
raise AirflowException("TPT FastLoad exited with return code " + str(conn['sp'].returncode) + ' because of ' +
|
|
193
|
-
failure_line)
|
|
194
|
-
|
|
195
|
-
if rows_error>0 and raise_on_rows_error:
|
|
196
|
-
raise AirflowException("Failed because of errors loading rows (Rows with error: %s )" % rows_error)
|
|
197
|
-
|
|
198
|
-
if rows_duplicated>0 and raise_on_rows_duplicated:
|
|
199
|
-
raise AirflowException("Failed because of errors loading rows (Rows with error: %s )" % rows_error)
|
|
200
|
-
|
|
201
|
-
if xcom_push_flag:
|
|
202
|
-
return line
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def execute_tptexport(self, sql, output_file, delimiter = ';', encoding='UTF8', spool_mode='SPOOL', xcom_push_flag=False, double_quote_varchar=True, max_sessions=1,block_size=1048472):
|
|
206
|
-
"""
|
|
207
|
-
Export a table from Teradata Table using tpt binary.
|
|
208
|
-
Note: The exported CSV file does not contains header row
|
|
209
|
-
:param file : file to load
|
|
210
|
-
:param delimeter : separator of the file to load
|
|
211
|
-
:param encoding : encoding of the file to load
|
|
212
|
-
:param table : output table
|
|
213
|
-
:param max_sessions : max sessions to use
|
|
214
|
-
:param block_size : specifies the block size to use when returning data to the client. The minimum is 256 bytes. The default is 1048472 bytes. The maximum is 16775168 bytes.
|
|
215
|
-
:param spool_mode : ref https://docs.teradata.com/reader/tRbhWsU75TDpkqzyEZReyA/2gbczYmS~PRXRVKD0Dngtg
|
|
216
|
-
:param xcom_push_flag: flag for pushing last line of BTEQ Log to XCom
|
|
217
|
-
:param double_quote_varchar: if true, replace quotes with escaping char for Teradata SQL in TPT
|
|
218
|
-
"""
|
|
219
|
-
conn = self.get_conn()
|
|
220
|
-
fexp_out_path = conn['ttu_log_folder'] + '/tbuild/logs'
|
|
221
|
-
if not os.path.exists(fexp_out_path):
|
|
222
|
-
self.log.debug('Creating directory ' + fexp_out_path)
|
|
223
|
-
os.makedirs(fexp_out_path)
|
|
224
|
-
|
|
225
|
-
fexp_checkpoint_path = conn['ttu_log_folder'] + '/tbuild/checkpoint'
|
|
226
|
-
if not os.path.exists(fexp_checkpoint_path):
|
|
227
|
-
self.log.debug('Creating directory ' + fexp_checkpoint_path)
|
|
228
|
-
os.makedirs(fexp_checkpoint_path)
|
|
229
|
-
if double_quote_varchar:
|
|
230
|
-
sql = sql.replace("'", "''")
|
|
231
|
-
self.log.info("""Exporting SQL '""" + sql + """' to file """ + output_file + """ using TPT Export""")
|
|
232
|
-
|
|
233
|
-
with TemporaryDirectory(prefix='airflowtmp_ttu_tpt') as tmp_dir:
|
|
234
|
-
with NamedTemporaryFile(dir=tmp_dir, prefix=uuid.uuid4().hex, mode='wb') as f:
|
|
235
|
-
f.write(bytes(self._prepare_tpt_export_script(
|
|
236
|
-
sql,
|
|
237
|
-
output_file,
|
|
238
|
-
encoding,
|
|
239
|
-
delimiter,
|
|
240
|
-
spool_mode,
|
|
241
|
-
conn['host'],
|
|
242
|
-
conn['login'],
|
|
243
|
-
conn['password'],
|
|
244
|
-
max_sessions,
|
|
245
|
-
block_size,
|
|
246
|
-
), 'utf_8'))
|
|
247
|
-
f.flush()
|
|
248
|
-
fname = f.name
|
|
249
|
-
self.log.debug("Temporary TPT Template "
|
|
250
|
-
"location :{0}".format(fname))
|
|
251
|
-
f.seek(0)
|
|
252
|
-
conn['sp'] = subprocess.Popen(
|
|
253
|
-
['tbuild', '-f', fname, 'airflow' + '_tpt_' + uuid.uuid4().hex],
|
|
254
|
-
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
255
|
-
preexec_fn=os.setsid)
|
|
256
|
-
|
|
257
|
-
line = ''
|
|
258
|
-
error_line = 'unknown reasons. Please see full TPT Output for more details.'
|
|
259
|
-
for line in iter(conn['sp'].stdout.readline, b''):
|
|
260
|
-
line = line.decode(conn['console_output_encoding']).strip()
|
|
261
|
-
self.log.info(line)
|
|
262
|
-
if "error" in line:
|
|
263
|
-
#Just save the last error
|
|
264
|
-
error_line = line
|
|
265
|
-
conn['sp'].wait()
|
|
266
|
-
self.log.info("tbuild command exited with "
|
|
267
|
-
"return code {0}".format(conn['sp'].returncode))
|
|
268
|
-
if conn['sp'].returncode:
|
|
269
|
-
raise AirflowException("TPT command exited with return code " + str(conn['sp'].returncode) + ' because of ' +
|
|
270
|
-
error_line)
|
|
271
|
-
|
|
272
|
-
if xcom_push_flag:
|
|
273
|
-
return line
|
|
274
|
-
|
|
275
|
-
def on_kill(self):
|
|
276
|
-
self.log.debug('Killing child process...')
|
|
277
|
-
conn = self.get_conn()
|
|
278
|
-
conn['sp'].kill()
|
|
279
|
-
|
|
280
|
-
@staticmethod
|
|
281
|
-
def _prepare_bteq_script(bteq_string, host, login, password, bteq_output_width, bteq_session_encoding, bteq_quit_zero) -> str:
|
|
282
|
-
"""
|
|
283
|
-
Prepare a BTEQ file with connection parameters for executing SQL Sentences with BTEQ syntax.
|
|
284
|
-
:param bteq_string : bteq sentences to execute
|
|
285
|
-
:param host : Teradata Host
|
|
286
|
-
:param login : username for login
|
|
287
|
-
:param password : password for login
|
|
288
|
-
:param bteq_output_width : width of BTEQ output in console
|
|
289
|
-
:param bteq_session_encoding : session encoding. See offical teradata docs for possible values
|
|
290
|
-
:param bteq_quit_zero : if True, force a .QUIT 0 sentence at the end of the sentences (forcing return code = 0)
|
|
291
|
-
"""
|
|
292
|
-
bteq_list = [".LOGON {}/{},{};".format(host, login, password)]
|
|
293
|
-
bteq_list += [".SET WIDTH " + str(bteq_output_width) + ";"]
|
|
294
|
-
bteq_list += [".SET SESSION CHARSET '" + bteq_session_encoding + "';"]
|
|
295
|
-
bteq_list += [bteq_string]
|
|
296
|
-
if bteq_quit_zero:
|
|
297
|
-
bteq_list += [".QUIT 0;"]
|
|
298
|
-
bteq_list += [".LOGOFF;"]
|
|
299
|
-
bteq_list += [".EXIT;"]
|
|
300
|
-
return "\n".join(bteq_list)
|
|
301
|
-
|
|
302
|
-
@staticmethod
|
|
303
|
-
def _prepare_tdload_command(input_file, host, login, password, encoding, table, delimiter, log_path, checkpoint_path, max_sessions, working_database, debug, restart_limit, job_name= 'airflow_tdload') -> str:
|
|
304
|
-
"""
|
|
305
|
-
Prepare a tdload file with connection parameters for loading data from file
|
|
306
|
-
:param input_file : bteq sentences to execute
|
|
307
|
-
:param host : Teradata Host
|
|
308
|
-
:param login : username for login
|
|
309
|
-
:param password : password for login
|
|
310
|
-
:param encoding : width of BTEQ output in console
|
|
311
|
-
:param table : table name. See offical teradata docs for possible values
|
|
312
|
-
:param delimiter : file separator. See offical teradata docs for possible values
|
|
313
|
-
:param log_path : path for command output log
|
|
314
|
-
:param checkpoint_path : path for command checkpoint.
|
|
315
|
-
:param max_sessions : how many sessions we use for loading data
|
|
316
|
-
:param working_database : database for staging data
|
|
317
|
-
:param job_name : job name
|
|
318
|
-
"""
|
|
319
|
-
tdload_command = ['tdload']
|
|
320
|
-
tdload_command += ['-f'] + [input_file]
|
|
321
|
-
tdload_command += ['-u'] + [login]
|
|
322
|
-
tdload_command += ['-p'] + [password]
|
|
323
|
-
tdload_command += ['-h'] + [host]
|
|
324
|
-
tdload_command += ['-c'] + [encoding]
|
|
325
|
-
tdload_command += ['-t'] + [table]
|
|
326
|
-
tdload_command += ['-d'] + [delimiter]
|
|
327
|
-
tdload_command += ['-L'] + [log_path]
|
|
328
|
-
tdload_command += ['-r'] + [checkpoint_path]
|
|
329
|
-
tdload_command += ['-R'] + [str(restart_limit)]
|
|
330
|
-
tdload_command += ['--TargetMaxSessions'] + [str(max_sessions)]
|
|
331
|
-
if working_database:
|
|
332
|
-
tdload_command += ['--TargetWorkingDatabase'] + [working_database]
|
|
333
|
-
if debug:
|
|
334
|
-
tdload_command += ['-x']
|
|
335
|
-
tdload_command += [ "%s_%s" % (job_name, uuid.uuid4().hex) ] #Job Name
|
|
336
|
-
return tdload_command
|
|
337
|
-
|
|
338
|
-
@staticmethod
|
|
339
|
-
def _prepare_tpt_export_script(sql, output_file, encoding, delimiter, spool_mode, host, login, password, max_sessions, block_size,job_name= 'airflow_tptexport') -> str:
|
|
340
|
-
"""
|
|
341
|
-
Prepare a tpt script file with connection parameters for exporting data to CSV
|
|
342
|
-
:param sql : SQL sentence to export
|
|
343
|
-
:param output_file : path to output file
|
|
344
|
-
:param encoding : encoding of exported CSV file (see teradata docs for possible value)
|
|
345
|
-
:param delimiter : Delimiter for exported CSV file
|
|
346
|
-
:param spool_mode : ref https://docs.teradata.com/reader/tRbhWsU75TDpkqzyEZReyA/2gbczYmS~PRXRVKD0Dngtg
|
|
347
|
-
:param host : Teradata Host
|
|
348
|
-
:param login : username for login
|
|
349
|
-
:param password : password for login
|
|
350
|
-
:param max_sessions : how many sessions we use for loading data
|
|
351
|
-
:param block_size : specifies the block size to use when returning data to the client. The minimum is 256 bytes. The default is 1048472 bytes. The maximum is 16775168 bytes.
|
|
352
|
-
:param job_name : job name
|
|
353
|
-
"""
|
|
354
|
-
option_max_sessions = 'MaxSessions = {max_sessions},'.format(max_sessions=max_sessions)
|
|
355
|
-
if max_sessions == -1:
|
|
356
|
-
option_max_sessions = ''
|
|
357
|
-
return '''
|
|
358
|
-
USING CHARACTER SET {encoding}
|
|
359
|
-
DEFINE JOB {job_name}
|
|
360
|
-
(
|
|
361
|
-
APPLY
|
|
362
|
-
TO OPERATOR
|
|
363
|
-
(
|
|
364
|
-
$FILE_WRITER()
|
|
365
|
-
|
|
366
|
-
ATTRIBUTES
|
|
367
|
-
(
|
|
368
|
-
FileName = '{filename}',
|
|
369
|
-
Format = 'DELIMITED',
|
|
370
|
-
OpenMode = 'Write',
|
|
371
|
-
IndicatorMode = 'N',
|
|
372
|
-
TextDelimiter = '{delimiter}'
|
|
373
|
-
)
|
|
374
|
-
)
|
|
375
|
-
SELECT * FROM OPERATOR
|
|
376
|
-
(
|
|
377
|
-
$EXPORT()
|
|
378
|
-
|
|
379
|
-
ATTRIBUTES
|
|
380
|
-
(
|
|
381
|
-
UserName = '{username}',
|
|
382
|
-
UserPassword = '{password}',
|
|
383
|
-
SelectStmt = '{sql}',
|
|
384
|
-
TdpId = '{host}',
|
|
385
|
-
{option_max_sessions}
|
|
386
|
-
SpoolMode = '{spool_mode}',
|
|
387
|
-
BlockSize = {block_size}
|
|
388
|
-
)
|
|
389
|
-
);
|
|
390
|
-
);
|
|
391
|
-
'''.format(filename=output_file, encoding=encoding, delimiter=delimiter, username=login,
|
|
392
|
-
password=password, sql=sql, host=host, option_max_sessions=option_max_sessions, job_name = job_name, spool_mode=spool_mode,block_size=block_size)
|
|
393
|
-
|
|
394
|
-
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
# or more contributor license agreements. See the NOTICE file
|
|
4
|
-
# distributed with this work for additional information
|
|
5
|
-
# regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
# to you under the Apache License, Version 2.0 (the
|
|
7
|
-
# "License"); you may not use this file except in compliance
|
|
8
|
-
# with the License. You may obtain a copy of the License at
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing,
|
|
13
|
-
# software distributed under the License is distributed on an
|
|
14
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
# KIND, either express or implied. See the License for the
|
|
16
|
-
# specific language governing permissions and limitations
|
|
17
|
-
# under the License.
|
|
18
|
-
from typing import Iterable, Mapping, Optional, Union
|
|
19
|
-
|
|
20
|
-
from airflow.models import BaseOperator
|
|
21
|
-
from airflow.providers.teradata.hooks.ttu import TtuHook
|
|
22
|
-
from airflow.utils.decorators import apply_defaults
|
|
23
|
-
|
|
24
|
-
class BteqOperator(BaseOperator):
|
|
25
|
-
"""
|
|
26
|
-
Executes BTEQ code in a specific Teradata database
|
|
27
|
-
|
|
28
|
-
:param sql: the bteq code to be executed. (templated)
|
|
29
|
-
:type sql: Can receive a str representing a sql statement,
|
|
30
|
-
a list of str (sql statements), or reference to a template file.
|
|
31
|
-
Template reference are recognized by str ending in '.sql'
|
|
32
|
-
:param postgres_conn_id: reference to a specific postgres database
|
|
33
|
-
:type postgres_conn_id: str
|
|
34
|
-
:param autocommit: if True, each command is automatically committed.
|
|
35
|
-
(default value: False)
|
|
36
|
-
:type autocommit: bool
|
|
37
|
-
:param parameters: (optional) the parameters to render the SQL query with.
|
|
38
|
-
:type parameters: dict or iterable
|
|
39
|
-
:param database: name of database which overwrite defined one in connection
|
|
40
|
-
:type database: str
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
template_fields = ('sql',)
|
|
44
|
-
template_ext = ('.sql', '.bteq',)
|
|
45
|
-
ui_color = '#ff976d'
|
|
46
|
-
|
|
47
|
-
@apply_defaults
|
|
48
|
-
def __init__(
|
|
49
|
-
self,
|
|
50
|
-
*,
|
|
51
|
-
bteq: str,
|
|
52
|
-
xcom_push: bool = True,
|
|
53
|
-
ttu_conn_id: str = 'ttu_default',
|
|
54
|
-
**kwargs
|
|
55
|
-
) -> None:
|
|
56
|
-
super().__init__(**kwargs)
|
|
57
|
-
self.sql = bteq
|
|
58
|
-
self._hook = None
|
|
59
|
-
self.xcom_push = xcom_push
|
|
60
|
-
self.ttu_conn_id = ttu_conn_id
|
|
61
|
-
|
|
62
|
-
def execute(self, context):
|
|
63
|
-
"""
|
|
64
|
-
Call execute_bteq method from TttuHook to run the provided BTEQ string
|
|
65
|
-
"""
|
|
66
|
-
self._hook = TtuHook(ttu_conn_id=self.ttu_conn_id)
|
|
67
|
-
self._hook.execute_bteq(self.sql, self.xcom_push)
|
|
68
|
-
|
|
69
|
-
def on_kill(self):
|
|
70
|
-
self._hook.on_kill()
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
-
# or more contributor license agreements. See the NOTICE file
|
|
4
|
-
# distributed with this work for additional information
|
|
5
|
-
# regarding copyright ownership. The ASF licenses this file
|
|
6
|
-
# to you under the Apache License, Version 2.0 (the
|
|
7
|
-
# "License"); you may not use this file except in compliance
|
|
8
|
-
# with the License. You may obtain a copy of the License at
|
|
9
|
-
#
|
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
#
|
|
12
|
-
# Unless required by applicable law or agreed to in writing,
|
|
13
|
-
# software distributed under the License is distributed on an
|
|
14
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
-
# KIND, either express or implied. See the License for the
|
|
16
|
-
# specific language governing permissions and limitations
|
|
17
|
-
# under the License.
|
|
18
|
-
from typing import Iterable, Mapping, Optional, Union
|
|
19
|
-
|
|
20
|
-
from airflow.models import BaseOperator
|
|
21
|
-
from airflow.providers.teradata.hooks.ttu import TtuHook
|
|
22
|
-
from airflow.utils.decorators import apply_defaults
|
|
23
|
-
|
|
24
|
-
class FastExportOperator(BaseOperator):
|
|
25
|
-
"""
|
|
26
|
-
Export a table from Teradata to csv file
|
|
27
|
-
:param sql_select_stmt: Select statament to export
|
|
28
|
-
:type sql_select_stmt: str
|
|
29
|
-
:param output_file: output file to export
|
|
30
|
-
:type output_file: str
|
|
31
|
-
"""
|
|
32
|
-
template_fields = ('sql','output_file',)
|
|
33
|
-
template_ext = ('.sql',)
|
|
34
|
-
ui_color = '#a8e4b1'
|
|
35
|
-
|
|
36
|
-
def __init__(
|
|
37
|
-
self,
|
|
38
|
-
*,
|
|
39
|
-
sql_select_stmt: str,
|
|
40
|
-
output_file: str,
|
|
41
|
-
delimiter: str = ';',
|
|
42
|
-
encoding: str = 'UTF8',
|
|
43
|
-
spool_mode: str = 'SPOOL',
|
|
44
|
-
xcom_push: bool = True,
|
|
45
|
-
ttu_conn_id: str = 'ttu_default',
|
|
46
|
-
max_sessions: Optional[int] = 1,
|
|
47
|
-
block_size: Optional[int] = 1048472,
|
|
48
|
-
**kwargs,
|
|
49
|
-
) -> None:
|
|
50
|
-
super().__init__(**kwargs)
|
|
51
|
-
self.sql = sql_select_stmt
|
|
52
|
-
self.output_file = output_file
|
|
53
|
-
self.delimiter = delimiter
|
|
54
|
-
self.encoding = encoding
|
|
55
|
-
self.spool_mode = spool_mode
|
|
56
|
-
self.xcom_push = xcom_push
|
|
57
|
-
self._hook = None
|
|
58
|
-
self.ttu_conn_id = ttu_conn_id
|
|
59
|
-
self.max_sessions = max_sessions
|
|
60
|
-
self.block_size = block_size
|
|
61
|
-
|
|
62
|
-
def execute(self, context):
|
|
63
|
-
"""
|
|
64
|
-
Call the function
|
|
65
|
-
"""
|
|
66
|
-
self._hook = TtuHook(ttu_conn_id=self.ttu_conn_id)
|
|
67
|
-
self._hook.execute_tptexport(sql=self.sql,
|
|
68
|
-
output_file=self.output_file,
|
|
69
|
-
delimiter=self.delimiter,
|
|
70
|
-
encoding=self.encoding,
|
|
71
|
-
spool_mode=self.spool_mode,
|
|
72
|
-
xcom_push_flag=self.xcom_push,
|
|
73
|
-
max_sessions=self.max_sessions,
|
|
74
|
-
block_size=self.block_size
|
|
75
|
-
)
|
|
76
|
-
def on_kill(self):
|
|
77
|
-
self._hook.on_kill()
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
#
|
|
15
|
-
import logging
|
|
16
|
-
|
|
17
|
-
from typing import Iterable, Mapping, Optional, Union
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from airflow.providers.teradata.hooks.ttu import TtuHook
|
|
21
|
-
from airflow.models import BaseOperator
|
|
22
|
-
from airflow.utils.decorators import apply_defaults
|
|
23
|
-
|
|
24
|
-
log = logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class FastLoadOperator(BaseOperator):
|
|
28
|
-
"""
|
|
29
|
-
Load a CSV file (without header) to Teradata Database Table
|
|
30
|
-
:param input_file: output file to export
|
|
31
|
-
:type input_file: str
|
|
32
|
-
:param target_table: target table
|
|
33
|
-
:type target_table: str
|
|
34
|
-
:param delimiter: file delimiter
|
|
35
|
-
:type delimiter: str
|
|
36
|
-
:param working_database: database to create log tables
|
|
37
|
-
:type working_database: str
|
|
38
|
-
:param encoding: encoding of the file
|
|
39
|
-
:type encoding: str
|
|
40
|
-
:param xcom_push: True if return last log to xcom
|
|
41
|
-
:type xcom_push: Bool
|
|
42
|
-
"""
|
|
43
|
-
template_fields = ('input_file', 'target_table', 'preoperator_bteq',)
|
|
44
|
-
template_ext = ('.sql', '.bteq',)
|
|
45
|
-
ui_color = '#4aa3ba'
|
|
46
|
-
|
|
47
|
-
@apply_defaults
|
|
48
|
-
def __init__(
|
|
49
|
-
self,
|
|
50
|
-
*,
|
|
51
|
-
input_file: str,
|
|
52
|
-
target_table: str,
|
|
53
|
-
working_database: str,
|
|
54
|
-
delimiter: str = ';',
|
|
55
|
-
encoding: str = 'UTF8',
|
|
56
|
-
preoperator_bteq: Optional[str],
|
|
57
|
-
raise_on_rows_error: bool = True,
|
|
58
|
-
raise_on_rows_duplicated: bool = True,
|
|
59
|
-
xcom_push: bool = True,
|
|
60
|
-
ttu_conn_id: str = 'ttu_default',
|
|
61
|
-
max_sessions: Optional[int] = 1,
|
|
62
|
-
debug: Optional[bool] = False,
|
|
63
|
-
**kwargs,
|
|
64
|
-
) -> None:
|
|
65
|
-
super().__init__(**kwargs)
|
|
66
|
-
self.input_file = input_file
|
|
67
|
-
self.target_table = target_table
|
|
68
|
-
self.working_database = working_database
|
|
69
|
-
self.delimiter = delimiter
|
|
70
|
-
self.encoding = encoding
|
|
71
|
-
self.xcom_push = xcom_push
|
|
72
|
-
self._hook = None
|
|
73
|
-
self.ttu_conn_id = ttu_conn_id
|
|
74
|
-
self.preoperator_bteq = preoperator_bteq
|
|
75
|
-
self.raise_on_rows_error = raise_on_rows_error
|
|
76
|
-
self.raise_on_rows_duplicated = raise_on_rows_duplicated
|
|
77
|
-
self.max_sessions = max_sessions
|
|
78
|
-
self.debug = debug
|
|
79
|
-
|
|
80
|
-
def execute(self, context):
|
|
81
|
-
"""
|
|
82
|
-
Call the executable from teradata
|
|
83
|
-
"""
|
|
84
|
-
self._hook = TtuHook(ttu_conn_id=self.ttu_conn_id)
|
|
85
|
-
|
|
86
|
-
if self.preoperator_bteq:
|
|
87
|
-
logging.info('Executing preoperator BTEQ')
|
|
88
|
-
self._hook.execute_bteq(self.preoperator_bteq, self.xcom_push)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
logging.info('Executing tdload command')
|
|
92
|
-
self._hook.execute_tdload(
|
|
93
|
-
input_file=self.input_file,
|
|
94
|
-
table=self.target_table,
|
|
95
|
-
working_database=self.working_database,
|
|
96
|
-
delimiter=self.delimiter,
|
|
97
|
-
encoding=self.encoding,
|
|
98
|
-
xcom_push_flag=self.xcom_push,
|
|
99
|
-
raise_on_rows_error=self.raise_on_rows_error,
|
|
100
|
-
raise_on_rows_duplicated=self.raise_on_rows_duplicated,
|
|
101
|
-
debug=self.debug,
|
|
102
|
-
max_sessions=self.max_sessions
|
|
103
|
-
)
|
|
104
|
-
def on_kill(self):
|
|
105
|
-
self._hook.on_kill()
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2021, Felipe Lolas
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|