instructure-pysqlsync 0.8.5.dev0__tar.gz → 1.0.0__tar.gz

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 (133) hide show
  1. {instructure_pysqlsync-0.8.5.dev0/instructure_pysqlsync.egg-info → instructure_pysqlsync-1.0.0}/PKG-INFO +8 -18
  2. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/README.md +0 -8
  3. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0/instructure_pysqlsync.egg-info}/PKG-INFO +8 -18
  4. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/instructure_pysqlsync.egg-info/SOURCES.txt +1 -0
  5. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/instructure_pysqlsync.egg-info/requires.txt +5 -10
  6. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pyproject.toml +7 -9
  7. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/__init__.py +2 -2
  8. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/base.py +5 -3
  9. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/connection.py +3 -13
  10. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/data/generator.py +55 -0
  11. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/data_types.py +8 -2
  12. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/inspection.py +1 -2
  13. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/util/dispatch.py +1 -3
  14. instructure_pysqlsync-1.0.0/pysqlsync/util/typing.py +6 -0
  15. instructure_pysqlsync-1.0.0/tests/test_mssql_odbc_types.py +57 -0
  16. instructure_pysqlsync-0.8.5.dev0/pysqlsync/util/typing.py +0 -13
  17. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/LICENSE +0 -0
  18. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/MANIFEST.in +0 -0
  19. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/instructure_pysqlsync.egg-info/dependency_links.txt +0 -0
  20. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/instructure_pysqlsync.egg-info/top_level.txt +0 -0
  21. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/instructure_pysqlsync.egg-info/zip-safe +0 -0
  22. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/data/__init__.py +0 -0
  23. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/data/exchange.py +0 -0
  24. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/README.md +0 -0
  25. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/__init__.py +0 -0
  26. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/__init__.py +0 -0
  27. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/data_types.py +0 -0
  28. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/dependency.py +0 -0
  29. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/engine.py +0 -0
  30. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/generator.py +0 -0
  31. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/delta/object_types.py +0 -0
  32. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/README.md +0 -0
  33. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/__init__.py +0 -0
  34. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/connection.py +0 -0
  35. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/dependency.py +0 -0
  36. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/discovery.py +0 -0
  37. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/engine.py +0 -0
  38. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/generator.py +0 -0
  39. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/mutation.py +0 -0
  40. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mssql/object_types.py +0 -0
  41. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/__init__.py +0 -0
  42. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/connection.py +0 -0
  43. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/data_types.py +0 -0
  44. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/dependency.py +0 -0
  45. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/discovery.py +0 -0
  46. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/engine.py +0 -0
  47. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/generator.py +0 -0
  48. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/mutation.py +0 -0
  49. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/mysql/object_types.py +0 -0
  50. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/README.md +0 -0
  51. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/__init__.py +0 -0
  52. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/connection.py +0 -0
  53. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/data_types.py +0 -0
  54. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/dependency.py +0 -0
  55. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/discovery.py +0 -0
  56. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/engine.py +0 -0
  57. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/generator.py +0 -0
  58. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/mutation.py +0 -0
  59. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/oracle/object_types.py +0 -0
  60. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/__init__.py +0 -0
  61. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/connection.py +0 -0
  62. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/data_types.py +0 -0
  63. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/dependency.py +0 -0
  64. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/discovery.py +0 -0
  65. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/engine.py +0 -0
  66. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/generator.py +0 -0
  67. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/mutation.py +0 -0
  68. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/postgresql/object_types.py +0 -0
  69. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/__init__.py +0 -0
  70. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/connection.py +0 -0
  71. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/data_types.py +0 -0
  72. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/dependency.py +0 -0
  73. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/engine.py +0 -0
  74. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/redshift/generator.py +0 -0
  75. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/__init__.py +0 -0
  76. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/data_types.py +0 -0
  77. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/dependency.py +0 -0
  78. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/engine.py +0 -0
  79. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/generator.py +0 -0
  80. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/snowflake/object_types.py +0 -0
  81. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/test/__init__.py +0 -0
  82. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/test/dependency.py +0 -0
  83. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/__init__.py +0 -0
  84. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/connection.py +0 -0
  85. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/dependency.py +0 -0
  86. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/discovery.py +0 -0
  87. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/engine.py +0 -0
  88. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/dialect/trino/generator.py +0 -0
  89. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/factory.py +0 -0
  90. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/__init__.py +0 -0
  91. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/constraints.py +0 -0
  92. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/data_types.py +0 -0
  93. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/discovery.py +0 -0
  94. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/mutation.py +0 -0
  95. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/object_dict.py +0 -0
  96. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/object_types.py +0 -0
  97. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/py_to_sql.py +0 -0
  98. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/formation/sql_to_py.py +0 -0
  99. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/__init__.py +0 -0
  100. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/data_types.py +0 -0
  101. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/entity_types.py +0 -0
  102. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/id_types.py +0 -0
  103. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/key_types.py +0 -0
  104. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/model/properties.py +0 -0
  105. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/py.typed +0 -0
  106. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/python_types.py +0 -0
  107. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/resultset.py +0 -0
  108. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/util/__init__.py +0 -0
  109. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/pysqlsync/util/dataclasses.py +0 -0
  110. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/setup.cfg +0 -0
  111. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/setup.py +0 -0
  112. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/__init__.py +0 -0
  113. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/example.py +0 -0
  114. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/measure.py +0 -0
  115. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/model/__init__.py +0 -0
  116. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/model/country.py +0 -0
  117. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/model/event.py +0 -0
  118. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/model/school.py +0 -0
  119. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/model/user.py +0 -0
  120. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/params.py +0 -0
  121. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/tables.py +0 -0
  122. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_api.py +0 -0
  123. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_connection.py +0 -0
  124. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_converter.py +0 -0
  125. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_discovery.py +0 -0
  126. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_exchange.py +0 -0
  127. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_generator.py +0 -0
  128. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_performance.py +0 -0
  129. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_synchronize.py +0 -0
  130. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/test_types.py +0 -0
  131. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/timed_test.py +0 -0
  132. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/update_readme.py +0 -0
  133. {instructure_pysqlsync-0.8.5.dev0 → instructure_pysqlsync-1.0.0}/tests/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instructure-pysqlsync
3
- Version: 0.8.5.dev0
3
+ Version: 1.0.0
4
4
  Summary: Synchronize schema and large volumes of data
5
5
  Author-email: "Instructure, Inc." <dapclient@instructure.com>
6
6
  Maintainer-email: "Instructure, Inc." <dapclient@instructure.com>
@@ -8,11 +8,10 @@ License-Expression: MIT
8
8
  Project-URL: Homepage, https://github.com/instructure-internal/pysqlsync
9
9
  Project-URL: Source, https://github.com/instructure-internal/pysqlsync
10
10
  Keywords: synchronization,sql-schema,sql-query,sql-insert,database-clients
11
- Classifier: Development Status :: 4 - Beta
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
@@ -23,19 +22,18 @@ Classifier: Topic :: Database :: Database Engines/Servers
23
22
  Classifier: Topic :: Software Development :: Code Generators
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Classifier: Typing :: Typed
26
- Requires-Python: >=3.9
25
+ Requires-Python: >=3.10
27
26
  Description-Content-Type: text/markdown
28
27
  License-File: LICENSE
29
- Requires-Dist: json_strong_typing>=0.4.2
30
- Requires-Dist: certifi>=2025.11.12; python_version < "3.10"
31
- Requires-Dist: truststore>=0.10; python_version >= "3.10"
28
+ Requires-Dist: instructure-json-strong-typing>=1.0.0
29
+ Requires-Dist: truststore>=0.10
32
30
  Requires-Dist: typing_extensions>=4.15; python_version < "3.12"
33
31
  Provides-Extra: tsv
34
- Requires-Dist: tsv2py>=0.7; extra == "tsv"
32
+ Requires-Dist: instructure-tsv2py>=1.0.0; extra == "tsv"
35
33
  Provides-Extra: postgresql
36
- Requires-Dist: asyncpg>=0.30; extra == "postgresql"
34
+ Requires-Dist: asyncpg>=0.31; extra == "postgresql"
37
35
  Provides-Extra: oracle
38
- Requires-Dist: oracledb>=3.4; extra == "oracle"
36
+ Requires-Dist: oracledb>=4.0; extra == "oracle"
39
37
  Provides-Extra: mysql
40
38
  Requires-Dist: aiomysql>=0.3; extra == "mysql"
41
39
  Requires-Dist: PyMySQL[rsa]>=1.1; extra == "mysql"
@@ -234,11 +232,3 @@ All objects are identified either with a local ID (e.g. namespaces and columns)
234
232
  pysqlsync comes with several database dialects shipped with the library. However, it is possible to create and register new dialects that behave the same way as built-in dialects. In terms of capabilities, there are no differences between built-in and user-defined dialects.
235
233
 
236
234
  If you are about to write integration for a new database dialect, it is recommended that you take one of the existing dialects (e.g. PostgreSQL, Microsoft SQL Server, Oracle or MySQL), and use it as a template. For more information, explore the folder `pysqlsync/dialect`.
237
-
238
- ## Upstream
239
-
240
- This is an internal fork of [hunyadi/pysqlsync](https://github.com/hunyadi/pysqlsync). To track upstream changes, add the original repository as a remote:
241
-
242
- ```sh
243
- git remote add hunyadi git@github.com:hunyadi/pysqlsync.git
244
- ```
@@ -185,11 +185,3 @@ All objects are identified either with a local ID (e.g. namespaces and columns)
185
185
  pysqlsync comes with several database dialects shipped with the library. However, it is possible to create and register new dialects that behave the same way as built-in dialects. In terms of capabilities, there are no differences between built-in and user-defined dialects.
186
186
 
187
187
  If you are about to write integration for a new database dialect, it is recommended that you take one of the existing dialects (e.g. PostgreSQL, Microsoft SQL Server, Oracle or MySQL), and use it as a template. For more information, explore the folder `pysqlsync/dialect`.
188
-
189
- ## Upstream
190
-
191
- This is an internal fork of [hunyadi/pysqlsync](https://github.com/hunyadi/pysqlsync). To track upstream changes, add the original repository as a remote:
192
-
193
- ```sh
194
- git remote add hunyadi git@github.com:hunyadi/pysqlsync.git
195
- ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instructure-pysqlsync
3
- Version: 0.8.5.dev0
3
+ Version: 1.0.0
4
4
  Summary: Synchronize schema and large volumes of data
5
5
  Author-email: "Instructure, Inc." <dapclient@instructure.com>
6
6
  Maintainer-email: "Instructure, Inc." <dapclient@instructure.com>
@@ -8,11 +8,10 @@ License-Expression: MIT
8
8
  Project-URL: Homepage, https://github.com/instructure-internal/pysqlsync
9
9
  Project-URL: Source, https://github.com/instructure-internal/pysqlsync
10
10
  Keywords: synchronization,sql-schema,sql-query,sql-insert,database-clients
11
- Classifier: Development Status :: 4 - Beta
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
@@ -23,19 +22,18 @@ Classifier: Topic :: Database :: Database Engines/Servers
23
22
  Classifier: Topic :: Software Development :: Code Generators
24
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
24
  Classifier: Typing :: Typed
26
- Requires-Python: >=3.9
25
+ Requires-Python: >=3.10
27
26
  Description-Content-Type: text/markdown
28
27
  License-File: LICENSE
29
- Requires-Dist: json_strong_typing>=0.4.2
30
- Requires-Dist: certifi>=2025.11.12; python_version < "3.10"
31
- Requires-Dist: truststore>=0.10; python_version >= "3.10"
28
+ Requires-Dist: instructure-json-strong-typing>=1.0.0
29
+ Requires-Dist: truststore>=0.10
32
30
  Requires-Dist: typing_extensions>=4.15; python_version < "3.12"
33
31
  Provides-Extra: tsv
34
- Requires-Dist: tsv2py>=0.7; extra == "tsv"
32
+ Requires-Dist: instructure-tsv2py>=1.0.0; extra == "tsv"
35
33
  Provides-Extra: postgresql
36
- Requires-Dist: asyncpg>=0.30; extra == "postgresql"
34
+ Requires-Dist: asyncpg>=0.31; extra == "postgresql"
37
35
  Provides-Extra: oracle
38
- Requires-Dist: oracledb>=3.4; extra == "oracle"
36
+ Requires-Dist: oracledb>=4.0; extra == "oracle"
39
37
  Provides-Extra: mysql
40
38
  Requires-Dist: aiomysql>=0.3; extra == "mysql"
41
39
  Requires-Dist: PyMySQL[rsa]>=1.1; extra == "mysql"
@@ -234,11 +232,3 @@ All objects are identified either with a local ID (e.g. namespaces and columns)
234
232
  pysqlsync comes with several database dialects shipped with the library. However, it is possible to create and register new dialects that behave the same way as built-in dialects. In terms of capabilities, there are no differences between built-in and user-defined dialects.
235
233
 
236
234
  If you are about to write integration for a new database dialect, it is recommended that you take one of the existing dialects (e.g. PostgreSQL, Microsoft SQL Server, Oracle or MySQL), and use it as a template. For more information, explore the folder `pysqlsync/dialect`.
237
-
238
- ## Upstream
239
-
240
- This is an internal fork of [hunyadi/pysqlsync](https://github.com/hunyadi/pysqlsync). To track upstream changes, add the original repository as a remote:
241
-
242
- ```sh
243
- git remote add hunyadi git@github.com:hunyadi/pysqlsync.git
244
- ```
@@ -116,6 +116,7 @@ tests/test_converter.py
116
116
  tests/test_discovery.py
117
117
  tests/test_exchange.py
118
118
  tests/test_generator.py
119
+ tests/test_mssql_odbc_types.py
119
120
  tests/test_performance.py
120
121
  tests/test_synchronize.py
121
122
  tests/test_types.py
@@ -1,14 +1,9 @@
1
- json_strong_typing>=0.4.2
2
-
3
- [:python_version < "3.10"]
4
- certifi>=2025.11.12
1
+ instructure-json-strong-typing>=1.0.0
2
+ truststore>=0.10
5
3
 
6
4
  [:python_version < "3.12"]
7
5
  typing_extensions>=4.15
8
6
 
9
- [:python_version >= "3.10"]
10
- truststore>=0.10
11
-
12
7
  [mssql]
13
8
  pyodbc>=5.3
14
9
 
@@ -17,10 +12,10 @@ aiomysql>=0.3
17
12
  PyMySQL[rsa]>=1.1
18
13
 
19
14
  [oracle]
20
- oracledb>=3.4
15
+ oracledb>=4.0
21
16
 
22
17
  [postgresql]
23
- asyncpg>=0.30
18
+ asyncpg>=0.31
24
19
 
25
20
  [snowflake]
26
21
  snowflake-connector-python>=3.18
@@ -29,4 +24,4 @@ snowflake-connector-python>=3.18
29
24
  aiotrino>=0.3
30
25
 
31
26
  [tsv]
32
- tsv2py>=0.7
27
+ instructure-tsv2py>=1.0.0
@@ -14,13 +14,12 @@ authors = [
14
14
  maintainers = [
15
15
  { name = "Instructure, Inc.", email = "dapclient@instructure.com" }
16
16
  ]
17
- requires-python = ">=3.9"
17
+ requires-python = ">=3.10"
18
18
  classifiers = [
19
- "Development Status :: 4 - Beta",
19
+ "Development Status :: 5 - Production/Stable",
20
20
  "Intended Audience :: Developers",
21
21
  "Operating System :: OS Independent",
22
22
  "Programming Language :: Python :: 3",
23
- "Programming Language :: Python :: 3.9",
24
23
  "Programming Language :: Python :: 3.10",
25
24
  "Programming Language :: Python :: 3.11",
26
25
  "Programming Language :: Python :: 3.12",
@@ -33,22 +32,21 @@ classifiers = [
33
32
  "Typing :: Typed"
34
33
  ]
35
34
  dependencies = [
36
- "json_strong_typing >= 0.4.2",
37
- "certifi >= 2025.11.12; python_version<'3.10'",
38
- "truststore >= 0.10; python_version>='3.10'",
35
+ "instructure-json-strong-typing >= 1.0.0",
36
+ "truststore >= 0.10",
39
37
  "typing_extensions >= 4.15; python_version < '3.12'"
40
38
  ]
41
39
  dynamic = ["version"]
42
40
 
43
41
  [project.optional-dependencies]
44
42
  tsv = [
45
- "tsv2py >= 0.7"
43
+ "instructure-tsv2py >= 1.0.0"
46
44
  ]
47
45
  postgresql = [
48
- "asyncpg >= 0.30"
46
+ "asyncpg >= 0.31"
49
47
  ]
50
48
  oracle = [
51
- "oracledb >= 3.4"
49
+ "oracledb >= 4.0"
52
50
  ]
53
51
  mysql = [
54
52
  "aiomysql >= 0.3",
@@ -7,9 +7,9 @@ of Python language elements and database drivers (client libraries).
7
7
  :see: https://github.com/instructure-internal/pysqlsync
8
8
  """
9
9
 
10
- __version__ = "0.8.5.dev0"
10
+ __version__ = "1.0.0"
11
11
  __author__ = "Instructure, Inc."
12
12
  __copyright__ = "Copyright 2023-2025, Levente Hunyadi; 2026 Instructure, Inc."
13
13
  __license__ = "MIT"
14
14
  __maintainer__ = "Instructure, Inc."
15
- __status__ = "Development"
15
+ __status__ = "Production"
@@ -721,7 +721,9 @@ class TransformerDataSource(CompositeDataSource):
721
721
  for transformer in self.transformers:
722
722
  fns.append(await transformer.get(batch))
723
723
  yield [
724
- tuple(((fn(field) if field is not None else None) if fn is not None else field) for fn, field in zip(fns, record))
724
+ tuple(
725
+ ((fn(field) if field is not None else None) if fn is not None else field) for fn, field in zip(fns, record, strict=True)
726
+ )
725
727
  for record in batch
726
728
  ]
727
729
 
@@ -751,7 +753,7 @@ class SelectorTransformerDataSource(CompositeDataSource):
751
753
  yield [
752
754
  tuple(
753
755
  ((fn(field) if field is not None else None) if fn is not None else field)
754
- for fn, field in zip(fns, (record[i] for i in indices))
756
+ for fn, field in zip(fns, (record[i] for i in indices), strict=True)
755
757
  )
756
758
  for record in batch
757
759
  ]
@@ -1091,7 +1093,7 @@ class BaseContext(abc.ABC):
1091
1093
 
1092
1094
  indices: list[int] = []
1093
1095
  transformers: list[RecordTransformer] = []
1094
- for index, field_type, field_name in zip(range(len(field_types)), field_types, field_names):
1096
+ for index, field_type, field_name in zip(range(len(field_types)), field_types, field_names, strict=True):
1095
1097
  if field_type is type(None) or not field_name:
1096
1098
  continue
1097
1099
 
@@ -8,15 +8,11 @@ This module helps create a secure sockets layer (SSL) context.
8
8
 
9
9
  import enum
10
10
  import ssl
11
- import sys
12
11
  from dataclasses import dataclass
13
12
  from typing import Optional
14
13
  from urllib.parse import quote
15
14
 
16
- if sys.version_info >= (3, 10):
17
- import truststore
18
- else:
19
- import certifi
15
+ import truststore
20
16
 
21
17
 
22
18
  @enum.unique
@@ -53,18 +49,12 @@ def create_context(ssl_mode: ConnectionSSLMode) -> Optional[ssl.SSLContext]:
53
49
  ctx.verify_mode = ssl.CERT_NONE
54
50
  return ctx
55
51
  elif ssl_mode is ConnectionSSLMode.verify_ca:
56
- if sys.version_info >= (3, 10):
57
- ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
58
- else:
59
- ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=certifi.where())
52
+ ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
60
53
  ctx.check_hostname = False
61
54
  ctx.verify_mode = ssl.CERT_REQUIRED
62
55
  return ctx
63
56
  elif ssl_mode is ConnectionSSLMode.verify_full:
64
- if sys.version_info >= (3, 10):
65
- ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
66
- else:
67
- ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=certifi.where())
57
+ ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
68
58
  ctx.check_hostname = True
69
59
  ctx.verify_mode = ssl.CERT_REQUIRED
70
60
  return ctx
@@ -154,6 +154,61 @@ def random_alphanumeric_str(min_len: int, max_len: int) -> str:
154
154
  return "".join(random.choices(string.ascii_letters + string.digits, k=random.randint(min_len, max_len)))
155
155
 
156
156
 
157
+ _DIACRITICS = (
158
+ "àáâãäåāăąèéêëēęìíîïīįòóôõöøőōùúûüűūųýÿçćčďðģğķļĺľłñńņňřŕśşšșťţþțźżžæœßı"
159
+ "ÀÁÂÃÄÅĀĂĄÈÉÊËĒĘÌÍÎÏĪĮÒÓÔÕÖØŐŌÙÚÛÜŰŪŲÝŸÇĆČĎÐĢĞĶĻĹĽŁÑŃŅŇŘŔŚŞŠȘŤŢÞȚŹŻŽÆŒẞİ"
160
+ )
161
+
162
+ _EMOJIS = "😂❤️🔥🥰🙏👍🎉😉💀"
163
+
164
+ UTF8_CHARS = string.ascii_letters + string.digits + _DIACRITICS + _EMOJIS
165
+ UTF8_CHARS_MAX_BYTE_LEN = {k: [ch for ch in UTF8_CHARS if len(ch.encode("utf-8")) <= k] for k in range(1, 5)}
166
+
167
+
168
+ def random_utf8_str(min_len: int, max_len: int) -> str:
169
+ """
170
+ Creates a random string of UTF-8 characters.
171
+
172
+ Languages covered:
173
+ * French
174
+ * Spanish
175
+ * Portuguese
176
+ * German
177
+ * Italian
178
+ * Nordic
179
+ * Czech
180
+ * Slovak
181
+ * Polish
182
+ * Hungarian
183
+ * Romanian
184
+ * Latvian
185
+ * Lithuanian
186
+ * Turkish
187
+ * Welsh
188
+ * Icelandic
189
+
190
+ :param min_len: The minimum number of bytes the string should comprise of.
191
+ :param max_len: The maximum number of bytes the string should comprise of.
192
+ """
193
+
194
+ if min_len < 0 or max_len < 0:
195
+ raise ValueError("length bounds must be non-negative")
196
+ if min_len > max_len:
197
+ raise ValueError("min_len must be <= max_len")
198
+
199
+ target_len = random.randint(min_len, max_len)
200
+ current_len = 0
201
+
202
+ chars: list[str] = []
203
+ while current_len < target_len:
204
+ max_char_byte_len = min(target_len - current_len, 4)
205
+ ch = random.choice(UTF8_CHARS_MAX_BYTE_LEN[max_char_byte_len])
206
+ chars.append(ch)
207
+ current_len += len(ch.encode("utf-8"))
208
+
209
+ return "".join(chars)
210
+
211
+
157
212
  P = TypeVar("P")
158
213
  R = TypeVar("R")
159
214
 
@@ -102,9 +102,15 @@ def sql_to_odbc_type(data_type: SqlDataType) -> tuple[int, int, int]:
102
102
  return pyodbc.SQL_TYPE_TIME, data_type.precision or 6, 0
103
103
 
104
104
  elif isinstance(data_type, SqlFixedCharacterType):
105
- return pyodbc.SQL_CHAR, data_type.limit or 0, 0
105
+ # Bind as a wide (Unicode) type so the driver describes the parameter
106
+ # buffer in terms of UTF-16 code units, not bytes. `char`/`varchar`
107
+ # columns under a UTF-8 collation hold multibyte data; binding them as
108
+ # the narrow `SQL_CHAR`/`SQL_VARCHAR` makes `fast_executemany` miscount
109
+ # the buffer width, observed to silently corrupt non-Latin text. SQL
110
+ # Server converts UTF-16 to the column's UTF-8 storage on insert.
111
+ return pyodbc.SQL_WCHAR, data_type.limit or 0, 0
106
112
  elif isinstance(data_type, SqlVariableCharacterType):
107
- return pyodbc.SQL_VARCHAR, data_type.limit or 0, 0
113
+ return pyodbc.SQL_WVARCHAR, data_type.limit or 0, 0
108
114
  elif isinstance(data_type, SqlFixedBinaryType):
109
115
  return pyodbc.SQL_BINARY, data_type.storage or 0, 0
110
116
  elif isinstance(data_type, SqlVariableBinaryType):
@@ -5,7 +5,7 @@ import sys
5
5
  import types
6
6
  import uuid
7
7
  from ipaddress import IPv4Address, IPv6Address
8
- from typing import Any, Optional
8
+ from typing import Any, Optional, TypeGuard
9
9
 
10
10
  from strong_typing.core import JsonType
11
11
  from strong_typing.inspection import (
@@ -23,7 +23,6 @@ from strong_typing.inspection import (
23
23
  )
24
24
 
25
25
  from ..model.properties import get_field_properties, is_primary_key_type
26
- from ..util.typing import TypeGuard
27
26
 
28
27
 
29
28
  def is_simple_type(typ: Any) -> bool:
@@ -2,9 +2,7 @@ import asyncio
2
2
  import functools
3
3
  import inspect
4
4
  from concurrent.futures import ThreadPoolExecutor
5
- from typing import Callable, Coroutine, TypeVar
6
-
7
- from .typing import ParamSpec
5
+ from typing import Callable, Coroutine, ParamSpec, TypeVar
8
6
 
9
7
  R = TypeVar("R")
10
8
  P = ParamSpec("P")
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ if sys.version_info >= (3, 12):
4
+ from typing import override as override # noqa: F401
5
+ else:
6
+ from typing_extensions import override as override # noqa: F401
@@ -0,0 +1,57 @@
1
+ import importlib.util
2
+ import unittest
3
+
4
+ from pysqlsync.model.data_types import (
5
+ SqlFixedCharacterType,
6
+ SqlVariableCharacterType,
7
+ )
8
+
9
+ HAS_PYODBC = importlib.util.find_spec("pyodbc") is not None
10
+
11
+
12
+ @unittest.skipUnless(HAS_PYODBC, "pyodbc (mssql extra) is not installed")
13
+ class TestMSSQLOdbcTypes(unittest.TestCase):
14
+ """
15
+ Pins the ODBC type codes that `setinputsizes` feeds to `fast_executemany`.
16
+
17
+ `char`/`varchar` must bind as the *wide* (Unicode) types so the driver
18
+ sizes the bulk parameter buffer in UTF-16 code units rather than bytes;
19
+ the narrow `SQL_CHAR`/`SQL_VARCHAR` silently corrupt non-Latin text under
20
+ a UTF-8 collation. See `sql_to_odbc_type`.
21
+
22
+ The `pyodbc`-dependent imports are local to each test so the module still
23
+ imports (and skips cleanly) where the `mssql` extra is absent.
24
+ """
25
+
26
+ def test_fixed_character_binds_as_wide(self) -> None:
27
+ import pyodbc
28
+
29
+ from pysqlsync.dialect.mssql.data_types import sql_to_odbc_type
30
+
31
+ odbc_type, size, _ = sql_to_odbc_type(SqlFixedCharacterType(40))
32
+ self.assertEqual(odbc_type, pyodbc.SQL_WCHAR)
33
+ self.assertEqual(size, 40)
34
+
35
+ def test_variable_character_binds_as_wide(self) -> None:
36
+ import pyodbc
37
+
38
+ from pysqlsync.dialect.mssql.data_types import sql_to_odbc_type
39
+
40
+ odbc_type, size, _ = sql_to_odbc_type(SqlVariableCharacterType(40))
41
+ self.assertEqual(odbc_type, pyodbc.SQL_WVARCHAR)
42
+ self.assertEqual(size, 40)
43
+
44
+ def test_unbounded_variable_character_binds_as_wide_size_zero(self) -> None:
45
+ # str -> MSSQLVariableCharacterType() with no limit, i.e. varchar(max);
46
+ # `limit or 0` yields column_size 0, letting the driver size from data.
47
+ import pyodbc
48
+
49
+ from pysqlsync.dialect.mssql.data_types import sql_to_odbc_type
50
+
51
+ odbc_type, size, _ = sql_to_odbc_type(SqlVariableCharacterType())
52
+ self.assertEqual(odbc_type, pyodbc.SQL_WVARCHAR)
53
+ self.assertEqual(size, 0)
54
+
55
+
56
+ if __name__ == "__main__":
57
+ unittest.main()
@@ -1,13 +0,0 @@
1
- import sys
2
-
3
- if sys.version_info >= (3, 10):
4
- from typing import ParamSpec as ParamSpec # noqa: F401
5
- from typing import TypeGuard as TypeGuard # noqa: F401
6
- else:
7
- from typing_extensions import ParamSpec as ParamSpec # noqa: F401
8
- from typing_extensions import TypeGuard as TypeGuard # noqa: F401
9
-
10
- if sys.version_info >= (3, 12):
11
- from typing import override as override # noqa: F401
12
- else:
13
- from typing_extensions import override as override # noqa: F401