MindsDB 25.3.4.1__py3-none-any.whl → 25.3.4.2__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 MindsDB might be problematic. Click here for more details.

Files changed (22) hide show
  1. mindsdb/__about__.py +1 -1
  2. mindsdb/api/executor/datahub/datanodes/integration_datanode.py +5 -2
  3. mindsdb/api/executor/datahub/datanodes/system_tables.py +131 -138
  4. mindsdb/api/mysql/mysql_proxy/libs/constants/mysql.py +74 -0
  5. mindsdb/integrations/handlers/confluence_handler/confluence_api_client.py +14 -2
  6. mindsdb/integrations/handlers/ms_teams_handler/ms_graph_api_teams_client.py +278 -55
  7. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_handler.py +52 -21
  8. mindsdb/integrations/handlers/ms_teams_handler/ms_teams_tables.py +6 -29
  9. mindsdb/integrations/handlers/mssql_handler/mssql_handler.py +37 -1
  10. mindsdb/integrations/handlers/mysql_handler/mysql_handler.py +28 -1
  11. mindsdb/integrations/handlers/oracle_handler/oracle_handler.py +53 -5
  12. mindsdb/integrations/handlers/postgres_handler/postgres_handler.py +37 -1
  13. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +42 -1
  14. mindsdb/integrations/utilities/handlers/auth_utilities/__init__.py +1 -1
  15. mindsdb/integrations/utilities/handlers/auth_utilities/microsoft/__init__.py +1 -1
  16. mindsdb/integrations/utilities/handlers/auth_utilities/microsoft/ms_graph_api_auth_utilities.py +97 -18
  17. mindsdb/utilities/render/sqlalchemy_render.py +30 -6
  18. {mindsdb-25.3.4.1.dist-info → mindsdb-25.3.4.2.dist-info}/METADATA +226 -228
  19. {mindsdb-25.3.4.1.dist-info → mindsdb-25.3.4.2.dist-info}/RECORD +22 -22
  20. {mindsdb-25.3.4.1.dist-info → mindsdb-25.3.4.2.dist-info}/WHEEL +0 -0
  21. {mindsdb-25.3.4.1.dist-info → mindsdb-25.3.4.2.dist-info}/licenses/LICENSE +0 -0
  22. {mindsdb-25.3.4.1.dist-info → mindsdb-25.3.4.2.dist-info}/top_level.txt +0 -0
mindsdb/__about__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  __title__ = 'MindsDB'
2
2
  __package_name__ = 'mindsdb'
3
- __version__ = '25.3.4.1'
3
+ __version__ = '25.3.4.2'
4
4
  __description__ = "MindsDB's AI SQL Server enables developers to build AI tools that need access to real-time data to perform their tasks"
5
5
  __email__ = "jorge@mindsdb.com"
6
6
  __author__ = 'MindsDB Inc'
@@ -64,6 +64,7 @@ class IntegrationDataNode(DataNode):
64
64
  df = response.data_frame
65
65
  # case independent
66
66
  columns = [str(c).lower() for c in df.columns]
67
+ df.columns = columns
67
68
 
68
69
  col_name = None
69
70
  # looking for specific column names
@@ -78,8 +79,10 @@ class IntegrationDataNode(DataNode):
78
79
  names = df[df.columns[col_name]]
79
80
 
80
81
  # type
81
- if 'type' in columns:
82
- types = df[df.columns[columns.index('type')]]
82
+ if 'mysql_data_type' in columns:
83
+ types = df['mysql_data_type']
84
+ elif 'type' in columns:
85
+ types = df['type']
83
86
  else:
84
87
  types = [None] * len(names)
85
88
 
@@ -1,14 +1,17 @@
1
+ from typing import Optional, Literal
2
+ from dataclasses import dataclass, astuple, fields
1
3
 
2
4
  import pandas as pd
3
5
  from mindsdb_sql_parser.ast.base import ASTNode
4
- from mindsdb.integrations.utilities.sql_utils import extract_comparison_conditions
5
6
 
7
+ from mindsdb.utilities import log
8
+ from mindsdb.utilities.config import config
9
+ from mindsdb.integrations.utilities.sql_utils import extract_comparison_conditions
10
+ from mindsdb.api.mysql.mysql_proxy.libs.constants.mysql import MYSQL_DATA_TYPE
6
11
  from mindsdb.api.executor.datahub.classes.tables_row import (
7
12
  TABLES_ROW_TYPE,
8
13
  TablesRow,
9
14
  )
10
- from mindsdb.utilities import log
11
- from mindsdb.utilities.config import config
12
15
 
13
16
  logger = log.getLogger(__name__)
14
17
 
@@ -162,132 +165,108 @@ class TablesTable(Table):
162
165
  return df
163
166
 
164
167
 
165
- class ColumnsTable(Table):
168
+ @dataclass
169
+ class ColumnsTableRow:
170
+ """Represents a row in the COLUMNS table.
171
+ Fields description: https://dev.mysql.com/doc/refman/8.4/en/information-schema-columns-table.html
172
+ NOTE: attrs order matter, don't change it.
173
+ """
174
+ TABLE_CATALOG: Literal['def'] = 'def'
175
+ TABLE_SCHEMA: Optional[str] = None
176
+ TABLE_NAME: Optional[str] = None
177
+ COLUMN_NAME: Optional[str] = None
178
+ ORDINAL_POSITION: int = 0
179
+ COLUMN_DEFAULT: Optional[str] = None
180
+ IS_NULLABLE: Literal['YES', 'NO'] = 'YES'
181
+ DATA_TYPE: str = MYSQL_DATA_TYPE.VARCHAR.value
182
+ CHARACTER_MAXIMUM_LENGTH: Optional[int] = None
183
+ CHARACTER_OCTET_LENGTH: Optional[int] = None
184
+ NUMERIC_PRECISION: Optional[int] = None
185
+ NUMERIC_SCALE: Optional[int] = None
186
+ DATETIME_PRECISION: Optional[int] = None
187
+ CHARACTER_SET_NAME: Optional[str] = None
188
+ COLLATION_NAME: Optional[str] = None
189
+ COLUMN_TYPE: Optional[str] = None
190
+ COLUMN_KEY: Optional[str] = None
191
+ EXTRA: Optional[str] = None
192
+ PRIVILEGES: str = 'select'
193
+ COLUMN_COMMENT: Optional[str] = None
194
+ GENERATION_EXPRESSION: Optional[str] = None
195
+
196
+ def __post_init__(self):
197
+ # region check mandatory fields
198
+ mandatory_fields = ['TABLE_SCHEMA', 'TABLE_NAME', 'COLUMN_NAME']
199
+ if any(getattr(self, field_name) is None for field_name in mandatory_fields):
200
+ raise ValueError('One of mandatory fields is missed when creating ColumnsTableRow')
201
+ # endregion
202
+
203
+ # region set default values depend on type
204
+ defaults = {
205
+ 'COLUMN_TYPE': self.DATA_TYPE
206
+ }
207
+ if MYSQL_DATA_TYPE(self.DATA_TYPE) in (
208
+ MYSQL_DATA_TYPE.TIMESTAMP,
209
+ MYSQL_DATA_TYPE.DATETIME,
210
+ MYSQL_DATA_TYPE.DATE
211
+ ):
212
+ defaults = {
213
+ 'DATETIME_PRECISION': 0,
214
+ 'COLUMN_TYPE': self.DATA_TYPE
215
+ }
216
+ elif MYSQL_DATA_TYPE(self.DATA_TYPE) in (
217
+ MYSQL_DATA_TYPE.FLOAT,
218
+ MYSQL_DATA_TYPE.DOUBLE,
219
+ MYSQL_DATA_TYPE.DECIMAL
220
+ ):
221
+ defaults = {
222
+ 'NUMERIC_PRECISION': 12,
223
+ 'NUMERIC_SCALE': 0,
224
+ 'COLUMN_TYPE': self.DATA_TYPE
225
+ }
226
+ elif MYSQL_DATA_TYPE(self.DATA_TYPE) in (
227
+ MYSQL_DATA_TYPE.TINYINT,
228
+ MYSQL_DATA_TYPE.SMALLINT,
229
+ MYSQL_DATA_TYPE.MEDIUMINT,
230
+ MYSQL_DATA_TYPE.INT,
231
+ MYSQL_DATA_TYPE.BIGINT
232
+ ):
233
+ defaults = {
234
+ 'NUMERIC_PRECISION': 20,
235
+ 'NUMERIC_SCALE': 0,
236
+ 'COLUMN_TYPE': self.DATA_TYPE
237
+ }
238
+ elif MYSQL_DATA_TYPE(self.DATA_TYPE) is MYSQL_DATA_TYPE.VARCHAR:
239
+ defaults = {
240
+ 'CHARACTER_MAXIMUM_LENGTH': 1024,
241
+ 'CHARACTER_OCTET_LENGTH': 3072,
242
+ 'CHARACTER_SET_NAME': 'utf8',
243
+ 'COLLATION_NAME': 'utf8_bin',
244
+ 'COLUMN_TYPE': 'varchar(1024)'
245
+ }
246
+ else:
247
+ # show as MYSQL_DATA_TYPE.TEXT:
248
+ defaults = {
249
+ 'CHARACTER_MAXIMUM_LENGTH': 65535, # from https://bugs.mysql.com/bug.php?id=90685
250
+ 'CHARACTER_OCTET_LENGTH': 65535, #
251
+ 'CHARACTER_SET_NAME': 'utf8',
252
+ 'COLLATION_NAME': 'utf8_bin',
253
+ 'COLUMN_TYPE': 'text'
254
+ }
255
+
256
+ for key, value in defaults.items():
257
+ setattr(self, key, value)
166
258
 
259
+ self.DATA_TYPE = self.DATA_TYPE.lower()
260
+ self.COLUMN_TYPE = self.COLUMN_TYPE.lower()
261
+ # endregion
262
+
263
+
264
+ class ColumnsTable(Table):
167
265
  name = 'COLUMNS'
168
- columns = [
169
- "TABLE_CATALOG",
170
- "TABLE_SCHEMA",
171
- "TABLE_NAME",
172
- "COLUMN_NAME",
173
- "ORDINAL_POSITION",
174
- "COLUMN_DEFAULT",
175
- "IS_NULLABLE",
176
- "DATA_TYPE",
177
- "CHARACTER_MAXIMUM_LENGTH",
178
- "CHARACTER_OCTET_LENGTH",
179
- "NUMERIC_PRECISION",
180
- "NUMERIC_SCALE",
181
- "DATETIME_PRECISION",
182
- "CHARACTER_SET_NAME",
183
- "COLLATION_NAME",
184
- "COLUMN_TYPE",
185
- "COLUMN_KEY",
186
- "EXTRA",
187
- "PRIVILEGES",
188
- "COLUMN_COMMENT",
189
- "GENERATION_EXPRESSION",
190
- ]
266
+ columns = [field.name for field in fields(ColumnsTableRow)]
191
267
 
192
268
  @classmethod
193
269
  def get_data(cls, inf_schema=None, query: ASTNode = None, **kwargs):
194
-
195
- # NOTE there is a lot of types in mysql, but listed below should be enough for our purposes
196
- row_templates = {
197
- "text": [
198
- "def",
199
- "SCHEMA_NAME",
200
- "TABLE_NAME",
201
- "COLUMN_NAME",
202
- "COL_INDEX",
203
- None,
204
- "YES",
205
- "varchar",
206
- 1024,
207
- 3072,
208
- None,
209
- None,
210
- None,
211
- "utf8",
212
- "utf8_bin",
213
- "varchar(1024)",
214
- None,
215
- None,
216
- "select",
217
- None,
218
- None,
219
- ],
220
- "timestamp": [
221
- "def",
222
- "SCHEMA_NAME",
223
- "TABLE_NAME",
224
- "COLUMN_NAME",
225
- "COL_INDEX",
226
- "CURRENT_TIMESTAMP",
227
- "YES",
228
- "timestamp",
229
- None,
230
- None,
231
- None,
232
- None,
233
- 0,
234
- None,
235
- None,
236
- "timestamp",
237
- None,
238
- None,
239
- "select",
240
- None,
241
- None,
242
- ],
243
- "bigint": [
244
- "def",
245
- "SCHEMA_NAME",
246
- "TABLE_NAME",
247
- "COLUMN_NAME",
248
- "COL_INDEX",
249
- None,
250
- "YES",
251
- "bigint",
252
- None,
253
- None,
254
- 20,
255
- 0,
256
- None,
257
- None,
258
- None,
259
- "bigint unsigned",
260
- None,
261
- None,
262
- "select",
263
- None,
264
- None,
265
- ],
266
- "float": [
267
- "def",
268
- "SCHEMA_NAME",
269
- "TABLE_NAME",
270
- "COLUMN_NAME",
271
- "COL_INDEX",
272
- None,
273
- "YES",
274
- "float",
275
- None,
276
- None,
277
- 12,
278
- 0,
279
- None,
280
- None,
281
- None,
282
- "float",
283
- None,
284
- None,
285
- "select",
286
- None,
287
- None,
288
- ],
289
- }
290
-
291
270
  result = []
292
271
 
293
272
  databases, tables_names = _get_scope(query)
@@ -320,20 +299,34 @@ class ColumnsTable(Table):
320
299
  for i, column in enumerate(table_columns):
321
300
  column_name = column['name']
322
301
  column_type = column.get('type', 'text')
323
- if column_type in ('double precision', 'real', 'numeric'):
324
- column_type = 'float'
325
- elif column_type in ('integer', 'smallint', 'int'):
326
- column_type = 'bigint'
327
- elif column_type in ('timestamp without time zone', 'timestamp with time zone', 'date'):
328
- column_type = 'timestamp'
329
- elif column_type not in row_templates:
330
- column_type = 'text'
331
- result_row = row_templates[column_type].copy()
332
- result_row[1] = db_name
333
- result_row[2] = table_name
334
- result_row[3] = column_name
335
- result_row[4] = i
336
- result.append(result_row)
302
+
303
+ # region infer type
304
+ if isinstance(column_type, MYSQL_DATA_TYPE) is False:
305
+ if column_type in ('double precision', 'real', 'numeric', 'float'):
306
+ column_type = MYSQL_DATA_TYPE.FLOAT
307
+ elif column_type in ('integer', 'smallint', 'int', 'bigint'):
308
+ column_type = MYSQL_DATA_TYPE.BIGINT
309
+ elif column_type in (
310
+ 'timestamp without time zone',
311
+ 'timestamp with time zone',
312
+ 'date', 'timestamp'
313
+ ):
314
+ column_type = MYSQL_DATA_TYPE.DATETIME
315
+ else:
316
+ column_type = MYSQL_DATA_TYPE.VARCHAR
317
+ # endregion
318
+
319
+ column_row = astuple(
320
+ ColumnsTableRow(
321
+ TABLE_SCHEMA=db_name,
322
+ TABLE_NAME=table_name,
323
+ COLUMN_NAME=column_name,
324
+ DATA_TYPE=column_type.value,
325
+ ORDINAL_POSITION=i
326
+ )
327
+ )
328
+
329
+ result.append(column_row)
337
330
 
338
331
  df = pd.DataFrame(result, columns=cls.columns)
339
332
  return df
@@ -8,6 +8,7 @@
8
8
  * permission of MindsDB Inc
9
9
  *******************************************************
10
10
  """
11
+ import enum
11
12
 
12
13
  # CAPABILITIES
13
14
  # As defined in : https://dev.mysql.com/doc/dev/mysql-server/8.0.0/group__group__cs__capabilities__flags.html
@@ -101,6 +102,8 @@ COMMANDS = COMMANDS()
101
102
 
102
103
 
103
104
  # FIELD TYPES
105
+ # https://dev.mysql.com/doc/dev/mysql-server/latest/field__types_8h_source.html
106
+ # https://mariadb.com/kb/en/result-set-packets/
104
107
  class TYPES(object):
105
108
  __slots__ = ()
106
109
  MYSQL_TYPE_DECIMAL = 0
@@ -123,6 +126,11 @@ class TYPES(object):
123
126
  MYSQL_TYPE_TIMESTAMP2 = 17
124
127
  MYSQL_TYPE_DATETIME2 = 18
125
128
  MYSQL_TYPE_TIME2 = 19
129
+ MYSQL_TYPE_TYPED_ARRAY = 20
130
+ MYSQL_TYPE_VECTOR = 242,
131
+ MYSQL_TYPE_INVALID = 243,
132
+ MYSQL_TYPE_BOOL = 244,
133
+ MYSQL_TYPE_JSON = 245,
126
134
  MYSQL_TYPE_NEWDECIMAL = 246
127
135
  MYSQL_TYPE_ENUM = 247
128
136
  MYSQL_TYPE_SET = 248
@@ -135,9 +143,75 @@ class TYPES(object):
135
143
  MYSQL_TYPE_GEOMETRY = 255
136
144
 
137
145
 
146
+ C_TYPES = TYPES()
138
147
  TYPES = TYPES()
139
148
 
140
149
 
150
+ class MYSQL_DATA_TYPE(enum.Enum):
151
+ TINYINT = 'TINYINT'
152
+ SMALLINT = 'SMALLINT'
153
+ MEDIUMINT = 'MEDIUMINT'
154
+ INT = 'INT'
155
+ BIGINT = 'BIGINT'
156
+ FLOAT = 'FLOAT'
157
+ DOUBLE = 'DOUBLE'
158
+ DECIMAL = 'DECIMAL'
159
+ YEAR = 'YEAR'
160
+ TIME = 'TIME'
161
+ DATE = 'DATE'
162
+ DATETIME = 'DATETIME'
163
+ TIMESTAMP = 'TIMESTAMP'
164
+ CHAR = 'CHAR'
165
+ BINARY = 'BINARY'
166
+ VARCHAR = 'VARCHAR'
167
+ VARBINARY = 'VARBINARY'
168
+ TINYBLOB = 'TINYBLOB'
169
+ TINYTEXT = 'TINYTEXT'
170
+ BLOB = 'BLOB'
171
+ TEXT = 'TEXT'
172
+ MEDIUMBLOB = 'MEDIUMBLOB'
173
+ MEDIUMTEXT = 'MEDIUMTEXT'
174
+ LONGBLOB = 'LONGBLOB'
175
+ LONGTEXT = 'LONGTEXT'
176
+ BIT = 'BIT'
177
+ BOOL = 'BOOL'
178
+ BOOLEAN = 'BOOLEAN'
179
+
180
+
181
+ # Map between data types and C types
182
+ # https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-statement-type-codes.html
183
+ DATA_C_TYPE_MAP = {
184
+ MYSQL_DATA_TYPE.TINYINT: C_TYPES.MYSQL_TYPE_TINY,
185
+ MYSQL_DATA_TYPE.SMALLINT: C_TYPES.MYSQL_TYPE_SHORT,
186
+ MYSQL_DATA_TYPE.MEDIUMINT: C_TYPES.MYSQL_TYPE_INT24,
187
+ MYSQL_DATA_TYPE.INT: C_TYPES.MYSQL_TYPE_LONG,
188
+ MYSQL_DATA_TYPE.BIGINT: C_TYPES.MYSQL_TYPE_LONGLONG,
189
+ MYSQL_DATA_TYPE.FLOAT: C_TYPES.MYSQL_TYPE_FLOAT,
190
+ MYSQL_DATA_TYPE.DOUBLE: C_TYPES.MYSQL_TYPE_DOUBLE,
191
+ MYSQL_DATA_TYPE.DECIMAL: C_TYPES.MYSQL_TYPE_NEWDECIMAL,
192
+ MYSQL_DATA_TYPE.YEAR: C_TYPES.MYSQL_TYPE_SHORT,
193
+ MYSQL_DATA_TYPE.TIME: C_TYPES.MYSQL_TYPE_TIME,
194
+ MYSQL_DATA_TYPE.DATE: C_TYPES.MYSQL_TYPE_DATE,
195
+ MYSQL_DATA_TYPE.DATETIME: C_TYPES.MYSQL_TYPE_DATETIME,
196
+ MYSQL_DATA_TYPE.TIMESTAMP: C_TYPES.MYSQL_TYPE_TIMESTAMP,
197
+ MYSQL_DATA_TYPE.CHAR: C_TYPES.MYSQL_TYPE_STRING,
198
+ MYSQL_DATA_TYPE.BINARY: C_TYPES.MYSQL_TYPE_STRING,
199
+ MYSQL_DATA_TYPE.VARCHAR: C_TYPES.MYSQL_TYPE_VAR_STRING,
200
+ MYSQL_DATA_TYPE.VARBINARY: C_TYPES.MYSQL_TYPE_VAR_STRING,
201
+ MYSQL_DATA_TYPE.TINYBLOB: C_TYPES.MYSQL_TYPE_TINY_BLOB,
202
+ MYSQL_DATA_TYPE.TINYTEXT: C_TYPES.MYSQL_TYPE_TINY_BLOB,
203
+ MYSQL_DATA_TYPE.BLOB: C_TYPES.MYSQL_TYPE_BLOB,
204
+ MYSQL_DATA_TYPE.TEXT: C_TYPES.MYSQL_TYPE_BLOB,
205
+ MYSQL_DATA_TYPE.MEDIUMBLOB: C_TYPES.MYSQL_TYPE_MEDIUM_BLOB,
206
+ MYSQL_DATA_TYPE.MEDIUMTEXT: C_TYPES.MYSQL_TYPE_MEDIUM_BLOB,
207
+ MYSQL_DATA_TYPE.LONGBLOB: C_TYPES.MYSQL_TYPE_LONG_BLOB,
208
+ MYSQL_DATA_TYPE.LONGTEXT: C_TYPES.MYSQL_TYPE_LONG_BLOB,
209
+ MYSQL_DATA_TYPE.BIT: C_TYPES.MYSQL_TYPE_BIT,
210
+ MYSQL_DATA_TYPE.BOOL: C_TYPES.MYSQL_TYPE_TINY,
211
+ MYSQL_DATA_TYPE.BOOLEAN: C_TYPES.MYSQL_TYPE_TINY
212
+ }
213
+
214
+
141
215
  class FIELD_FLAG(object):
142
216
  __slots__ = ()
143
217
  NOT_NULL = 1 # field cannot be null
@@ -149,8 +149,20 @@ class ConfluenceAPIClient:
149
149
  results.extend(response["results"])
150
150
 
151
151
  while response["_links"].get("next"):
152
- params["cursor"] = response["_links"].get("next")
153
- response = self._make_request("GET", url, params)
152
+ next_url = response["_links"].get("next")
153
+ next_params = {}
154
+ if params:
155
+ next_params.update(params)
156
+ if "cursor=" in next_url:
157
+ # cursor= is 7 characters long
158
+ cursor_start = next_url.find("cursor=") + 7
159
+ cursor_value = next_url[cursor_start:]
160
+ if "&" in cursor_value:
161
+ cursor_value = cursor_value.split("&")[0]
162
+ next_params["cursor"] = cursor_value
163
+ response = self._make_request("GET", url, next_params)
164
+ else:
165
+ response = self._make_request("GET", next_url)
154
166
  results.extend(response["results"])
155
167
 
156
168
  return results