Skip to content

Commit 9b2398e

Browse files
committed
Updated the logic for retrieving database metadata through the getCatalogs(), getSchemas(), getTables(), and getColumns() API methods
1 parent 4a7b450 commit 9b2398e

17 files changed

+2189
-106
lines changed

redshift_connector/cursor.py

Lines changed: 210 additions & 100 deletions
Large diffs are not rendered by default.
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import logging
2+
import typing
3+
import re
4+
5+
6+
from redshift_connector.utils.oids import RedshiftOID
7+
from redshift_connector.utils.sql_types import SQLType
8+
9+
from redshift_connector.error import (
10+
MISSING_MODULE_ERROR_MSG,
11+
InterfaceError,
12+
ProgrammingError,
13+
)
14+
15+
_logger: logging.Logger = logging.getLogger(__name__)
16+
17+
18+
class MetadataAPIHelper:
19+
def __init__(self: "MetadataAPIHelper") -> None:
20+
self._CatalogsColNum: int = 1
21+
self._get_catalogs_col: typing.Dict = {"TABLE_CAT": int(RedshiftOID.VARCHAR)}
22+
23+
self._SchemasColNum: int = 2
24+
self._get_schemas_col: typing.Dict = {"TABLE_SCHEM": int(RedshiftOID.VARCHAR),
25+
"TABLE_CATALOG": int(RedshiftOID.VARCHAR)}
26+
27+
self._TablesColNum: int = 10
28+
self._get_tables_col: typing.Dict = {"TABLE_CAT": int(RedshiftOID.VARCHAR),
29+
"TABLE_SCHEM": int(RedshiftOID.VARCHAR),
30+
"TABLE_NAME": int(RedshiftOID.VARCHAR),
31+
"TABLE_TYPE": int(RedshiftOID.VARCHAR),
32+
"REMARKS": int(RedshiftOID.VARCHAR),
33+
"TYPE_CAT": int(RedshiftOID.VARCHAR),
34+
"TYPE_SCHEM": int(RedshiftOID.VARCHAR),
35+
"TYPE_NAME": int(RedshiftOID.VARCHAR),
36+
"SELF_REFERENCING_COL_NAME": int(RedshiftOID.VARCHAR),
37+
"REF_GENERATION": int(RedshiftOID.VARCHAR)}
38+
39+
self._ColumnsColNum: int = 24
40+
self._get_columns_col: typing.Dict = {"TABLE_CAT": int(RedshiftOID.VARCHAR),
41+
"TABLE_SCHEM": int(RedshiftOID.VARCHAR),
42+
"TABLE_NAME": int(RedshiftOID.VARCHAR),
43+
"COLUMN_NAME": int(RedshiftOID.VARCHAR),
44+
"DATA_TYPE": int(RedshiftOID.INTEGER),
45+
"TYPE_NAME": int(RedshiftOID.VARCHAR),
46+
"COLUMN_SIZE": int(RedshiftOID.INTEGER),
47+
"BUFFER_LENGTH": int(RedshiftOID.INTEGER),
48+
"DECIMAL_DIGITS": int(RedshiftOID.INTEGER),
49+
"NUM_PREC_RADIX": int(RedshiftOID.INTEGER),
50+
"NULLABLE": int(RedshiftOID.INTEGER),
51+
"REMARKS": int(RedshiftOID.VARCHAR),
52+
"COLUMN_DEF": int(RedshiftOID.VARCHAR),
53+
"SQL_DATA_TYPE": int(RedshiftOID.INTEGER),
54+
"SQL_DATETIME_SUB": int(RedshiftOID.INTEGER),
55+
"CHAR_OCTET_LENGTH": int(RedshiftOID.INTEGER),
56+
"ORDINAL_POSITION": int(RedshiftOID.INTEGER),
57+
"IS_NULLABLE": int(RedshiftOID.VARCHAR),
58+
"SCOPE_CATALOG": int(RedshiftOID.VARCHAR),
59+
"SCOPE_SCHEMA": int(RedshiftOID.VARCHAR),
60+
"SCOPE_TABLE": int(RedshiftOID.VARCHAR),
61+
"SOURCE_DATA_TYPE": int(RedshiftOID.SMALLINT),
62+
"IS_AUTOINCREMENT": int(RedshiftOID.VARCHAR),
63+
"IS_GENERATEDCOLUMN": int(RedshiftOID.VARCHAR)}
64+
65+
self._SHOW_DATABASES_database_name: str = 'database_name'
66+
67+
self._SHOW_SCHEMA_database_name: str = 'database_name'
68+
self._SHOW_SCHEMA_schema_name: str = 'schema_name'
69+
70+
self._SHOW_TABLES_database_name: str = 'database_name'
71+
self._SHOW_TABLES_schema_name: str = 'schema_name'
72+
self._SHOW_TABLES_table_name: str = 'table_name'
73+
self._SHOW_TABLES_table_type: str = 'table_type'
74+
self._SHOW_TABLES_remarks: str = 'remarks'
75+
76+
self._SHOW_COLUMNS_database_name: str = "database_name"
77+
self._SHOW_COLUMNS_schema_name: str = "schema_name"
78+
self._SHOW_COLUMNS_table_name: str = "table_name"
79+
self._SHOW_COLUMNS_column_name: str = "column_name"
80+
self._SHOW_COLUMNS_ordinal_position: str = "ordinal_position"
81+
self._SHOW_COLUMNS_column_default: str = "column_default"
82+
self._SHOW_COLUMNS_is_nullable: str = "is_nullable"
83+
self._SHOW_COLUMNS_data_type: str = "data_type"
84+
self._SHOW_COLUMNS_character_maximum_length: str = "character_maximum_length"
85+
self._SHOW_COLUMNS_numeric_precision: str = "numeric_precision"
86+
self._SHOW_COLUMNS_numeric_scale: str = "numeric_scale"
87+
self._SHOW_COLUMNS_remarks: str = "remarks"
88+
89+
90+
self._sql_show_databases: str = "SHOW DATABASES;"
91+
self._sql_show_databases_like: str = "SHOW DATABASES LIKE '{0}';"
92+
self._sql_show_schemas: str = "SHOW SCHEMAS FROM DATABASE {0};"
93+
self._sql_show_schemas_like: str = "SHOW SCHEMAS FROM DATABASE {0} LIKE '{1}';"
94+
self._sql_show_tables: str = "SHOW TABLES FROM SCHEMA {0}.{1};"
95+
self._sql_show_tables_like: str = "SHOW TABLES FROM SCHEMA {0}.{1} LIKE '{2}';"
96+
self._sql_show_columns: str = "SHOW COLUMNS FROM TABLE {0}.{1}.{2};"
97+
self._sql_show_columns_like: str = "SHOW COLUMNS FROM TABLE {0}.{1}.{2} LIKE '{3}';"
98+
99+
self.__rs_type_map = {
100+
"character varying": "varchar",
101+
"\"char\"": "char",
102+
"character": "char",
103+
"smallint": "int2",
104+
"integer": "int4",
105+
"bigint": "int8",
106+
"real": "float4",
107+
"double precision": "float8",
108+
"boolean": "bool",
109+
"time without time zone": "time",
110+
"time with time zone": "timetz",
111+
"timestamp without time zone": "timestamp",
112+
"timestamp with time zone": "timestamptz",
113+
"interval year to month": "intervaly2m",
114+
"interval year": "intervaly2m",
115+
"interval month": "intervaly2m",
116+
"interval day to second": "intervald2s",
117+
"interval day": "intervald2s",
118+
"interval second": "intervald2s",
119+
"binary varying": "varbyte"
120+
}
121+
122+
self.__sql_type_mapping = {
123+
"varchar": int(SQLType.SQL_VARCHAR),
124+
"char": int(SQLType.SQL_CHAR),
125+
"int2": int(SQLType.SQL_SMALLINT),
126+
"int4": int(SQLType.SQL_INTEGER),
127+
"int8": int(SQLType.SQL_BIGINT),
128+
"float4": int(SQLType.SQL_REAL),
129+
"float8": int(SQLType.SQL_DOUBLE),
130+
"numeric": int(SQLType.SQL_NUMERIC),
131+
"bool": int(SQLType.SQL_BIT),
132+
"date": int(SQLType.SQL_DATE),
133+
"time": int(SQLType.SQL_TIME),
134+
"timetz": int(SQLType.SQL_TIME_WITH_TIMEZONE),
135+
"timestamp": int(SQLType.SQL_TIMESTAMP),
136+
"timestamptz": int(SQLType.SQL_TIMESTAMP_WITH_TIMEZONE),
137+
"intervaly2m": int(SQLType.SQL_OTHER),
138+
"intervald2s": int(SQLType.SQL_OTHER),
139+
"super": int(SQLType.SQL_LONGVARCHAR),
140+
"geometry": int(SQLType.SQL_LONGVARBINARY),
141+
"geography": int(SQLType.SQL_LONGVARBINARY),
142+
"varbyte": int(SQLType.SQL_LONGVARBINARY)
143+
}
144+
145+
self.__data_type_length = {
146+
"bool": 1,
147+
"bit": 1,
148+
"boolean": 1,
149+
"int2": 5,
150+
"smallint": 5,
151+
"int4": 10,
152+
"integer": 10,
153+
"int": 10,
154+
"int8": 19,
155+
"bigint": 19,
156+
"float4": 8,
157+
"real": 8,
158+
"float8": 17,
159+
"double precision": 17,
160+
"date": 13,
161+
"time": 15,
162+
"timetz": 21,
163+
"timestamp": 29,
164+
"timestamptz": 35,
165+
"intervaly2m": 32,
166+
"intervald2s": 64
167+
}
168+
169+
def get_second_fraction(self, data_type: str = None) -> (str, bool):
170+
date_time_customize_precision: bool = False
171+
precisions: int = 6
172+
if re.match(r"(time|timetz|timestamp|timestamptz).*\(\d+\)", data_type) or re.match(r"interval.*\(\d+\)", data_type):
173+
rs_type = self.get_rs_type(str(re.sub(r"\(\d+\)", "", data_type).rstrip()))
174+
precisions = int(re.search(r"\(\d+\)", data_type).group(0)[1:-1])
175+
date_time_customize_precision = True
176+
else:
177+
rs_type = self.get_rs_type(data_type)
178+
179+
return rs_type, date_time_customize_precision, precisions
180+
181+
def get_rs_type(self, rs_type: str) -> str:
182+
return self.__rs_type_map.get(rs_type, rs_type)
183+
184+
def get_sql_type(self,rs_type: str) -> int:
185+
return self.__sql_type_mapping.get(rs_type, int(SQLType.SQL_OTHER))
186+
187+
188+
def get_column_size(self, rs_type: str, numeric_precision: int, character_maximum_length: int) -> int:
189+
if rs_type == "numeric" or rs_type == "decimal":
190+
return int(numeric_precision)
191+
elif rs_type == "varchar" or rs_type == "character varying" or rs_type == "char" or rs_type == "character" or rs_type == "bpchar":
192+
return int(character_maximum_length)
193+
elif rs_type == "super" or rs_type == "geometry" or rs_type == "geography" or rs_type == "varbyte":
194+
return None
195+
else:
196+
return self.__data_type_length.get(rs_type, 2147483647)
197+
198+
@staticmethod
199+
def get_decimal_digits(rs_type: str, numeric_scale: int, precision: int, customizePrecision: bool) -> int:
200+
if rs_type == "float4" or rs_type == "real":
201+
return 8
202+
elif rs_type == "float8" or rs_type == "double precision":
203+
return 17
204+
elif rs_type == "numeric" or rs_type == "decimal":
205+
return int(numeric_scale)
206+
elif rs_type == "time" or rs_type == "timetz" or rs_type == "timestamp" or rs_type == "timestamptz" or rs_type == "intervald2s":
207+
return precision if customizePrecision else 6
208+
elif rs_type == "super" or rs_type == "geometry" or rs_type == "geography" or rs_type == "varbyte":
209+
return None
210+
else:
211+
return 0
212+
213+
@staticmethod
214+
def get_num_prefix_radix(rs_type: str) -> int:
215+
if rs_type == "geography" or rs_type == "varbyte":
216+
return 2
217+
else:
218+
return 10
219+
220+
@staticmethod
221+
def get_nullable(nullable: str) -> int:
222+
if nullable == "YES":
223+
return 1
224+
elif nullable == "NO":
225+
return 0
226+
else:
227+
return 2
228+
229+
@staticmethod
230+
def get_auto_increment(col_def: str) -> str:
231+
if col_def is not None and ("\"identity\"" in col_def or "default_identity" in col_def):
232+
return "YES"
233+
else:
234+
return "NO"
235+
236+
@staticmethod
237+
def check_name_is_not_pattern(name: str) -> bool:
238+
return name is None or not name or name == "%"
239+
240+
@staticmethod
241+
def check_name_is_exact_name(name: str) -> bool:
242+
if name is not None and len(name) != 0 and ("%" not in name):
243+
return True
244+
return False

0 commit comments

Comments
 (0)