SQLAlchemy 2.0.40__py3-none-any.whl → 2.0.41__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.
Files changed (64) hide show
  1. sqlalchemy/__init__.py +1 -12
  2. sqlalchemy/dialects/mssql/base.py +3 -6
  3. sqlalchemy/dialects/mysql/base.py +1 -2
  4. sqlalchemy/dialects/mysql/mysqlconnector.py +9 -1
  5. sqlalchemy/dialects/oracle/__init__.py +10 -0
  6. sqlalchemy/dialects/oracle/base.py +264 -1
  7. sqlalchemy/dialects/oracle/vector.py +266 -0
  8. sqlalchemy/dialects/postgresql/_psycopg_common.py +0 -1
  9. sqlalchemy/dialects/postgresql/base.py +167 -74
  10. sqlalchemy/dialects/postgresql/ext.py +44 -9
  11. sqlalchemy/dialects/postgresql/named_types.py +44 -11
  12. sqlalchemy/dialects/postgresql/pg_catalog.py +19 -7
  13. sqlalchemy/dialects/sqlite/aiosqlite.py +3 -26
  14. sqlalchemy/dialects/sqlite/base.py +187 -119
  15. sqlalchemy/dialects/sqlite/provision.py +0 -2
  16. sqlalchemy/dialects/sqlite/pysqlite.py +3 -69
  17. sqlalchemy/engine/base.py +10 -11
  18. sqlalchemy/engine/default.py +1 -3
  19. sqlalchemy/engine/mock.py +7 -6
  20. sqlalchemy/engine/reflection.py +4 -1
  21. sqlalchemy/engine/result.py +10 -3
  22. sqlalchemy/engine/url.py +1 -1
  23. sqlalchemy/ext/asyncio/scoping.py +1 -2
  24. sqlalchemy/ext/asyncio/session.py +1 -2
  25. sqlalchemy/orm/clsregistry.py +2 -2
  26. sqlalchemy/orm/context.py +1 -3
  27. sqlalchemy/orm/decl_base.py +0 -2
  28. sqlalchemy/orm/dependency.py +1 -1
  29. sqlalchemy/orm/mapper.py +2 -2
  30. sqlalchemy/orm/properties.py +0 -2
  31. sqlalchemy/orm/query.py +5 -6
  32. sqlalchemy/orm/relationships.py +0 -5
  33. sqlalchemy/orm/scoping.py +1 -2
  34. sqlalchemy/orm/session.py +2 -10
  35. sqlalchemy/orm/strategies.py +0 -3
  36. sqlalchemy/orm/strategy_options.py +0 -1
  37. sqlalchemy/schema.py +1 -0
  38. sqlalchemy/sql/_typing.py +5 -0
  39. sqlalchemy/sql/base.py +19 -7
  40. sqlalchemy/sql/cache_key.py +1 -1
  41. sqlalchemy/sql/compiler.py +1 -1
  42. sqlalchemy/sql/crud.py +1 -1
  43. sqlalchemy/sql/ddl.py +2 -1
  44. sqlalchemy/sql/functions.py +57 -50
  45. sqlalchemy/sql/lambdas.py +2 -5
  46. sqlalchemy/sql/schema.py +3 -4
  47. sqlalchemy/sql/selectable.py +15 -5
  48. sqlalchemy/sql/sqltypes.py +21 -8
  49. sqlalchemy/testing/fixtures/mypy.py +25 -5
  50. sqlalchemy/testing/plugin/pytestplugin.py +0 -1
  51. sqlalchemy/testing/requirements.py +46 -0
  52. sqlalchemy/testing/suite/test_dialect.py +2 -2
  53. sqlalchemy/testing/suite/test_reflection.py +5 -3
  54. sqlalchemy/testing/suite/test_select.py +1 -1
  55. sqlalchemy/util/__init__.py +1 -1
  56. sqlalchemy/util/compat.py +2 -0
  57. sqlalchemy/util/langhelpers.py +112 -31
  58. sqlalchemy/util/topological.py +1 -1
  59. sqlalchemy/util/typing.py +8 -7
  60. {sqlalchemy-2.0.40.dist-info → sqlalchemy-2.0.41.dist-info}/METADATA +1 -2
  61. {sqlalchemy-2.0.40.dist-info → sqlalchemy-2.0.41.dist-info}/RECORD +64 -63
  62. {sqlalchemy-2.0.40.dist-info → sqlalchemy-2.0.41.dist-info}/WHEEL +1 -1
  63. {sqlalchemy-2.0.40.dist-info → sqlalchemy-2.0.41.dist-info}/licenses/LICENSE +0 -0
  64. {sqlalchemy-2.0.40.dist-info → sqlalchemy-2.0.41.dist-info}/top_level.txt +0 -0
sqlalchemy/__init__.py CHANGED
@@ -269,7 +269,7 @@ from .types import Uuid as Uuid
269
269
  from .types import VARBINARY as VARBINARY
270
270
  from .types import VARCHAR as VARCHAR
271
271
 
272
- __version__ = "2.0.40"
272
+ __version__ = "2.0.41"
273
273
 
274
274
 
275
275
  def __go(lcls: Any) -> None:
@@ -281,14 +281,3 @@ def __go(lcls: Any) -> None:
281
281
 
282
282
 
283
283
  __go(locals())
284
-
285
-
286
- def __getattr__(name: str) -> Any:
287
- if name == "SingleonThreadPool":
288
- _util.warn_deprecated(
289
- "SingleonThreadPool was a typo in the v2 series. "
290
- "Please use the correct SingletonThreadPool name.",
291
- "2.0.24",
292
- )
293
- return SingletonThreadPool
294
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -3991,10 +3991,8 @@ index_info AS (
3991
3991
  )
3992
3992
 
3993
3993
  # group rows by constraint ID, to handle multi-column FKs
3994
- fkeys = []
3995
-
3996
- def fkey_rec():
3997
- return {
3994
+ fkeys = util.defaultdict(
3995
+ lambda: {
3998
3996
  "name": None,
3999
3997
  "constrained_columns": [],
4000
3998
  "referred_schema": None,
@@ -4002,8 +4000,7 @@ index_info AS (
4002
4000
  "referred_columns": [],
4003
4001
  "options": {},
4004
4002
  }
4005
-
4006
- fkeys = util.defaultdict(fkey_rec)
4003
+ )
4007
4004
 
4008
4005
  for r in connection.execute(s).all():
4009
4006
  (
@@ -1933,7 +1933,7 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
1933
1933
  if (
1934
1934
  self.dialect._support_default_function
1935
1935
  and not re.match(r"^\s*[\'\"\(]", default)
1936
- and "ON UPDATE" not in default
1936
+ and not re.search(r"ON +UPDATE", default, re.I)
1937
1937
  and re.match(r".*\W.*", default)
1938
1938
  ):
1939
1939
  colspec.append(f"DEFAULT ({default})")
@@ -3478,7 +3478,6 @@ class MySQLDialect(default.DefaultDialect):
3478
3478
  full_name = self.identifier_preparer.format_table(table)
3479
3479
  st = "SHOW CREATE TABLE %s" % full_name
3480
3480
 
3481
- rp = None
3482
3481
  try:
3483
3482
  rp = connection.execution_options(
3484
3483
  skip_user_error_events=True
@@ -22,11 +22,19 @@ degree which the driver is functional. There are still ongoing issues
22
22
  with features such as server side cursors which remain disabled until
23
23
  upstream issues are repaired.
24
24
 
25
+ .. warning:: The MySQL Connector/Python driver published by Oracle is subject
26
+ to frequent, major regressions of essential functionality such as being able
27
+ to correctly persist simple binary strings which indicate it is not well
28
+ tested. The SQLAlchemy project is not able to maintain this dialect fully as
29
+ regressions in the driver prevent it from being included in continuous
30
+ integration.
31
+
25
32
  .. versionchanged:: 2.0.39
26
33
 
27
34
  The MySQL Connector/Python dialect has been updated to support the
28
35
  latest version of this DBAPI. Previously, MySQL Connector/Python
29
- was not fully supported.
36
+ was not fully supported. However, support remains limited due to ongoing
37
+ regressions introduced in this driver.
30
38
 
31
39
  Connecting to MariaDB with MySQL Connector/Python
32
40
  --------------------------------------------------
@@ -32,6 +32,11 @@ from .base import ROWID
32
32
  from .base import TIMESTAMP
33
33
  from .base import VARCHAR
34
34
  from .base import VARCHAR2
35
+ from .base import VECTOR
36
+ from .base import VectorIndexConfig
37
+ from .base import VectorIndexType
38
+ from .vector import VectorDistanceType
39
+ from .vector import VectorStorageFormat
35
40
 
36
41
  # Alias oracledb also as oracledb_async
37
42
  oracledb_async = type(
@@ -64,4 +69,9 @@ __all__ = (
64
69
  "NVARCHAR2",
65
70
  "ROWID",
66
71
  "REAL",
72
+ "VECTOR",
73
+ "VectorDistanceType",
74
+ "VectorIndexType",
75
+ "VectorIndexConfig",
76
+ "VectorStorageFormat",
67
77
  )
@@ -722,11 +722,177 @@ The ``oracle_compress`` parameter accepts either an integer specifying the
722
722
  number of prefix columns to compress, or ``True`` to use the default (all
723
723
  columns for non-unique indexes, all but the last column for unique indexes).
724
724
 
725
+ .. _oracle_vector_datatype:
726
+
727
+ VECTOR Datatype
728
+ ---------------
729
+
730
+ Oracle Database 23ai introduced a new VECTOR datatype for artificial intelligence
731
+ and machine learning search operations. The VECTOR datatype is a homogeneous array
732
+ of 8-bit signed integers, 8-bit unsigned integers (binary), 32-bit floating-point numbers,
733
+ or 64-bit floating-point numbers.
734
+
735
+ .. seealso::
736
+
737
+ `Using VECTOR Data
738
+ <https://python-oracledb.readthedocs.io/en/latest/user_guide/vector_data_type.html>`_ - in the documentation
739
+ for the :ref:`oracledb` driver.
740
+
741
+ .. versionadded:: 2.0.41
742
+
743
+ CREATE TABLE support for VECTOR
744
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
745
+
746
+ With the :class:`.VECTOR` datatype, you can specify the dimension for the data
747
+ and the storage format. Valid values for storage format are enum values from
748
+ :class:`.VectorStorageFormat`. To create a table that includes a
749
+ :class:`.VECTOR` column::
750
+
751
+ from sqlalchemy.dialects.oracle import VECTOR, VectorStorageFormat
752
+
753
+ t = Table(
754
+ "t1",
755
+ metadata,
756
+ Column("id", Integer, primary_key=True),
757
+ Column(
758
+ "embedding",
759
+ VECTOR(dim=3, storage_format=VectorStorageFormat.FLOAT32),
760
+ ),
761
+ Column(...),
762
+ ...,
763
+ )
764
+
765
+ Vectors can also be defined with an arbitrary number of dimensions and formats.
766
+ This allows you to specify vectors of different dimensions with the various
767
+ storage formats mentioned above.
768
+
769
+ **Examples**
770
+
771
+ * In this case, the storage format is flexible, allowing any vector type data to be inserted,
772
+ such as INT8 or BINARY etc::
773
+
774
+ vector_col: Mapped[array.array] = mapped_column(VECTOR(dim=3))
775
+
776
+ * The dimension is flexible in this case, meaning that any dimension vector can be used::
777
+
778
+ vector_col: Mapped[array.array] = mapped_column(
779
+ VECTOR(storage_format=VectorStorageType.INT8)
780
+ )
781
+
782
+ * Both the dimensions and the storage format are flexible::
783
+
784
+ vector_col: Mapped[array.array] = mapped_column(VECTOR)
785
+
786
+ Python Datatypes for VECTOR
787
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
788
+
789
+ VECTOR data can be inserted using Python list or Python ``array.array()`` objects.
790
+ Python arrays of type FLOAT (32-bit), DOUBLE (64-bit), or INT (8-bit signed integer)
791
+ are used as bind values when inserting VECTOR columns::
792
+
793
+ from sqlalchemy import insert, select
794
+
795
+ with engine.begin() as conn:
796
+ conn.execute(
797
+ insert(t1),
798
+ {"id": 1, "embedding": [1, 2, 3]},
799
+ )
800
+
801
+ VECTOR Indexes
802
+ ~~~~~~~~~~~~~~
803
+
804
+ The VECTOR feature supports an Oracle-specific parameter ``oracle_vector``
805
+ on the :class:`.Index` construct, which allows the construction of VECTOR
806
+ indexes.
807
+
808
+ To utilize VECTOR indexing, set the ``oracle_vector`` parameter to True to use
809
+ the default values provided by Oracle. HNSW is the default indexing method::
810
+
811
+ from sqlalchemy import Index
812
+
813
+ Index(
814
+ "vector_index",
815
+ t1.c.embedding,
816
+ oracle_vector=True,
817
+ )
818
+
819
+ The full range of parameters for vector indexes are available by using the
820
+ :class:`.VectorIndexConfig` dataclass in place of a boolean; this dataclass
821
+ allows full configuration of the index::
822
+
823
+ Index(
824
+ "hnsw_vector_index",
825
+ t1.c.embedding,
826
+ oracle_vector=VectorIndexConfig(
827
+ index_type=VectorIndexType.HNSW,
828
+ distance=VectorDistanceType.COSINE,
829
+ accuracy=90,
830
+ hnsw_neighbors=5,
831
+ hnsw_efconstruction=20,
832
+ parallel=10,
833
+ ),
834
+ )
835
+
836
+ Index(
837
+ "ivf_vector_index",
838
+ t1.c.embedding,
839
+ oracle_vector=VectorIndexConfig(
840
+ index_type=VectorIndexType.IVF,
841
+ distance=VectorDistanceType.DOT,
842
+ accuracy=90,
843
+ ivf_neighbor_partitions=5,
844
+ ),
845
+ )
846
+
847
+ For complete explanation of these parameters, see the Oracle documentation linked
848
+ below.
849
+
850
+ .. seealso::
851
+
852
+ `CREATE VECTOR INDEX <https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-B396C369-54BB-4098-A0DD-7C54B3A0D66F>`_ - in the Oracle documentation
853
+
854
+
855
+
856
+ Similarity Searching
857
+ ~~~~~~~~~~~~~~~~~~~~
858
+
859
+ When using the :class:`_oracle.VECTOR` datatype with a :class:`.Column` or similar
860
+ ORM mapped construct, additional comparison functions are available, including:
861
+
862
+ * ``l2_distance``
863
+ * ``cosine_distance``
864
+ * ``inner_product``
865
+
866
+ Example Usage::
867
+
868
+ result_vector = connection.scalars(
869
+ select(t1).order_by(t1.embedding.l2_distance([2, 3, 4])).limit(3)
870
+ )
871
+
872
+ for user in vector:
873
+ print(user.id, user.embedding)
874
+
875
+ FETCH APPROXIMATE support
876
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
877
+
878
+ Approximate vector search can only be performed when all syntax and semantic
879
+ rules are satisfied, the corresponding vector index is available, and the
880
+ query optimizer determines to perform it. If any of these conditions are
881
+ unmet, then an approximate search is not performed. In this case the query
882
+ returns exact results.
883
+
884
+ To enable approximate searching during similarity searches on VECTORS, the
885
+ ``oracle_fetch_approximate`` parameter may be used with the :meth:`.Select.fetch`
886
+ clause to add ``FETCH APPROX`` to the SELECT statement::
887
+
888
+ select(users_table).fetch(5, oracle_fetch_approximate=True)
889
+
725
890
  """ # noqa
726
891
 
727
892
  from __future__ import annotations
728
893
 
729
894
  from collections import defaultdict
895
+ from dataclasses import fields
730
896
  from functools import lru_cache
731
897
  from functools import wraps
732
898
  import re
@@ -749,6 +915,9 @@ from .types import RAW
749
915
  from .types import ROWID # noqa
750
916
  from .types import TIMESTAMP
751
917
  from .types import VARCHAR2 # noqa
918
+ from .vector import VECTOR
919
+ from .vector import VectorIndexConfig
920
+ from .vector import VectorIndexType
752
921
  from ... import Computed
753
922
  from ... import exc
754
923
  from ... import schema as sa_schema
@@ -767,6 +936,7 @@ from ...sql import func
767
936
  from ...sql import null
768
937
  from ...sql import or_
769
938
  from ...sql import select
939
+ from ...sql import selectable as sa_selectable
770
940
  from ...sql import sqltypes
771
941
  from ...sql import util as sql_util
772
942
  from ...sql import visitors
@@ -828,6 +998,7 @@ ischema_names = {
828
998
  "BINARY_DOUBLE": BINARY_DOUBLE,
829
999
  "BINARY_FLOAT": BINARY_FLOAT,
830
1000
  "ROWID": ROWID,
1001
+ "VECTOR": VECTOR,
831
1002
  }
832
1003
 
833
1004
 
@@ -985,6 +1156,16 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
985
1156
  def visit_ROWID(self, type_, **kw):
986
1157
  return "ROWID"
987
1158
 
1159
+ def visit_VECTOR(self, type_, **kw):
1160
+ if type_.dim is None and type_.storage_format is None:
1161
+ return "VECTOR(*,*)"
1162
+ elif type_.storage_format is None:
1163
+ return f"VECTOR({type_.dim},*)"
1164
+ elif type_.dim is None:
1165
+ return f"VECTOR(*,{type_.storage_format.value})"
1166
+ else:
1167
+ return f"VECTOR({type_.dim},{type_.storage_format.value})"
1168
+
988
1169
 
989
1170
  class OracleCompiler(compiler.SQLCompiler):
990
1171
  """Oracle compiler modifies the lexical structure of Select
@@ -1223,6 +1404,29 @@ class OracleCompiler(compiler.SQLCompiler):
1223
1404
  else:
1224
1405
  return select._fetch_clause
1225
1406
 
1407
+ def fetch_clause(
1408
+ self,
1409
+ select,
1410
+ fetch_clause=None,
1411
+ require_offset=False,
1412
+ use_literal_execute_for_simple_int=False,
1413
+ **kw,
1414
+ ):
1415
+ text = super().fetch_clause(
1416
+ select,
1417
+ fetch_clause=fetch_clause,
1418
+ require_offset=require_offset,
1419
+ use_literal_execute_for_simple_int=(
1420
+ use_literal_execute_for_simple_int
1421
+ ),
1422
+ **kw,
1423
+ )
1424
+
1425
+ if select.dialect_options["oracle"]["fetch_approximate"]:
1426
+ text = re.sub("FETCH FIRST", "FETCH APPROX FIRST", text)
1427
+
1428
+ return text
1429
+
1226
1430
  def translate_select_structure(self, select_stmt, **kwargs):
1227
1431
  select = select_stmt
1228
1432
 
@@ -1471,6 +1675,48 @@ class OracleCompiler(compiler.SQLCompiler):
1471
1675
 
1472
1676
 
1473
1677
  class OracleDDLCompiler(compiler.DDLCompiler):
1678
+
1679
+ def _build_vector_index_config(
1680
+ self, vector_index_config: VectorIndexConfig
1681
+ ) -> str:
1682
+ parts = []
1683
+ sql_param_name = {
1684
+ "hnsw_neighbors": "neighbors",
1685
+ "hnsw_efconstruction": "efconstruction",
1686
+ "ivf_neighbor_partitions": "neighbor partitions",
1687
+ "ivf_sample_per_partition": "sample_per_partition",
1688
+ "ivf_min_vectors_per_partition": "min_vectors_per_partition",
1689
+ }
1690
+ if vector_index_config.index_type == VectorIndexType.HNSW:
1691
+ parts.append("ORGANIZATION INMEMORY NEIGHBOR GRAPH")
1692
+ elif vector_index_config.index_type == VectorIndexType.IVF:
1693
+ parts.append("ORGANIZATION NEIGHBOR PARTITIONS")
1694
+ if vector_index_config.distance is not None:
1695
+ parts.append(f"DISTANCE {vector_index_config.distance.value}")
1696
+
1697
+ if vector_index_config.accuracy is not None:
1698
+ parts.append(
1699
+ f"WITH TARGET ACCURACY {vector_index_config.accuracy}"
1700
+ )
1701
+
1702
+ parameters_str = [f"type {vector_index_config.index_type.name}"]
1703
+ prefix = vector_index_config.index_type.name.lower() + "_"
1704
+
1705
+ for field in fields(vector_index_config):
1706
+ if field.name.startswith(prefix):
1707
+ key = sql_param_name.get(field.name)
1708
+ value = getattr(vector_index_config, field.name)
1709
+ if value is not None:
1710
+ parameters_str.append(f"{key} {value}")
1711
+
1712
+ parameters_str = ", ".join(parameters_str)
1713
+ parts.append(f"PARAMETERS ({parameters_str})")
1714
+
1715
+ if vector_index_config.parallel is not None:
1716
+ parts.append(f"PARALLEL {vector_index_config.parallel}")
1717
+
1718
+ return " ".join(parts)
1719
+
1474
1720
  def define_constraint_cascades(self, constraint):
1475
1721
  text = ""
1476
1722
  if constraint.ondelete is not None:
@@ -1503,6 +1749,9 @@ class OracleDDLCompiler(compiler.DDLCompiler):
1503
1749
  text += "UNIQUE "
1504
1750
  if index.dialect_options["oracle"]["bitmap"]:
1505
1751
  text += "BITMAP "
1752
+ vector_options = index.dialect_options["oracle"]["vector"]
1753
+ if vector_options:
1754
+ text += "VECTOR "
1506
1755
  text += "INDEX %s ON %s (%s)" % (
1507
1756
  self._prepared_index_name(index, include_schema=True),
1508
1757
  preparer.format_table(index.table, use_schema=True),
@@ -1520,6 +1769,11 @@ class OracleDDLCompiler(compiler.DDLCompiler):
1520
1769
  text += " COMPRESS %d" % (
1521
1770
  index.dialect_options["oracle"]["compress"]
1522
1771
  )
1772
+ if vector_options:
1773
+ if vector_options is True:
1774
+ vector_options = VectorIndexConfig()
1775
+
1776
+ text += " " + self._build_vector_index_config(vector_options)
1523
1777
  return text
1524
1778
 
1525
1779
  def post_create_table(self, table):
@@ -1670,7 +1924,16 @@ class OracleDialect(default.DefaultDialect):
1670
1924
  "tablespace": None,
1671
1925
  },
1672
1926
  ),
1673
- (sa_schema.Index, {"bitmap": False, "compress": False}),
1927
+ (
1928
+ sa_schema.Index,
1929
+ {
1930
+ "bitmap": False,
1931
+ "compress": False,
1932
+ "vector": False,
1933
+ },
1934
+ ),
1935
+ (sa_selectable.Select, {"fetch_approximate": False}),
1936
+ (sa_selectable.CompoundSelect, {"fetch_approximate": False}),
1674
1937
  ]
1675
1938
 
1676
1939
  @util.deprecated_params(