2016-07-28 14:07:07 -07:00
|
|
|
#
|
|
|
|
# Copyright 2015 LinkedIn Corp. All rights reserved.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
#
|
|
|
|
|
2017-04-25 15:11:02 -07:00
|
|
|
import csv
|
2016-07-28 14:07:07 -07:00
|
|
|
import datetime
|
2017-07-24 11:11:03 -07:00
|
|
|
import FileUtil
|
2017-04-25 15:11:02 -07:00
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
from com.ziclix.python.sql import zxJDBC
|
2016-07-28 14:07:07 -07:00
|
|
|
from org.slf4j import LoggerFactory
|
2017-07-24 11:11:03 -07:00
|
|
|
from wherehows.common import Constant
|
2017-04-25 15:11:02 -07:00
|
|
|
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
class OracleExtract:
|
|
|
|
table_dict = {}
|
|
|
|
table_output_list = []
|
|
|
|
field_output_list = []
|
2017-07-24 11:11:03 -07:00
|
|
|
sample_output_list = []
|
|
|
|
|
|
|
|
ignored_owner_regex = 'ANONYMOUS|PUBLIC|SYS|SYSTEM|DBSNMP|MDSYS|CTXSYS|XDB|TSMSYS|ORACLE.*|APEX.*|TEST?*|GG_.*|\$'
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.logger = LoggerFactory.getLogger('jython script : ' + self.__class__.__name__)
|
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
def get_table_info(self, excluded_owner_list, table_name):
|
|
|
|
"""
|
2016-07-28 14:07:07 -07:00
|
|
|
get table, column info from Oracle all_tables
|
2017-07-24 11:11:03 -07:00
|
|
|
here Owner, Schema, Database have same meaning: a collection of tables
|
|
|
|
:param excluded_owner_list: schema blacklist
|
|
|
|
:param table_name: get specific table name, not used in common case
|
2016-07-28 14:07:07 -07:00
|
|
|
:return:
|
2017-07-24 11:11:03 -07:00
|
|
|
"""
|
|
|
|
owner_exclusion_filter = ''
|
2016-07-28 14:07:07 -07:00
|
|
|
table_name_filter = ''
|
2017-07-24 11:11:03 -07:00
|
|
|
if excluded_owner_list and len(excluded_owner_list) > 0:
|
|
|
|
owner_exclusion_filter = " AND NOT REGEXP_LIKE(t.OWNER, '%s') " % '|'.join(excluded_owner_list)
|
|
|
|
self.logger.info("Get Oracle metadata with extra excluded schema: %s" % excluded_owner_list)
|
2016-07-28 14:07:07 -07:00
|
|
|
if table_name and len(table_name) > 0:
|
|
|
|
if table_name.find('.') > 0:
|
|
|
|
table_name_filter = " AND OWNER='%s' AND TABLE_NAME='%s' " % table_name.split('.')
|
|
|
|
else:
|
|
|
|
table_name_filter = " AND TABLE_NAME='%s' " % table_name
|
|
|
|
self.logger.info("Get Oracle metadata with extra filter: %s" % table_name_filter)
|
|
|
|
|
|
|
|
curs_meta = self.conn_db.cursor()
|
|
|
|
|
|
|
|
column_info_sql = """
|
|
|
|
select
|
|
|
|
t.OWNER, t.TABLE_NAME, t.PARTITIONED,
|
|
|
|
c.COLUMN_ID, c.COLUMN_NAME, c.DATA_TYPE, c.NULLABLE,
|
|
|
|
c.DATA_LENGTH, c.DATA_PRECISION, c.DATA_SCALE,
|
|
|
|
c.CHAR_LENGTH, c.CHARACTER_SET_NAME,
|
|
|
|
c.DATA_DEFAULT, m.COMMENTS
|
|
|
|
from ALL_TABLES t
|
|
|
|
join ALL_TAB_COLUMNS c
|
|
|
|
on t.OWNER = c.OWNER
|
|
|
|
and t.TABLE_NAME = c.TABLE_NAME
|
|
|
|
left join ALL_COL_COMMENTS m
|
|
|
|
on c.OWNER = m.OWNER
|
|
|
|
and c.TABLE_NAME = m.TABLE_NAME
|
|
|
|
and c.COLUMN_NAME = m.COLUMN_NAME
|
2017-07-24 11:11:03 -07:00
|
|
|
where NOT REGEXP_LIKE(t.OWNER, '%s')
|
|
|
|
%s /* extra excluded schema/owner */
|
|
|
|
%s /* table filter */
|
2016-07-28 14:07:07 -07:00
|
|
|
order by 1,2,4
|
2017-07-24 11:11:03 -07:00
|
|
|
""" % (self.ignored_owner_regex, owner_exclusion_filter, table_name_filter)
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
self.logger.debug(column_info_sql)
|
|
|
|
curs_meta.execute(column_info_sql)
|
|
|
|
|
|
|
|
rows = curs_meta.fetchall()
|
2017-07-24 11:11:03 -07:00
|
|
|
self.logger.info("Fetched %d records of Oracle metadata" % len(rows))
|
|
|
|
curs_meta.close()
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
prev_table_key = ''
|
|
|
|
for row in rows:
|
|
|
|
current_table_key = "%s.%s" % (row[0], row[1]) # OWNER.TABLE_NAME
|
|
|
|
if current_table_key != prev_table_key:
|
|
|
|
self.table_dict[current_table_key] = {"partitioned": row[2]}
|
|
|
|
prev_table_key = current_table_key
|
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
self.logger.info("Fetched %d tables: %s" % (len(self.table_dict), self.table_dict))
|
2016-07-28 14:07:07 -07:00
|
|
|
return rows
|
|
|
|
|
|
|
|
def get_extra_table_info(self):
|
2017-07-24 11:11:03 -07:00
|
|
|
"""
|
2016-07-28 14:07:07 -07:00
|
|
|
Index, Partition, Size info
|
|
|
|
:return: index,partition,constraint
|
2017-07-24 11:11:03 -07:00
|
|
|
"""
|
2016-07-28 14:07:07 -07:00
|
|
|
index_info_sql = """
|
|
|
|
select
|
|
|
|
i.TABLE_OWNER, i.TABLE_NAME, i.INDEX_NAME, i.INDEX_TYPE, i.UNIQUENESS,
|
|
|
|
t.CONSTRAINT_NAME,
|
|
|
|
--LISTAGG(c.COLUMN_NAME,',')
|
|
|
|
-- WITHIN GROUP (ORDER BY c.COLUMN_POSITION) as INDEX_COLUMNS,
|
|
|
|
RTRIM(XMLAGG(xmlelement(s,c.COLUMN_NAME,',').extract('//text()')
|
|
|
|
ORDER BY c.COLUMN_POSITION),',') INDEX_COLUMNS,
|
|
|
|
COUNT(1) NUM_COLUMNS
|
|
|
|
from ALL_INDEXES i
|
|
|
|
join ALL_IND_COLUMNS c
|
|
|
|
on i.OWNER = c.INDEX_OWNER
|
|
|
|
and i.INDEX_NAME = c.INDEX_NAME
|
|
|
|
and i.TABLE_OWNER = c.TABLE_OWNER
|
|
|
|
and i.TABLE_NAME = c.TABLE_NAME
|
|
|
|
left join (select coalesce(INDEX_OWNER,OWNER) OWNER, INDEX_NAME, CONSTRAINT_NAME
|
|
|
|
from ALL_CONSTRAINTS t
|
|
|
|
where INDEX_NAME IS NOT NULL) t
|
|
|
|
on i.OWNER = t.OWNER
|
|
|
|
and i.INDEX_NAME = t.INDEX_NAME
|
2017-07-24 11:11:03 -07:00
|
|
|
where NOT REGEXP_LIKE(i.TABLE_OWNER, '%s')
|
2016-07-28 14:07:07 -07:00
|
|
|
group by i.TABLE_OWNER, i.TABLE_NAME, i.INDEX_NAME,
|
|
|
|
i.INDEX_TYPE, i.UNIQUENESS, t.CONSTRAINT_NAME
|
2017-07-24 11:11:03 -07:00
|
|
|
order by 1,2,3
|
|
|
|
""" % self.ignored_owner_regex
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
partition_col_sql = """
|
|
|
|
select
|
|
|
|
OWNER TABLE_OWNER, NAME TABLE_NAME,
|
|
|
|
RTRIM(XMLAGG(xmlelement(s,c.COLUMN_NAME,',').extract('//text()')
|
|
|
|
ORDER BY c.COLUMN_POSITION),',') PARTITION_COLUMNS,
|
|
|
|
COUNT(1) NUM_COLUMNS
|
|
|
|
from ALL_PART_KEY_COLUMNS c
|
2017-07-24 11:11:03 -07:00
|
|
|
where c.OBJECT_TYPE = 'TABLE' and NOT REGEXP_LIKE(c.OWNER, '%s')
|
2016-07-28 14:07:07 -07:00
|
|
|
group by c.OWNER, c.NAME
|
2017-07-24 11:11:03 -07:00
|
|
|
order by 1,2
|
|
|
|
""" % self.ignored_owner_regex
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
curs_meta = self.conn_db.cursor()
|
|
|
|
|
|
|
|
# get index and partition info one by one
|
|
|
|
|
|
|
|
curs_meta.execute(partition_col_sql)
|
|
|
|
rows = curs_meta.fetchall()
|
|
|
|
for row in rows:
|
|
|
|
table_name_key = "%s.%s" % (row[0], row[1])
|
|
|
|
if table_name_key not in self.table_dict:
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.table_dict[table_name_key]['partition_columns'] = row[2]
|
2017-07-24 11:11:03 -07:00
|
|
|
self.logger.info("Found %d record for partition info" % len(rows))
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
curs_meta.execute(index_info_sql)
|
|
|
|
rows = curs_meta.fetchall()
|
2017-07-24 11:11:03 -07:00
|
|
|
curs_meta.close()
|
2016-07-28 14:07:07 -07:00
|
|
|
for row in rows:
|
|
|
|
table_name_key = "%s.%s" % (row[0], row[1])
|
|
|
|
if table_name_key not in self.table_dict:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if "indexes" not in self.table_dict[table_name_key]:
|
|
|
|
self.table_dict[table_name_key]["indexes"] = []
|
|
|
|
|
|
|
|
self.table_dict[table_name_key]["indexes"].append(
|
|
|
|
{
|
|
|
|
"name": row[2],
|
|
|
|
"type": row[3],
|
|
|
|
"is_unique": 'Y' if row[4] == 'UNIQUE' else 'N',
|
|
|
|
"constraint_name": row[5],
|
|
|
|
"index_columns": row[6],
|
|
|
|
"num_of_columns": row[7]
|
|
|
|
}
|
|
|
|
)
|
2017-07-24 11:11:03 -07:00
|
|
|
self.logger.info("Found %d record for index info" % len(rows))
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
def format_table_metadata(self, rows):
|
|
|
|
"""
|
|
|
|
add table info with columns from rows into table schema
|
|
|
|
:param rows: input. each row is a table column
|
2016-07-28 14:07:07 -07:00
|
|
|
:param schema: {schema : _, type : _, tables : ['name' : _, ... 'original_name' : _] }
|
|
|
|
:return:
|
2017-07-24 11:11:03 -07:00
|
|
|
"""
|
2016-07-28 14:07:07 -07:00
|
|
|
schema_dict = {"fields": []}
|
|
|
|
table_record = {}
|
|
|
|
table_idx = 0
|
|
|
|
field_idx = 0
|
|
|
|
|
|
|
|
for row in rows:
|
|
|
|
table_name_key = "%s.%s" % (row[0], row[1])
|
|
|
|
table_urn = "oracle:///%s/%s" % (row[0], row[1])
|
|
|
|
|
|
|
|
if 'urn' not in table_record or table_urn != table_record['urn']:
|
|
|
|
# This is a new table. Let's push the previous table record into output_list
|
|
|
|
if 'urn' in table_record:
|
|
|
|
schema_dict["num_fields"] = field_idx
|
2016-08-03 18:49:00 -07:00
|
|
|
table_record["columns"] = json.dumps(schema_dict)
|
2016-07-28 14:07:07 -07:00
|
|
|
self.table_output_list.append(table_record)
|
|
|
|
|
2016-08-03 18:49:00 -07:00
|
|
|
properties = {
|
|
|
|
"indexes": self.table_dict[table_name_key].get("indexes"),
|
|
|
|
"partition_column": self.table_dict[table_name_key].get("partition_column")
|
|
|
|
}
|
2016-07-28 14:07:07 -07:00
|
|
|
table_record = {
|
|
|
|
"name": row[1],
|
2016-08-03 18:49:00 -07:00
|
|
|
"columns": None,
|
2016-07-28 14:07:07 -07:00
|
|
|
"schema_type": "JSON",
|
2016-08-03 18:49:00 -07:00
|
|
|
"properties": json.dumps(properties),
|
2016-07-28 14:07:07 -07:00
|
|
|
"urn": table_urn,
|
|
|
|
"source": "Oracle",
|
|
|
|
"location_prefix": row[0],
|
|
|
|
"parent_name": row[0],
|
|
|
|
"storage_type": "Table",
|
|
|
|
"dataset_type": "oracle",
|
|
|
|
"is_partitioned": 'Y' if self.table_dict[table_name_key]["partitioned"] == 'YES' else 'N'
|
|
|
|
}
|
|
|
|
schema_dict = {"fields": []}
|
|
|
|
table_idx += 1
|
|
|
|
field_idx = 0
|
|
|
|
|
|
|
|
field_record = {
|
|
|
|
"sort_id": self.num_to_int(row[3]),
|
|
|
|
"name": row[4],
|
|
|
|
"data_type": row[5],
|
|
|
|
"nullable": row[6],
|
|
|
|
"size": self.num_to_int(row[7]),
|
|
|
|
"precision": self.num_to_int(row[8]),
|
|
|
|
"scale": self.num_to_int(row[9]),
|
|
|
|
"default_value": self.trim_newline(row[12]),
|
|
|
|
"doc": self.trim_newline(row[13])
|
|
|
|
}
|
|
|
|
schema_dict['fields'].append(field_record)
|
|
|
|
field_record['dataset_urn'] = table_urn
|
|
|
|
self.field_output_list.append(field_record)
|
|
|
|
field_idx += 1
|
|
|
|
|
|
|
|
# finish all remaining rows
|
|
|
|
schema_dict["num_fields"] = field_idx
|
2016-08-03 18:49:00 -07:00
|
|
|
table_record["columns"] = json.dumps(schema_dict)
|
2016-07-28 14:07:07 -07:00
|
|
|
self.table_output_list.append(table_record)
|
|
|
|
self.logger.info("%d Table records generated" % table_idx)
|
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
def get_sample_data(self, table_fullname, num_rows):
|
2016-07-28 14:07:07 -07:00
|
|
|
"""
|
2017-07-24 11:11:03 -07:00
|
|
|
select top rows from table as sample data
|
|
|
|
:return: json of sample data
|
2016-07-28 14:07:07 -07:00
|
|
|
"""
|
2017-07-24 11:11:03 -07:00
|
|
|
table_urn = "oracle:///%s" % (table_fullname.replace('.', '/'))
|
2016-07-28 14:07:07 -07:00
|
|
|
columns = []
|
2017-07-24 11:11:03 -07:00
|
|
|
sample_data = []
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
sql = 'SELECT * FROM %s WHERE ROWNUM <= %d' % (table_fullname, num_rows)
|
|
|
|
cursor = self.conn_db.cursor()
|
2016-07-28 14:07:07 -07:00
|
|
|
try:
|
2017-07-24 11:11:03 -07:00
|
|
|
cursor.execute(sql)
|
|
|
|
rows = cursor.fetchall()
|
|
|
|
|
2016-07-28 14:07:07 -07:00
|
|
|
if len(rows) == 0:
|
2017-07-24 11:11:03 -07:00
|
|
|
self.logger.error("dataset {} is empty".format(table_fullname))
|
|
|
|
return
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
# retrieve column names
|
|
|
|
columns = [i[0] for i in cursor.description]
|
|
|
|
# self.logger.info("Table {} columns: {}".format(table_fullname, columns))
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
# retrieve data
|
|
|
|
for row in rows:
|
|
|
|
row_data = []
|
|
|
|
# encode each field to a new value
|
|
|
|
for value in row:
|
|
|
|
if value is None:
|
|
|
|
row_data.append('')
|
|
|
|
else:
|
|
|
|
row_data.append(unicode(value, errors='ignore'))
|
|
|
|
sample_data.append(row_data)
|
|
|
|
except Exception as ex:
|
2017-07-26 13:25:14 -07:00
|
|
|
self.logger.error("Error fetch sample for {}: {}".format(table_fullname, str(ex)))
|
2017-07-24 11:11:03 -07:00
|
|
|
return
|
|
|
|
|
|
|
|
cursor.close()
|
|
|
|
data_with_column = map(lambda x: dict(zip(columns, x)), sample_data)
|
|
|
|
self.sample_output_list.append({'dataset_urn': table_urn, 'sample_data': data_with_column})
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
def num_to_int(self, num):
|
|
|
|
try:
|
|
|
|
return int(num)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def trim_newline(self, line):
|
2017-04-04 13:02:46 -07:00
|
|
|
return line.replace('\n', ' ').replace('\r', ' ').strip().encode('ascii', 'ignore') if line else None
|
2016-08-03 18:49:00 -07:00
|
|
|
|
|
|
|
def write_csv(self, csv_filename, csv_columns, data_list):
|
|
|
|
csvfile = open(csv_filename, 'wb')
|
|
|
|
os.chmod(csv_filename, 0644)
|
|
|
|
writer = csv.DictWriter(csvfile, fieldnames=csv_columns, delimiter='\x1A', lineterminator='\n',
|
|
|
|
quoting=csv.QUOTE_NONE, quotechar='\1', escapechar='\0')
|
|
|
|
writer.writeheader()
|
|
|
|
for data in data_list:
|
|
|
|
writer.writerow(data)
|
|
|
|
csvfile.close()
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
def run(self, exclude_database_list, table_name, table_file, field_file, sample_file, sample=False):
|
2016-07-28 14:07:07 -07:00
|
|
|
"""
|
|
|
|
The entrance of the class, extract schema and sample data
|
|
|
|
Notice the database need to have a order that the databases have more info (DWH_STG) should be scaned first.
|
2017-07-24 11:11:03 -07:00
|
|
|
:param exclude_database_list: list of excluded databases/owners/schemas
|
|
|
|
:param table_name: specific table name to query
|
|
|
|
:param table_file: table output csv file path
|
|
|
|
:param field_file: table fields output csv file path
|
|
|
|
:param sample_file: sample data output csv file path
|
|
|
|
:param sample: do sample or not
|
2016-07-28 14:07:07 -07:00
|
|
|
:return:
|
|
|
|
"""
|
2017-07-24 11:11:03 -07:00
|
|
|
begin = datetime.datetime.now().strftime("%H:%M:%S")
|
|
|
|
# collect table info
|
|
|
|
rows = self.get_table_info(exclude_database_list, table_name)
|
|
|
|
self.get_extra_table_info()
|
|
|
|
self.format_table_metadata(rows)
|
|
|
|
mid = datetime.datetime.now().strftime("%H:%M:%S")
|
|
|
|
self.logger.info("Collecting table info [%s -> %s]" % (str(begin), str(mid)))
|
|
|
|
|
|
|
|
csv_columns = ['name', 'columns', 'schema_type', 'properties', 'urn', 'source', 'location_prefix',
|
|
|
|
'parent_name', 'storage_type', 'dataset_type', 'is_partitioned']
|
|
|
|
self.write_csv(table_file, csv_columns, self.table_output_list)
|
|
|
|
|
|
|
|
csv_columns = ['dataset_urn', 'sort_id', 'name', 'data_type', 'nullable',
|
|
|
|
'size', 'precision', 'scale', 'default_value', 'doc']
|
|
|
|
self.write_csv(field_file, csv_columns, self.field_output_list)
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
if sample:
|
|
|
|
# collect sample data
|
2017-07-24 11:11:03 -07:00
|
|
|
for table in self.table_dict.keys():
|
|
|
|
self.get_sample_data(table, 10)
|
|
|
|
end = datetime.datetime.now().strftime("%H:%M:%S")
|
|
|
|
self.logger.info("Collecting sample data [%s -> %s]" % (str(mid), str(end)))
|
|
|
|
|
|
|
|
csv_columns = ['dataset_urn', 'sample_data']
|
|
|
|
self.write_csv(sample_file, csv_columns, self.sample_output_list)
|
2016-07-28 14:07:07 -07:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
args = sys.argv[1]
|
|
|
|
|
|
|
|
# connection
|
|
|
|
username = args[Constant.ORA_DB_USERNAME_KEY]
|
|
|
|
password = args[Constant.ORA_DB_PASSWORD_KEY]
|
|
|
|
JDBC_DRIVER = args[Constant.ORA_DB_DRIVER_KEY]
|
|
|
|
JDBC_URL = args[Constant.ORA_DB_URL_KEY]
|
|
|
|
|
|
|
|
e = OracleExtract()
|
|
|
|
e.conn_db = zxJDBC.connect(JDBC_URL, username, password, JDBC_DRIVER)
|
2017-07-26 13:25:14 -07:00
|
|
|
|
|
|
|
exclude_databases = filter(bool, args[Constant.ORA_EXCLUDE_DATABASES_KEY].split(','))
|
2016-07-28 14:07:07 -07:00
|
|
|
collect_sample = False
|
|
|
|
if Constant.ORA_LOAD_SAMPLE in args:
|
2017-07-26 13:25:14 -07:00
|
|
|
collect_sample = FileUtil.parse_bool(args[Constant.ORA_LOAD_SAMPLE], False)
|
2016-07-28 14:07:07 -07:00
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
temp_dir = FileUtil.etl_temp_dir(args, "ORACLE")
|
2017-04-25 15:11:02 -07:00
|
|
|
table_output_file = os.path.join(temp_dir, args[Constant.ORA_SCHEMA_OUTPUT_KEY])
|
|
|
|
field_output_file = os.path.join(temp_dir, args[Constant.ORA_FIELD_OUTPUT_KEY])
|
|
|
|
sample_output_file = os.path.join(temp_dir, args[Constant.ORA_SAMPLE_OUTPUT_KEY])
|
|
|
|
|
2016-07-28 14:07:07 -07:00
|
|
|
try:
|
|
|
|
e.conn_db.cursor().execute("ALTER SESSION SET TIME_ZONE = 'US/Pacific'")
|
|
|
|
e.conn_db.cursor().execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'")
|
|
|
|
e.conn_db.cursor().execute("CALL dbms_application_info.set_module('%s','%d')" %
|
|
|
|
('WhereHows (Jython)', os.getpid()))
|
|
|
|
e.conn_db.commit()
|
|
|
|
|
2017-07-24 11:11:03 -07:00
|
|
|
e.run(exclude_databases,
|
2017-04-25 15:11:02 -07:00
|
|
|
None,
|
|
|
|
table_output_file,
|
|
|
|
field_output_file,
|
|
|
|
sample_output_file,
|
2016-07-28 14:07:07 -07:00
|
|
|
sample=collect_sample)
|
|
|
|
finally:
|
|
|
|
e.conn_db.cursor().close()
|
|
|
|
e.conn_db.close()
|