MindsDB 25.2.1.2__py3-none-any.whl → 25.2.2.1__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 (33) hide show
  1. {MindsDB-25.2.1.2.dist-info → MindsDB-25.2.2.1.dist-info}/METADATA +234 -230
  2. {MindsDB-25.2.1.2.dist-info → MindsDB-25.2.2.1.dist-info}/RECORD +33 -33
  3. mindsdb/__about__.py +1 -1
  4. mindsdb/api/executor/command_executor.py +1 -57
  5. mindsdb/api/executor/datahub/datanodes/system_tables.py +34 -33
  6. mindsdb/api/executor/planner/query_planner.py +7 -2
  7. mindsdb/api/executor/sql_query/steps/fetch_dataframe.py +19 -11
  8. mindsdb/api/executor/sql_query/steps/subselect_step.py +44 -2
  9. mindsdb/integrations/handlers/byom_handler/byom_handler.py +1 -1
  10. mindsdb/integrations/handlers/byom_handler/requirements.txt +1 -1
  11. mindsdb/integrations/handlers/file_handler/file_handler.py +13 -320
  12. mindsdb/integrations/handlers/file_handler/tests/test_file_handler.py +60 -156
  13. mindsdb/integrations/handlers/huggingface_handler/requirements.txt +1 -1
  14. mindsdb/integrations/handlers/huggingface_handler/requirements_cpu.txt +1 -1
  15. mindsdb/integrations/handlers/lancedb_handler/requirements.txt +1 -1
  16. mindsdb/integrations/handlers/lightwood_handler/requirements.txt +3 -3
  17. mindsdb/integrations/handlers/ms_one_drive_handler/ms_graph_api_one_drive_client.py +3 -3
  18. mindsdb/integrations/handlers/ms_one_drive_handler/ms_one_drive_tables.py +2 -20
  19. mindsdb/integrations/handlers/salesforce_handler/connection_args.py +9 -1
  20. mindsdb/integrations/handlers/salesforce_handler/salesforce_handler.py +2 -1
  21. mindsdb/integrations/handlers/snowflake_handler/snowflake_handler.py +1 -1
  22. mindsdb/integrations/handlers/writer_handler/requirements.txt +1 -1
  23. mindsdb/integrations/utilities/files/file_reader.py +120 -61
  24. mindsdb/integrations/utilities/handlers/api_utilities/microsoft/ms_graph_api_utilities.py +1 -8
  25. mindsdb/integrations/utilities/query_traversal.py +42 -37
  26. mindsdb/interfaces/agents/langfuse_callback_handler.py +205 -27
  27. mindsdb/interfaces/file/file_controller.py +1 -1
  28. mindsdb/interfaces/skills/custom/text2sql/mindsdb_sql_toolkit.py +12 -2
  29. mindsdb/utilities/config.py +2 -2
  30. mindsdb/utilities/render/sqlalchemy_render.py +52 -19
  31. {MindsDB-25.2.1.2.dist-info → MindsDB-25.2.2.1.dist-info}/LICENSE +0 -0
  32. {MindsDB-25.2.1.2.dist-info → MindsDB-25.2.2.1.dist-info}/WHEEL +0 -0
  33. {MindsDB-25.2.1.2.dist-info → MindsDB-25.2.2.1.dist-info}/top_level.txt +0 -0
@@ -54,6 +54,23 @@ def _compile_interval(element, compiler, **kw):
54
54
  return "INTERVAL " + args
55
55
 
56
56
 
57
+ class AttributedStr(str):
58
+ """
59
+ Custom str-like object to pass it to `_requires_quotes` method with `is_quoted` flag
60
+ """
61
+ def __new__(cls, string, is_quoted: bool):
62
+ obj = str.__new__(cls, string)
63
+ obj.is_quoted = is_quoted
64
+ return obj
65
+
66
+
67
+ def get_is_quoted(identifier: ast.Identifier):
68
+ quoted = getattr(identifier, 'is_quoted', [])
69
+ # len can be different
70
+ quoted = quoted + [None] * (len(identifier.parts) - len(quoted))
71
+ return quoted
72
+
73
+
57
74
  class SqlalchemyRender:
58
75
 
59
76
  def __init__(self, dialect_name):
@@ -72,6 +89,29 @@ class SqlalchemyRender:
72
89
  else:
73
90
  dialect = dialect_name
74
91
 
92
+ # override dialect's preparer
93
+ if hasattr(dialect, 'preparer'):
94
+ class Preparer(dialect.preparer):
95
+
96
+ def __init__(self, *args, **kwargs):
97
+ super().__init__(*args, **kwargs)
98
+
99
+ def _requires_quotes(self, value: str) -> bool:
100
+ # check force-quote flag
101
+ if isinstance(value, AttributedStr):
102
+ if value.is_quoted:
103
+ return True
104
+
105
+ lc_value = value.lower()
106
+ return (
107
+ lc_value in self.reserved_words
108
+ or value[0] in self.illegal_initial_characters
109
+ or not self.legal_characters.match(str(value))
110
+ # Override sqlalchemy behavior: don't require to quote mixed- or upper-case
111
+ # or (lc_value != value)
112
+ )
113
+ dialect.preparer = Preparer
114
+
75
115
  # remove double percent signs
76
116
  # https://docs.sqlalchemy.org/en/14/faq/sqlexpressions.html#why-are-percent-signs-being-doubled-up-when-stringifying-sql-statements
77
117
  self.dialect = dialect(paramstyle="named")
@@ -90,26 +130,16 @@ class SqlalchemyRender:
90
130
 
91
131
  parts2 = []
92
132
 
93
- quoted = getattr(identifier, 'is_quoted', [])
94
- # len can be different
95
- quoted = quoted + [None] * (len(identifier.parts) - len(quoted))
96
-
133
+ quoted = get_is_quoted(identifier)
97
134
  for i, is_quoted in zip(identifier.parts, quoted):
98
135
  if isinstance(i, ast.Star):
99
136
  part = '*'
100
- elif is_quoted:
101
- part = self.dialect.identifier_preparer.quote(i)
137
+ elif is_quoted or i.lower() in RESERVED_WORDS:
138
+ # quote anyway
139
+ part = self.dialect.identifier_preparer.quote_identifier(i)
102
140
  else:
103
- part = str(sa.column(i).compile(dialect=self.dialect))
104
-
105
- if not i.islower():
106
- # if lower value is not quoted
107
- # then it is quoted only because of mixed case
108
- # in that case use origin string
109
-
110
- part_lower = str(sa.column(i.lower()).compile(dialect=self.dialect))
111
- if part.lower() != part_lower and i.lower() not in RESERVED_WORDS:
112
- part = i
141
+ # quote if required
142
+ part = self.dialect.identifier_preparer.quote(i)
113
143
 
114
144
  parts2.append(part)
115
145
 
@@ -120,7 +150,9 @@ class SqlalchemyRender:
120
150
  return None
121
151
  if len(alias.parts) > 1:
122
152
  raise NotImplementedError(f'Multiple alias {alias.parts}')
123
- return alias.parts[0]
153
+
154
+ is_quoted = get_is_quoted(alias)[0]
155
+ return AttributedStr(alias.parts[0], is_quoted)
124
156
 
125
157
  def to_expression(self, t):
126
158
 
@@ -435,15 +467,16 @@ class SqlalchemyRender:
435
467
  schema = None
436
468
  if isinstance(table_name, ast.Identifier):
437
469
  parts = table_name.parts
470
+ quoted = get_is_quoted(table_name)
438
471
 
439
472
  if len(parts) > 2:
440
473
  # TODO tests is failing
441
474
  raise NotImplementedError(f'Path to long: {table_name.parts}')
442
475
 
443
476
  if len(parts) == 2:
444
- schema = parts[-2]
477
+ schema = AttributedStr(parts[-2], quoted[-2])
445
478
 
446
- table_name = parts[-1]
479
+ table_name = AttributedStr(parts[-1], quoted[-1])
447
480
 
448
481
  return schema, table_name
449
482