quollio-core 0.4.17__py3-none-any.whl → 0.4.19__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.
quollio_core/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Quollio Core"""
2
2
 
3
- __version__ = "0.4.17"
3
+ __version__ = "0.4.19"
4
4
  __author__ = "Quollio Technologies, Inc"
@@ -19,7 +19,7 @@ def snowflake_table_to_table_lineage(
19
19
  tenant_id: str,
20
20
  ) -> None:
21
21
  with snowflake.SnowflakeQueryExecutor(conn) as sf_executor:
22
- results = sf_executor.get_query_results(
22
+ results, err = sf_executor.get_query_results(
23
23
  query="""
24
24
  SELECT
25
25
  *
@@ -30,6 +30,13 @@ def snowflake_table_to_table_lineage(
30
30
  schema=conn.account_schema,
31
31
  )
32
32
  )
33
+ if err is not None:
34
+ handle_error(err=err)
35
+ if len(results) == 0:
36
+ logger.warning(
37
+ "No lineage data in ACCOUNT_USAGE.SNOWFLAKE. Please check the data in `QUOLLIO_LINEAGE_TABLE_LEVEL`."
38
+ )
39
+ return
33
40
  parsed_results = parse_snowflake_results(results=results)
34
41
  update_table_lineage_inputs = gen_table_lineage_payload(
35
42
  tenant_id=tenant_id,
@@ -62,7 +69,7 @@ def snowflake_column_to_column_lineage(
62
69
  tenant_id: str,
63
70
  ) -> None:
64
71
  with snowflake.SnowflakeQueryExecutor(conn) as sf_executor:
65
- results = sf_executor.get_query_results(
72
+ results, err = sf_executor.get_query_results(
66
73
  query="""
67
74
  SELECT
68
75
  *
@@ -73,6 +80,13 @@ def snowflake_column_to_column_lineage(
73
80
  schema=conn.account_schema,
74
81
  )
75
82
  )
83
+ if err is not None:
84
+ handle_error(err=err)
85
+ if len(results) == 0:
86
+ logger.warning(
87
+ "No lineage data in ACCOUNT_USAGE.SNOWFLAKE. Please check the data in `QUOLLIO_LINEAGE_COLUMN_LEVEL`."
88
+ )
89
+ return
76
90
  update_column_lineage_inputs = gen_column_lineage_payload(
77
91
  tenant_id=tenant_id,
78
92
  endpoint=conn.account_id,
@@ -105,7 +119,7 @@ def snowflake_table_level_sqllineage(
105
119
  tenant_id: str,
106
120
  ) -> None:
107
121
  with snowflake.SnowflakeQueryExecutor(conn) as sf_executor:
108
- results = sf_executor.get_query_results(
122
+ results, err = sf_executor.get_query_results(
109
123
  query="""
110
124
  SELECT
111
125
  database_name
@@ -118,6 +132,13 @@ def snowflake_table_level_sqllineage(
118
132
  schema=conn.account_schema,
119
133
  )
120
134
  )
135
+ if err is not None:
136
+ handle_error(err=err)
137
+ if len(results) == 0:
138
+ logger.warning(
139
+ "No lineage data in ACCOUNT_USAGE.SNOWFLAKE. Please check the data in `QUOLLIO_SQLLINEAGE_SOURCES`."
140
+ )
141
+ return
121
142
  update_table_lineage_inputs_list = list()
122
143
  sql_lineage = SQLLineage()
123
144
  for result in results:
@@ -158,12 +179,20 @@ def snowflake_table_stats(
158
179
  stats_items: List[str],
159
180
  ) -> None:
160
181
  with snowflake.SnowflakeQueryExecutor(conn) as sf_executor:
161
- stats_query = _gen_get_stats_views_query(
182
+ get_stats_view_query = _gen_get_stats_views_query(
162
183
  db=conn.account_database,
163
184
  schema=conn.account_schema,
164
185
  )
165
- stats_views = sf_executor.get_query_results(query=stats_query)
166
-
186
+ stats_views, err = sf_executor.get_query_results(query=get_stats_view_query)
187
+ if err is not None:
188
+ handle_error(err=err)
189
+ if len(stats_views) == 0:
190
+ logger.warning(
191
+ f"No target table for stats aggregation. Please see the error message above \
192
+ and fix it or grant usage permission to both `{conn.account_database}` and `{conn.account_schema}` \
193
+ and select permissions to views begins with `QUOLLIO_STATS_COLUMNS_`."
194
+ )
195
+ return
167
196
  req_count = 0
168
197
  is_aggregate_items = get_is_target_stats_items(stats_items=stats_items)
169
198
  for stats_view in stats_views:
@@ -172,7 +201,15 @@ def snowflake_table_stats(
172
201
  )
173
202
  stats_query = render_sql_for_stats(is_aggregate_items=is_aggregate_items, table_fqn=table_fqn)
174
203
  logger.debug(f"The following sql will be fetched to retrieve stats values. {stats_query}")
175
- stats_result = sf_executor.get_query_results(query=stats_query)
204
+ stats_result, err = sf_executor.get_query_results(query=stats_query)
205
+ if err is not None:
206
+ handle_error(err=err, force_skip=True)
207
+ if len(stats_result) == 0:
208
+ logger.warning(
209
+ f"No stats value. Please query {table_fqn} to check the value exists in it \
210
+ or user has select permission to it."
211
+ )
212
+ continue
176
213
  payloads = gen_table_stats_payload(tenant_id=tenant_id, endpoint=conn.account_id, stats=stats_result)
177
214
  for payload in payloads:
178
215
  logger.info(
@@ -209,3 +246,34 @@ def _gen_get_stats_views_query(db: str, schema: str) -> str:
209
246
  db=db, schema=schema
210
247
  )
211
248
  return query
249
+
250
+
251
+ def handle_error(err: Exception, force_skip: bool = False):
252
+ if err.errno == 2037:
253
+ logger.warning(
254
+ "snowflake get_query_results failed. The table you query exists but user doesn't have permission to select.\
255
+ Please check a user has select or ownership permissions. ErrorNo: {0} SQLState: {1} Message: {2} SfqID: {3}".format(
256
+ err.errno, err.sqlstate, err.msg, err.sfqid
257
+ )
258
+ )
259
+ return
260
+ elif err.errno == 2003:
261
+ logger.warning(
262
+ "snowflake get_query_results failed. User doesn't have select permission to the object \
263
+ or the object you query doesn't exist.\
264
+ Please check a user has select or ownership permissions and whether the object exists or not. \
265
+ ErrorNo: {0} SQLState: {1} Message: {2} SfqID: {3}".format(
266
+ err.errno, err.sqlstate, err.msg, err.sfqid
267
+ )
268
+ )
269
+ return
270
+ else:
271
+ logger.error(
272
+ "snowflake get_query_results failed.\
273
+ Please check ErrNo and message. ErrorNo: {0} SQLState: {1} Message: {2} SfqID: {3}".format(
274
+ err.errno, err.sqlstate, err.msg, err.sfqid
275
+ )
276
+ )
277
+ if not force_skip:
278
+ raise Exception
279
+ return
@@ -25,6 +25,10 @@ class QDCExternalAPIClient:
25
25
  Tried to find a package for oauth0 client credentials flow,
26
26
  but any of them contains bugs or lacks of features to handle the token refresh when it's expired
27
27
  """
28
+ is_domain_valid = is_valid_domain(domain=self.base_url)
29
+ if not is_domain_valid:
30
+ raise ValueError("The format of quollio API URL is invalid. The URL must end with `.com`")
31
+
28
32
  url = f"{self.base_url}/oauth2/token"
29
33
  creds = f"{self.client_id}:{self.client_secret}"
30
34
  encoded_creds = base64.b64encode(creds.encode()).decode()
@@ -104,3 +108,7 @@ class QDCExternalAPIClient:
104
108
 
105
109
  def initialize_qdc_client(api_url: str, client_id: str, client_secret: str) -> QDCExternalAPIClient:
106
110
  return QDCExternalAPIClient(base_url=api_url, client_id=client_id, client_secret=client_secret)
111
+
112
+
113
+ def is_valid_domain(domain: str) -> bool:
114
+ return domain.endswith(".com")
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from dataclasses import asdict, dataclass
3
- from typing import Dict, List
3
+ from typing import Dict, List, Tuple
4
4
 
5
5
  from snowflake.connector import DictCursor, connect, errors
6
6
  from snowflake.connector.connection import SnowflakeConnection
@@ -46,16 +46,13 @@ class SnowflakeQueryExecutor:
46
46
  )
47
47
  return conn
48
48
 
49
- def get_query_results(self, query: str) -> List[Dict[str, str]]:
49
+ def get_query_results(self, query: str) -> Tuple[List[Dict[str, str]], Exception]:
50
50
  with self.conn.cursor(DictCursor) as cur:
51
51
  try:
52
52
  cur.execute(query)
53
53
  result: List[Dict[str, str]] = cur.fetchall()
54
- return result
54
+ return (result, None)
55
55
  except errors.ProgrammingError as e:
56
- logger.error(query)
57
- logger.error(
58
- "snowflake get_query_results failed. {0} ({1}): {2} ({3})".format(
59
- e.errno, e.sqlstate, e.msg, e.sfqid
60
- )
61
- )
56
+ return ([], e)
57
+ except Exception as e:
58
+ return ([], e)
quollio_core/snowflake.py CHANGED
@@ -103,7 +103,7 @@ def load_lineage(
103
103
  else:
104
104
  logger.info("Skip column lineage ingestion. Set enable_column_lineage to True if you ingest column lineage.")
105
105
 
106
- logger.info("Lineage data is successfully loaded.")
106
+ logger.info("Lineage data is successfully finished.")
107
107
 
108
108
  return
109
109
 
@@ -127,7 +127,7 @@ def load_stats(
127
127
  stats_items=stats_items,
128
128
  )
129
129
 
130
- logger.info("Stats data is successfully loaded.")
130
+ logger.info("Stats data is successfully finished.")
131
131
 
132
132
  return
133
133
 
@@ -144,7 +144,7 @@ def load_sqllineage(
144
144
  tenant_id=tenant_id,
145
145
  )
146
146
 
147
- logger.info("sqllineage data is successfully loaded.")
147
+ logger.info("sqllineage data is successfully finished.")
148
148
 
149
149
  return
150
150
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quollio-core
3
- Version: 0.4.17
3
+ Version: 0.4.19
4
4
  Summary: Quollio Core
5
5
  Author-email: quollio-dev <qt.dev@quollio.com>
6
6
  Maintainer-email: RyoAriyama <ryo.arym@gmail.com>, tharuta <35373297+TakumiHaruta@users.noreply.github.com>
@@ -1,8 +1,8 @@
1
- quollio_core/__init__.py,sha256=nStM2GZJp1JH52jZ8xIBUcsz2SAv0sq6Sgsjtpoik5c,84
1
+ quollio_core/__init__.py,sha256=y0SJ_NkmDeB1AzUwy_oG9Ivh80sUSTMebFbwH83kHe8,84
2
2
  quollio_core/bigquery.py,sha256=6Oq4DVGpa3X21Es_nbrsb8pK3vaxwb9Egnvq3huo95k,5894
3
3
  quollio_core/bricks.py,sha256=8h3kbI2b6lGH2s-56jE_Q5-R5-nIsQYMfvtRrkFOzoU,10784
4
4
  quollio_core/redshift.py,sha256=KcdljY95xYf9JYrsaMOBoP_XxQQ8wFVE5ue_XEMVSFc,11504
5
- quollio_core/snowflake.py,sha256=_UFIkr9liNby-5F46PAxupWPOqLhayoggjdLenwdMRU,12210
5
+ quollio_core/snowflake.py,sha256=D-d26OwCUIpXIqfxZnkv4Ei1GJ1mdw9z8YA8K0G-bSE,12216
6
6
  quollio_core/dbt_projects/databricks/.gitignore,sha256=1jJAyXSzJ3YUm0nx3i7wUSE4RjQMX3ad6F8O88UbtzI,29
7
7
  quollio_core/dbt_projects/databricks/README.md,sha256=ZpRQyhFAODAiS8dc1Kb_ndkul4cu4o4udN_EMa49CU4,440
8
8
  quollio_core/dbt_projects/databricks/dbt_project.yml,sha256=3sH98RNk7TnphvI3yEdXDstb92kW5BNxr-cT0tXhwzk,480
@@ -71,17 +71,17 @@ quollio_core/profilers/bigquery.py,sha256=mEr7CFQNgBFqWR8XfCOk8WTm5k5qZhLF8ODVWf
71
71
  quollio_core/profilers/databricks.py,sha256=ik4RiR_GOeU3S7s6C6Y9SGe1D_Y_f98BDWJVlEJXL4U,7868
72
72
  quollio_core/profilers/lineage.py,sha256=4FyxIuPBrUFihqZryqTQBcfB0Z7634lKl_WwkD82vzE,6865
73
73
  quollio_core/profilers/redshift.py,sha256=p6ONDCkhndZAOcKAwEyQ5fsi-jsQrlwHHb7LTI_m1uk,6473
74
- quollio_core/profilers/snowflake.py,sha256=WbfFTeTCyfG3Yi3sVieuLBJ7-ABuWmMl2o0Qv4mHnI8,8317
74
+ quollio_core/profilers/snowflake.py,sha256=m9Ivv2LRwnrmgKS36a039AhrO27sR1EaOOdqNF26PhI,11156
75
75
  quollio_core/profilers/sqllineage.py,sha256=XkF7hwDWIGNtyEP5cv2wETBgMfdQxeHolv7qPIkntSQ,5066
76
76
  quollio_core/profilers/stats.py,sha256=OLQrdrh0y64jo9rmzvGlDdxy_c7gMz_GnlXPJzWkBjM,7343
77
77
  quollio_core/repository/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  quollio_core/repository/bigquery.py,sha256=3AyGcJNYGnUyMweyc6lGm4quwrOzd-ZBS2zNnFwafII,3990
79
79
  quollio_core/repository/databricks.py,sha256=9Cgdv8qBnVaHqu3RA-IUBieAqb69moQ-KAAMVSf5Ds4,1877
80
80
  quollio_core/repository/dbt.py,sha256=cnLwJPywLi8VowVW7zfIBa9jxVwDWO7xzzNRn1vWiuw,659
81
- quollio_core/repository/qdc.py,sha256=0Gz6jauYMzs2bRAD-eO4RSeCIRq8y-JxMWfiefUVULs,4623
81
+ quollio_core/repository/qdc.py,sha256=_5ygUD6h-zs02f4rzj6evxXqD1JjSgtWc-oHPEofaig,4902
82
82
  quollio_core/repository/redshift.py,sha256=p2ouEuYcDCjx1oBhc6H1ekQsvEqHGd3bFu3PW0ngYBc,2880
83
- quollio_core/repository/snowflake.py,sha256=J9rHshfWdOSnjQWxwGEYPpAU2lY7Tu5UFB_BNakkAX0,1892
84
- quollio_core-0.4.17.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
85
- quollio_core-0.4.17.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
86
- quollio_core-0.4.17.dist-info/METADATA,sha256=jZtfiEZjkdmmLyNCuYTiXWTS9RIZzbNOBb_OxXRB3g0,6887
87
- quollio_core-0.4.17.dist-info/RECORD,,
83
+ quollio_core/repository/snowflake.py,sha256=zL9-xi98AIftdW9MuKI-M3pZ1kQuuH-UiZH8HcJvmk4,1769
84
+ quollio_core-0.4.19.dist-info/LICENSE,sha256=V8j_M8nAz8PvAOZQocyRDX7keai8UJ9skgmnwqETmdY,34520
85
+ quollio_core-0.4.19.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
86
+ quollio_core-0.4.19.dist-info/METADATA,sha256=cGPdKKcHhb6b3D9rz46w4FMygML4fUZ-ZQgtfI2DqLY,6887
87
+ quollio_core-0.4.19.dist-info/RECORD,,