matrixone-python-sdk 0.1.0__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.
- matrixone/__init__.py +155 -0
- matrixone/account.py +723 -0
- matrixone/async_client.py +3913 -0
- matrixone/async_metadata_manager.py +311 -0
- matrixone/async_orm.py +123 -0
- matrixone/async_vector_index_manager.py +633 -0
- matrixone/base_client.py +208 -0
- matrixone/client.py +4672 -0
- matrixone/config.py +452 -0
- matrixone/connection_hooks.py +286 -0
- matrixone/exceptions.py +89 -0
- matrixone/logger.py +782 -0
- matrixone/metadata.py +820 -0
- matrixone/moctl.py +219 -0
- matrixone/orm.py +2277 -0
- matrixone/pitr.py +646 -0
- matrixone/pubsub.py +771 -0
- matrixone/restore.py +411 -0
- matrixone/search_vector_index.py +1176 -0
- matrixone/snapshot.py +550 -0
- matrixone/sql_builder.py +844 -0
- matrixone/sqlalchemy_ext/__init__.py +161 -0
- matrixone/sqlalchemy_ext/adapters.py +163 -0
- matrixone/sqlalchemy_ext/dialect.py +534 -0
- matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
- matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
- matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
- matrixone/sqlalchemy_ext/ivf_config.py +252 -0
- matrixone/sqlalchemy_ext/table_builder.py +351 -0
- matrixone/sqlalchemy_ext/vector_index.py +1721 -0
- matrixone/sqlalchemy_ext/vector_type.py +948 -0
- matrixone/version.py +580 -0
- matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
- matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
- matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
- matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
- matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +19 -0
- tests/offline/__init__.py +20 -0
- tests/offline/conftest.py +77 -0
- tests/offline/test_account.py +703 -0
- tests/offline/test_async_client_query_comprehensive.py +1218 -0
- tests/offline/test_basic.py +54 -0
- tests/offline/test_case_sensitivity.py +227 -0
- tests/offline/test_connection_hooks_offline.py +287 -0
- tests/offline/test_dialect_schema_handling.py +609 -0
- tests/offline/test_explain_methods.py +346 -0
- tests/offline/test_filter_logical_in.py +237 -0
- tests/offline/test_fulltext_search_comprehensive.py +795 -0
- tests/offline/test_ivf_config.py +249 -0
- tests/offline/test_join_methods.py +281 -0
- tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
- tests/offline/test_logical_in_method.py +237 -0
- tests/offline/test_matrixone_version_parsing.py +264 -0
- tests/offline/test_metadata_offline.py +557 -0
- tests/offline/test_moctl.py +300 -0
- tests/offline/test_moctl_simple.py +251 -0
- tests/offline/test_model_support_offline.py +359 -0
- tests/offline/test_model_support_simple.py +225 -0
- tests/offline/test_pinecone_filter_offline.py +377 -0
- tests/offline/test_pitr.py +585 -0
- tests/offline/test_pubsub.py +712 -0
- tests/offline/test_query_update.py +283 -0
- tests/offline/test_restore.py +445 -0
- tests/offline/test_snapshot_comprehensive.py +384 -0
- tests/offline/test_sql_escaping_edge_cases.py +551 -0
- tests/offline/test_sqlalchemy_integration.py +382 -0
- tests/offline/test_sqlalchemy_vector_integration.py +434 -0
- tests/offline/test_table_builder.py +198 -0
- tests/offline/test_unified_filter.py +398 -0
- tests/offline/test_unified_transaction.py +495 -0
- tests/offline/test_vector_index.py +238 -0
- tests/offline/test_vector_operations.py +688 -0
- tests/offline/test_vector_type.py +174 -0
- tests/offline/test_version_core.py +328 -0
- tests/offline/test_version_management.py +372 -0
- tests/offline/test_version_standalone.py +652 -0
- tests/online/__init__.py +20 -0
- tests/online/conftest.py +216 -0
- tests/online/test_account_management.py +194 -0
- tests/online/test_advanced_features.py +344 -0
- tests/online/test_async_client_interfaces.py +330 -0
- tests/online/test_async_client_online.py +285 -0
- tests/online/test_async_model_insert_online.py +293 -0
- tests/online/test_async_orm_online.py +300 -0
- tests/online/test_async_simple_query_online.py +802 -0
- tests/online/test_async_transaction_simple_query.py +300 -0
- tests/online/test_basic_connection.py +130 -0
- tests/online/test_client_online.py +238 -0
- tests/online/test_config.py +90 -0
- tests/online/test_config_validation.py +123 -0
- tests/online/test_connection_hooks_new_online.py +217 -0
- tests/online/test_dialect_schema_handling_online.py +331 -0
- tests/online/test_filter_logical_in_online.py +374 -0
- tests/online/test_fulltext_comprehensive.py +1773 -0
- tests/online/test_fulltext_label_online.py +433 -0
- tests/online/test_fulltext_search_online.py +842 -0
- tests/online/test_ivf_stats_online.py +506 -0
- tests/online/test_logger_integration.py +311 -0
- tests/online/test_matrixone_query_orm.py +540 -0
- tests/online/test_metadata_online.py +579 -0
- tests/online/test_model_insert_online.py +255 -0
- tests/online/test_mysql_driver_validation.py +213 -0
- tests/online/test_orm_advanced_features.py +2022 -0
- tests/online/test_orm_cte_integration.py +269 -0
- tests/online/test_orm_online.py +270 -0
- tests/online/test_pinecone_filter.py +708 -0
- tests/online/test_pubsub_operations.py +352 -0
- tests/online/test_query_methods.py +225 -0
- tests/online/test_query_update_online.py +433 -0
- tests/online/test_search_vector_index.py +557 -0
- tests/online/test_simple_fulltext_online.py +915 -0
- tests/online/test_snapshot_comprehensive.py +998 -0
- tests/online/test_sqlalchemy_engine_integration.py +336 -0
- tests/online/test_sqlalchemy_integration.py +425 -0
- tests/online/test_transaction_contexts.py +1219 -0
- tests/online/test_transaction_insert_methods.py +356 -0
- tests/online/test_transaction_query_methods.py +288 -0
- tests/online/test_unified_filter_online.py +529 -0
- tests/online/test_vector_comprehensive.py +706 -0
- tests/online/test_version_management.py +291 -0
@@ -0,0 +1,331 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""
|
16
|
+
Online tests for MatrixOne dialect schema handling.
|
17
|
+
Tests the has_table method and type compiler functionality with real database connections.
|
18
|
+
"""
|
19
|
+
|
20
|
+
import pytest
|
21
|
+
import sqlalchemy
|
22
|
+
from sqlalchemy import (
|
23
|
+
create_engine,
|
24
|
+
MetaData,
|
25
|
+
Table,
|
26
|
+
Column,
|
27
|
+
Integer,
|
28
|
+
String,
|
29
|
+
Text,
|
30
|
+
Boolean,
|
31
|
+
Float,
|
32
|
+
Numeric,
|
33
|
+
Date,
|
34
|
+
DateTime,
|
35
|
+
Time,
|
36
|
+
TIMESTAMP,
|
37
|
+
text,
|
38
|
+
)
|
39
|
+
from sqlalchemy.orm import declarative_base
|
40
|
+
from sqlalchemy.exc import SQLAlchemyError
|
41
|
+
|
42
|
+
from matrixone.sqlalchemy_ext.dialect import MatrixOneDialect
|
43
|
+
from .test_config import online_config
|
44
|
+
|
45
|
+
# SQLAlchemy version compatibility
|
46
|
+
SA_VERSION = tuple(map(int, sqlalchemy.__version__.split(".")[:2]))
|
47
|
+
|
48
|
+
|
49
|
+
def execute_sql(connection, sql):
|
50
|
+
"""Execute SQL in a version-compatible way."""
|
51
|
+
# For SQLAlchemy 2.0 compatibility, use explicit transaction management
|
52
|
+
if SA_VERSION >= (2, 0):
|
53
|
+
# SQLAlchemy 2.0+ style
|
54
|
+
return connection.execute(text(sql))
|
55
|
+
else:
|
56
|
+
# SQLAlchemy 1.4.x style - use begin() for explicit transaction
|
57
|
+
if hasattr(connection, 'in_transaction') and not connection.in_transaction():
|
58
|
+
with connection.begin():
|
59
|
+
return connection.execute(text(sql))
|
60
|
+
else:
|
61
|
+
return connection.execute(text(sql))
|
62
|
+
|
63
|
+
|
64
|
+
class TestMatrixOneDialectSchemaHandlingOnline:
|
65
|
+
"""Test MatrixOne dialect schema handling with real database connections."""
|
66
|
+
|
67
|
+
@pytest.fixture(scope="class")
|
68
|
+
def engine(self):
|
69
|
+
"""Create engine for testing."""
|
70
|
+
# Use configurable connection parameters
|
71
|
+
host, port, user, password, database = online_config.get_connection_params()
|
72
|
+
connection_string = f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}"
|
73
|
+
engine = create_engine(connection_string)
|
74
|
+
# Replace the dialect with our MatrixOne dialect but preserve dbapi
|
75
|
+
original_dbapi = engine.dialect.dbapi
|
76
|
+
engine.dialect = MatrixOneDialect()
|
77
|
+
engine.dialect.dbapi = original_dbapi
|
78
|
+
return engine
|
79
|
+
|
80
|
+
@pytest.fixture(scope="class")
|
81
|
+
def metadata(self):
|
82
|
+
"""Create metadata for testing."""
|
83
|
+
return MetaData()
|
84
|
+
|
85
|
+
def test_has_table_with_none_schema_online(self, engine, metadata):
|
86
|
+
"""Test has_table method with schema=None using real database connection."""
|
87
|
+
# Create a test table
|
88
|
+
test_table = Table(
|
89
|
+
'test_schema_table',
|
90
|
+
metadata,
|
91
|
+
Column('id', Integer, primary_key=True),
|
92
|
+
Column('name', String(100)),
|
93
|
+
)
|
94
|
+
|
95
|
+
# Create the table
|
96
|
+
test_table.create(engine, checkfirst=True)
|
97
|
+
|
98
|
+
try:
|
99
|
+
# Test has_table with schema=None (this was the main issue)
|
100
|
+
dialect = engine.dialect
|
101
|
+
with engine.connect() as connection:
|
102
|
+
# This should not raise AssertionError
|
103
|
+
result = dialect.has_table(connection, 'test_schema_table', schema=None)
|
104
|
+
assert result is True
|
105
|
+
|
106
|
+
# Test with non-existent table
|
107
|
+
result = dialect.has_table(connection, 'non_existent_table', schema=None)
|
108
|
+
assert result is False
|
109
|
+
|
110
|
+
finally:
|
111
|
+
# Clean up
|
112
|
+
test_table.drop(engine, checkfirst=True)
|
113
|
+
|
114
|
+
def test_has_table_with_explicit_schema_online(self, engine, metadata):
|
115
|
+
"""Test has_table method with explicit schema using real database connection."""
|
116
|
+
# Create a test table
|
117
|
+
test_table = Table(
|
118
|
+
'test_explicit_schema_table',
|
119
|
+
metadata,
|
120
|
+
Column('id', Integer, primary_key=True),
|
121
|
+
Column('name', String(100)),
|
122
|
+
)
|
123
|
+
|
124
|
+
# Create the table
|
125
|
+
test_table.create(engine, checkfirst=True)
|
126
|
+
|
127
|
+
try:
|
128
|
+
# Get database name from connection
|
129
|
+
with engine.connect() as connection:
|
130
|
+
# Use version-compatible API
|
131
|
+
result = connection.execute(text("SELECT DATABASE()"))
|
132
|
+
current_db = result.scalar()
|
133
|
+
|
134
|
+
# Test has_table with explicit schema
|
135
|
+
dialect = engine.dialect
|
136
|
+
result = dialect.has_table(connection, 'test_explicit_schema_table', schema=current_db)
|
137
|
+
assert result is True
|
138
|
+
|
139
|
+
# Test with non-existent table
|
140
|
+
result = dialect.has_table(connection, 'non_existent_table', schema=current_db)
|
141
|
+
assert result is False
|
142
|
+
|
143
|
+
finally:
|
144
|
+
# Clean up
|
145
|
+
test_table.drop(engine, checkfirst=True)
|
146
|
+
|
147
|
+
def test_type_compiler_with_standard_types_online(self, engine, metadata):
|
148
|
+
"""Test type compiler with standard SQLAlchemy types using real database connection."""
|
149
|
+
# Create a test table with various standard types
|
150
|
+
test_table = Table(
|
151
|
+
'test_type_compiler_table',
|
152
|
+
metadata,
|
153
|
+
Column('id', Integer, primary_key=True),
|
154
|
+
Column('name', String(100)),
|
155
|
+
Column('description', Text),
|
156
|
+
Column('is_active', Boolean),
|
157
|
+
Column('price', Float),
|
158
|
+
Column('quantity', Numeric(10, 2)),
|
159
|
+
Column('created_date', Date),
|
160
|
+
Column('created_at', DateTime),
|
161
|
+
Column('updated_at', Time),
|
162
|
+
Column('timestamp', TIMESTAMP),
|
163
|
+
)
|
164
|
+
|
165
|
+
try:
|
166
|
+
# Create the table - this should not raise UnsupportedCompilationError
|
167
|
+
test_table.create(engine, checkfirst=True)
|
168
|
+
|
169
|
+
# Verify the table was created
|
170
|
+
with engine.connect() as connection:
|
171
|
+
dialect = engine.dialect
|
172
|
+
result = dialect.has_table(connection, 'test_type_compiler_table', schema=None)
|
173
|
+
assert result is True
|
174
|
+
|
175
|
+
finally:
|
176
|
+
# Clean up
|
177
|
+
test_table.drop(engine, checkfirst=True)
|
178
|
+
|
179
|
+
def test_create_all_with_declarative_base_online(self, engine):
|
180
|
+
"""Test create_all with declarative_base (the original failing scenario)."""
|
181
|
+
# Create declarative base
|
182
|
+
Base = declarative_base()
|
183
|
+
|
184
|
+
# Define a test model
|
185
|
+
class TestModel(Base):
|
186
|
+
__tablename__ = 'test_declarative_model'
|
187
|
+
|
188
|
+
id = Column(Integer, primary_key=True)
|
189
|
+
name = Column(String(100))
|
190
|
+
description = Column(Text)
|
191
|
+
|
192
|
+
try:
|
193
|
+
# This should not raise AssertionError or UnsupportedCompilationError
|
194
|
+
Base.metadata.create_all(engine, checkfirst=True)
|
195
|
+
|
196
|
+
# Verify the table was created
|
197
|
+
with engine.connect() as connection:
|
198
|
+
dialect = engine.dialect
|
199
|
+
result = dialect.has_table(connection, 'test_declarative_model', schema=None)
|
200
|
+
assert result is True
|
201
|
+
|
202
|
+
finally:
|
203
|
+
# Clean up
|
204
|
+
Base.metadata.drop_all(engine, checkfirst=True)
|
205
|
+
|
206
|
+
def test_create_all_with_vector_types_online(self, engine):
|
207
|
+
"""Test create_all with vector types (if available)."""
|
208
|
+
try:
|
209
|
+
from matrixone.sqlalchemy_ext import Vectorf32, create_vector_column
|
210
|
+
|
211
|
+
# Create declarative base
|
212
|
+
Base = declarative_base()
|
213
|
+
|
214
|
+
# Define a test model with vector types
|
215
|
+
class VectorTestModel(Base):
|
216
|
+
__tablename__ = 'test_vector_model'
|
217
|
+
|
218
|
+
id = Column(Integer, primary_key=True)
|
219
|
+
name = Column(String(100))
|
220
|
+
embedding = create_vector_column(128, "f32")
|
221
|
+
|
222
|
+
# This should not raise any errors
|
223
|
+
Base.metadata.create_all(engine, checkfirst=True)
|
224
|
+
|
225
|
+
# Verify the table was created
|
226
|
+
with engine.connect() as connection:
|
227
|
+
dialect = engine.dialect
|
228
|
+
result = dialect.has_table(connection, 'test_vector_model', schema=None)
|
229
|
+
assert result is True
|
230
|
+
|
231
|
+
except ImportError:
|
232
|
+
# Skip if vector types are not available
|
233
|
+
pytest.skip("Vector types not available")
|
234
|
+
finally:
|
235
|
+
# Clean up
|
236
|
+
try:
|
237
|
+
Base.metadata.drop_all(engine, checkfirst=True)
|
238
|
+
except:
|
239
|
+
pass
|
240
|
+
|
241
|
+
def test_dialect_connection_handling_online(self, engine):
|
242
|
+
"""Test dialect connection handling with real database."""
|
243
|
+
dialect = engine.dialect
|
244
|
+
|
245
|
+
with engine.connect() as connection:
|
246
|
+
# Test that the dialect can handle the connection
|
247
|
+
assert hasattr(dialect, 'has_table')
|
248
|
+
assert hasattr(dialect, 'get_table_names')
|
249
|
+
assert hasattr(dialect, 'get_columns')
|
250
|
+
|
251
|
+
# Test basic dialect functionality
|
252
|
+
tables = dialect.get_table_names(connection, schema="test")
|
253
|
+
assert isinstance(tables, list)
|
254
|
+
|
255
|
+
# Test that we can get current database name
|
256
|
+
# Use version-compatible API
|
257
|
+
result = execute_sql(connection, "SELECT DATABASE()")
|
258
|
+
current_db = result.scalar()
|
259
|
+
assert current_db is not None
|
260
|
+
|
261
|
+
def test_dialect_error_handling_online(self, engine):
|
262
|
+
"""Test dialect error handling with real database."""
|
263
|
+
dialect = engine.dialect
|
264
|
+
|
265
|
+
with engine.connect() as connection:
|
266
|
+
# Test error code extraction
|
267
|
+
try:
|
268
|
+
# Try to create a table that already exists (should raise an error)
|
269
|
+
# Use version-compatible API
|
270
|
+
execute_sql(connection, "CREATE TABLE test_error_handling (id INT PRIMARY KEY)")
|
271
|
+
execute_sql(connection, "CREATE TABLE test_error_handling (id INT PRIMARY KEY)")
|
272
|
+
# connection.execute(text("CREATE TABLE test_error_handling (id INT PRIMARY KEY)"))
|
273
|
+
except Exception as e:
|
274
|
+
# Test error code extraction
|
275
|
+
error_code = dialect._extract_error_code(e)
|
276
|
+
# The error code should be extracted if it's an integer
|
277
|
+
if error_code is not None:
|
278
|
+
assert isinstance(error_code, int)
|
279
|
+
|
280
|
+
# Clean up
|
281
|
+
try:
|
282
|
+
execute_sql(connection, "DROP TABLE IF EXISTS test_error_handling")
|
283
|
+
except:
|
284
|
+
pass
|
285
|
+
|
286
|
+
def test_dialect_type_compiler_registration_online(self, engine):
|
287
|
+
"""Test that type compiler is properly registered with the dialect."""
|
288
|
+
dialect = engine.dialect
|
289
|
+
|
290
|
+
# Test that the type compiler is properly registered
|
291
|
+
assert hasattr(dialect, 'type_compiler')
|
292
|
+
assert dialect.type_compiler is not None
|
293
|
+
|
294
|
+
# Test that we can create a type compiler instance
|
295
|
+
# In SQLAlchemy 1.4.x, type_compiler is a class, not an instance
|
296
|
+
if hasattr(dialect.type_compiler, '__call__'):
|
297
|
+
type_compiler = dialect.type_compiler(dialect)
|
298
|
+
else:
|
299
|
+
type_compiler = dialect.type_compiler
|
300
|
+
assert type_compiler is not None
|
301
|
+
|
302
|
+
# Test that the type compiler has the required methods
|
303
|
+
assert hasattr(type_compiler, 'visit_integer')
|
304
|
+
assert hasattr(type_compiler, 'visit_string')
|
305
|
+
assert hasattr(type_compiler, 'visit_text')
|
306
|
+
assert hasattr(type_compiler, 'visit_boolean')
|
307
|
+
assert hasattr(type_compiler, 'visit_float')
|
308
|
+
assert hasattr(type_compiler, 'visit_numeric')
|
309
|
+
assert hasattr(type_compiler, 'visit_date')
|
310
|
+
assert hasattr(type_compiler, 'visit_datetime')
|
311
|
+
assert hasattr(type_compiler, 'visit_time')
|
312
|
+
assert hasattr(type_compiler, 'visit_timestamp')
|
313
|
+
|
314
|
+
def test_dialect_statement_compiler_registration_online(self, engine):
|
315
|
+
"""Test that statement compiler is properly registered with the dialect."""
|
316
|
+
dialect = engine.dialect
|
317
|
+
|
318
|
+
# Test that the statement compiler is properly registered
|
319
|
+
assert hasattr(dialect, 'statement_compiler')
|
320
|
+
assert dialect.statement_compiler is not None
|
321
|
+
|
322
|
+
# Test that we can create a statement compiler instance
|
323
|
+
with engine.connect() as connection:
|
324
|
+
# Create a simple statement to test the compiler
|
325
|
+
# Use SQLAlchemy 1.4 compatible API
|
326
|
+
stmt = text("SELECT 1")
|
327
|
+
statement_compiler = dialect.statement_compiler(dialect, stmt)
|
328
|
+
assert statement_compiler is not None
|
329
|
+
|
330
|
+
# Test that the statement compiler has the required methods
|
331
|
+
assert hasattr(statement_compiler, 'visit_user_defined_type')
|