mage-ai 0.9.32__py3-none-any.whl → 0.9.34__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 mage-ai might be problematic. Click here for more details.

Files changed (365) hide show
  1. mage_ai/api/constants.py +37 -2
  2. mage_ai/api/errors.py +9 -5
  3. mage_ai/api/middleware.py +2 -2
  4. mage_ai/api/mixins/__init__.py +0 -0
  5. mage_ai/api/mixins/result_set.py +65 -0
  6. mage_ai/api/operations/base.py +41 -11
  7. mage_ai/api/operations/constants.py +7 -6
  8. mage_ai/api/parsers/BaseParser.py +6 -4
  9. mage_ai/api/parsers/PipelineScheduleParser.py +36 -2
  10. mage_ai/api/policies/BackfillPolicy.py +1 -0
  11. mage_ai/api/policies/BasePolicy.py +229 -40
  12. mage_ai/api/policies/PermissionPolicy.py +17 -0
  13. mage_ai/api/policies/PipelineSchedulePolicy.py +18 -2
  14. mage_ai/api/policies/RolePermissionPolicy.py +2 -1
  15. mage_ai/api/policies/RolePolicy.py +3 -0
  16. mage_ai/api/policies/SessionPolicy.py +36 -1
  17. mage_ai/api/policies/StatusPolicy.py +46 -3
  18. mage_ai/api/policies/UserPolicy.py +25 -2
  19. mage_ai/api/policies/mixins/__init__.py +0 -0
  20. mage_ai/api/policies/mixins/user_permissions.py +241 -0
  21. mage_ai/api/presenters/PermissionPresenter.py +35 -8
  22. mage_ai/api/presenters/RolePresenter.py +29 -12
  23. mage_ai/api/presenters/StatusPresenter.py +11 -0
  24. mage_ai/api/presenters/UserPresenter.py +21 -24
  25. mage_ai/api/resources/BaseResource.py +24 -9
  26. mage_ai/api/resources/BlockResource.py +3 -2
  27. mage_ai/api/resources/DatabaseResource.py +2 -2
  28. mage_ai/api/resources/PermissionResource.py +65 -3
  29. mage_ai/api/resources/Resource.py +8 -1
  30. mage_ai/api/resources/RolePermissionResource.py +23 -0
  31. mage_ai/api/resources/RoleResource.py +75 -4
  32. mage_ai/api/resources/StatusResource.py +34 -0
  33. mage_ai/api/resources/UserResource.py +35 -4
  34. mage_ai/api/result_set.py +8 -1
  35. mage_ai/authentication/permissions/constants.py +78 -0
  36. mage_ai/data_integrations/destinations/constants.py +1 -0
  37. mage_ai/data_integrations/utils/config.py +9 -0
  38. mage_ai/data_integrations/utils/scheduler.py +16 -7
  39. mage_ai/data_preparation/models/block/__init__.py +56 -16
  40. mage_ai/data_preparation/models/block/data_integration/mixins.py +92 -1
  41. mage_ai/data_preparation/models/block/data_integration/utils.py +5 -5
  42. mage_ai/data_preparation/models/block/integration/__init__.py +12 -7
  43. mage_ai/data_preparation/models/block/sql/__init__.py +5 -2
  44. mage_ai/data_preparation/models/block/sql/utils/shared.py +15 -1
  45. mage_ai/data_preparation/models/pipelines/interactions.py +15 -0
  46. mage_ai/data_preparation/shared/secrets.py +7 -17
  47. mage_ai/data_preparation/templates/data_exporters/streaming/google_cloud_pubsub.yaml +5 -0
  48. mage_ai/data_preparation/templates/repo/metadata.yaml +5 -0
  49. mage_ai/io/druid.py +9 -5
  50. mage_ai/io/mssql.py +52 -30
  51. mage_ai/io/postgres.py +1 -0
  52. mage_ai/io/sql.py +15 -1
  53. mage_ai/orchestration/db/models/oauth.py +181 -17
  54. mage_ai/orchestration/db/models/schedules.py +11 -0
  55. mage_ai/presenters/interactions/constants.py +1 -0
  56. mage_ai/presenters/pages/loaders/pipeline_schedules.py +9 -3
  57. mage_ai/server/api/base.py +16 -6
  58. mage_ai/server/api/v1.py +2 -1
  59. mage_ai/server/constants.py +1 -1
  60. mage_ai/server/frontend_dist/404.html +2 -2
  61. mage_ai/server/frontend_dist/_next/static/PBVuphyo_muEAj347ZP_b/_buildManifest.js +1 -0
  62. mage_ai/server/frontend_dist/_next/static/chunks/1124-d8fc76201b83b376.js +1 -0
  63. mage_ai/server/frontend_dist/_next/static/chunks/{1749-9ec0f4709f5a9284.js → 1749-a6bdce4ee8a09bce.js} +1 -1
  64. mage_ai/server/frontend_dist/_next/static/chunks/1769-613e23e361eb5bce.js +1 -0
  65. mage_ai/server/frontend_dist/_next/static/chunks/1821-953efd0da290d25f.js +1 -0
  66. mage_ai/server/frontend_dist/_next/static/chunks/2327-1a797c758f8b064a.js +1 -0
  67. mage_ai/server/frontend_dist/_next/static/chunks/2677-a85c5a72bb695304.js +1 -0
  68. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3419-6c8ec8db8c398c12.js → frontend_dist/_next/static/chunks/3419-0df6c5ef72f2e672.js} +1 -1
  69. mage_ai/server/frontend_dist/_next/static/chunks/3684-e1a713b7c16f0151.js +1 -0
  70. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/3859-3501cdba0a33f9f2.js → frontend_dist/_next/static/chunks/3859-ba594d21a1260cd2.js} +1 -1
  71. mage_ai/server/frontend_dist/_next/static/chunks/4636-84f545d1d238df13.js +1 -0
  72. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/4839-963da65cea58054f.js → frontend_dist/_next/static/chunks/4839-e5fe343a369734bc.js} +1 -1
  73. mage_ai/server/frontend_dist/_next/static/chunks/5457-97433bc45b42a88a.js +1 -0
  74. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/5499-63667ad5a785dba5.js → frontend_dist/_next/static/chunks/5499-b74459f6be5f9229.js} +1 -1
  75. mage_ai/server/frontend_dist/_next/static/chunks/6043-6ea109833b88eb1d.js +1 -0
  76. mage_ai/server/frontend_dist/_next/static/chunks/6333-ca4cd6a73a597a40.js +1 -0
  77. mage_ai/server/frontend_dist/_next/static/chunks/7022-80d082a1d7fd1234.js +1 -0
  78. mage_ai/server/frontend_dist/_next/static/chunks/7361-25f211ef377e5958.js +1 -0
  79. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/8013-1d6b1f7c13264cb4.js → frontend_dist/_next/static/chunks/8013-e67c71ddea072a20.js} +1 -1
  80. mage_ai/server/frontend_dist/_next/static/chunks/8146-941c5155c3bfcc35.js +1 -0
  81. mage_ai/server/frontend_dist/_next/static/chunks/9161-837b653aa849a76f.js +1 -0
  82. mage_ai/server/frontend_dist/_next/static/chunks/9264-5730e4e059db40a8.js +1 -0
  83. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-752d991f239a128f.js +1 -0
  84. mage_ai/server/frontend_dist/_next/static/chunks/pages/{block-layout-a624972d126a3dbd.js → block-layout-a27a28d2a615e364.js} +1 -1
  85. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-98e27a4d7bd0da47.js +1 -0
  86. mage_ai/server/frontend_dist/_next/static/chunks/pages/global-data-products/{[...slug]-a95787fe1695f493.js → [...slug]-9eb5dad57da13efd.js} +1 -1
  87. mage_ai/server/frontend_dist/_next/static/chunks/pages/{global-data-products-0d55711df91a78d0.js → global-data-products-26909dec66f00231.js} +1 -1
  88. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-b9eea6abc676ca81.js +1 -0
  89. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-11c601c6ef07fe86.js +1 -0
  90. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-ae6083077c9c1c41.js +1 -0
  91. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-bfce0ee677d57206.js +1 -0
  92. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-476c921d62f565fc.js +1 -0
  93. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/overview-9bc34ef66d84330a.js → frontend_dist/_next/static/chunks/pages/overview-2ec6b17e45a52be8.js} +1 -1
  94. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-605918f3a5c1aac4.js +1 -0
  95. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-84ec3ab0770bcd68.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ee65a62ed166bd85.js} +1 -1
  96. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-2402004a19a6ad53.js +1 -0
  97. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-9a5b4768a640cd68.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-8781db69f19759a1.js} +1 -1
  98. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-d7fd4857579e2b00.js +1 -0
  99. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-bd4bd009146bab36.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-234007c99efdccf6.js} +1 -1
  100. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-8405a83adeaa8ff5.js → block-runs-da7510d4b277e47b.js} +1 -1
  101. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runtime-98aa0840a00cc661.js → block-runtime-eae853ff34b09481.js} +1 -1
  102. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-0fb48e1cc51f78b2.js → monitors-a057b17847b82468.js} +1 -1
  103. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-845c1f010d5ec380.js → [run]-99d11c86f8b15369.js} +1 -1
  104. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-9255f85760e1f57a.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-3f0980d8810a540b.js} +1 -1
  105. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{settings-187ac359020704e1.js → settings-9edf75d03460aaeb.js} +1 -1
  106. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-c52d0c49439ec620.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-e7692e54979f037d.js} +1 -1
  107. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-7b3b57523b226a0e.js → frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-cb6a3bcaf4fa1a81.js} +1 -1
  108. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-5eff96c149584e87.js +1 -0
  109. mage_ai/server/frontend_dist/_next/static/chunks/pages/{pipelines-efc080913a247e99.js → pipelines-270b912e1ac189b5.js} +1 -1
  110. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-2058d022972cdea4.js +1 -0
  111. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions/[...slug]-1a95628ea8d0d846.js +1 -0
  112. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/permissions-cb1cdf5f8e5bf9c5.js +1 -0
  113. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-58978256db4efbda.js +1 -0
  114. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles/[...slug]-9ddd7eb842d5a911.js +1 -0
  115. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/roles-1694c5eb1acbcf30.js +1 -0
  116. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-6850e854fbedbb61.js +1 -0
  117. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users/[...slug]-5061c073e1c0de07.js +1 -0
  118. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-f551c5665bfd3494.js +1 -0
  119. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-6fe3657070f98aca.js → frontend_dist/_next/static/chunks/pages/sign-in-e779dbab123e626e.js} +1 -1
  120. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-935113d252ada806.js +1 -0
  121. mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/templates-25b6d24cfa81c306.js → frontend_dist/_next/static/chunks/pages/templates-7079d637e396f2a8.js} +1 -1
  122. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-67fdb4a3be93aa14.js +1 -0
  123. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-9c0374c7c783b34a.js +1 -0
  124. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-ea3a0d48e5822b42.js +1 -0
  125. mage_ai/server/frontend_dist/block-layout.html +2 -2
  126. mage_ai/server/frontend_dist/files.html +2 -2
  127. mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
  128. mage_ai/server/frontend_dist/global-data-products.html +2 -2
  129. mage_ai/server/frontend_dist/index.html +2 -2
  130. mage_ai/server/frontend_dist/manage/settings.html +2 -2
  131. mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
  132. mage_ai/server/frontend_dist/manage/users/new.html +2 -2
  133. mage_ai/server/frontend_dist/manage/users.html +2 -2
  134. mage_ai/server/frontend_dist/manage.html +2 -2
  135. mage_ai/server/frontend_dist/overview.html +2 -2
  136. mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
  137. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
  138. mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
  139. mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
  140. mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
  141. mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
  142. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
  143. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
  144. mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
  145. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
  146. mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
  147. mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
  148. mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
  149. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
  150. mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
  151. mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
  152. mage_ai/server/frontend_dist/pipelines.html +2 -2
  153. mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
  154. mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +24 -0
  155. mage_ai/server/frontend_dist/settings/workspace/permissions.html +24 -0
  156. mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
  157. mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +24 -0
  158. mage_ai/server/frontend_dist/settings/workspace/roles.html +24 -0
  159. mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
  160. mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +24 -0
  161. mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
  162. mage_ai/server/frontend_dist/settings.html +2 -2
  163. mage_ai/server/frontend_dist/sign-in.html +5 -5
  164. mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
  165. mage_ai/server/frontend_dist/templates.html +2 -2
  166. mage_ai/server/frontend_dist/terminal.html +2 -2
  167. mage_ai/server/frontend_dist/test.html +2 -2
  168. mage_ai/server/frontend_dist/triggers.html +2 -2
  169. mage_ai/server/frontend_dist/version-control.html +2 -2
  170. mage_ai/server/frontend_dist_base_path_template/404.html +2 -2
  171. mage_ai/server/frontend_dist_base_path_template/_next/static/L-IKw5_bRZUs-wyjnpN_j/_buildManifest.js +1 -0
  172. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1124-d8fc76201b83b376.js +1 -0
  173. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{1749-9ec0f4709f5a9284.js → 1749-a6bdce4ee8a09bce.js} +1 -1
  174. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1769-613e23e361eb5bce.js +1 -0
  175. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1821-953efd0da290d25f.js +1 -0
  176. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2327-1a797c758f8b064a.js +1 -0
  177. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2677-a85c5a72bb695304.js +1 -0
  178. mage_ai/server/{frontend_dist/_next/static/chunks/3419-6c8ec8db8c398c12.js → frontend_dist_base_path_template/_next/static/chunks/3419-0df6c5ef72f2e672.js} +1 -1
  179. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3684-e1a713b7c16f0151.js +1 -0
  180. mage_ai/server/{frontend_dist/_next/static/chunks/3859-3501cdba0a33f9f2.js → frontend_dist_base_path_template/_next/static/chunks/3859-ba594d21a1260cd2.js} +1 -1
  181. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/4636-84f545d1d238df13.js +1 -0
  182. mage_ai/server/{frontend_dist/_next/static/chunks/4839-963da65cea58054f.js → frontend_dist_base_path_template/_next/static/chunks/4839-e5fe343a369734bc.js} +1 -1
  183. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-97433bc45b42a88a.js +1 -0
  184. mage_ai/server/{frontend_dist/_next/static/chunks/5499-63667ad5a785dba5.js → frontend_dist_base_path_template/_next/static/chunks/5499-b74459f6be5f9229.js} +1 -1
  185. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6043-6ea109833b88eb1d.js +1 -0
  186. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-ca4cd6a73a597a40.js +1 -0
  187. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7022-80d082a1d7fd1234.js +1 -0
  188. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-25f211ef377e5958.js +1 -0
  189. mage_ai/server/{frontend_dist/_next/static/chunks/8013-1d6b1f7c13264cb4.js → frontend_dist_base_path_template/_next/static/chunks/8013-e67c71ddea072a20.js} +1 -1
  190. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/8146-941c5155c3bfcc35.js +1 -0
  191. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9161-837b653aa849a76f.js +1 -0
  192. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-5730e4e059db40a8.js +1 -0
  193. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-752d991f239a128f.js +1 -0
  194. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{block-layout-a624972d126a3dbd.js → block-layout-a27a28d2a615e364.js} +1 -1
  195. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-98e27a4d7bd0da47.js +1 -0
  196. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/global-data-products/{[...slug]-a95787fe1695f493.js → [...slug]-9eb5dad57da13efd.js} +1 -1
  197. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{global-data-products-0d55711df91a78d0.js → global-data-products-26909dec66f00231.js} +1 -1
  198. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-b9eea6abc676ca81.js +1 -0
  199. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-11c601c6ef07fe86.js +1 -0
  200. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-ae6083077c9c1c41.js +1 -0
  201. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-bfce0ee677d57206.js +1 -0
  202. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-476c921d62f565fc.js +1 -0
  203. mage_ai/server/{frontend_dist/_next/static/chunks/pages/overview-9bc34ef66d84330a.js → frontend_dist_base_path_template/_next/static/chunks/pages/overview-2ec6b17e45a52be8.js} +1 -1
  204. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-605918f3a5c1aac4.js +1 -0
  205. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-84ec3ab0770bcd68.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ee65a62ed166bd85.js} +1 -1
  206. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-2402004a19a6ad53.js +1 -0
  207. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-9a5b4768a640cd68.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/dashboard-8781db69f19759a1.js} +1 -1
  208. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-d7fd4857579e2b00.js +1 -0
  209. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/logs-bd4bd009146bab36.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/logs-234007c99efdccf6.js} +1 -1
  210. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runs-8405a83adeaa8ff5.js → block-runs-da7510d4b277e47b.js} +1 -1
  211. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/monitors/{block-runtime-98aa0840a00cc661.js → block-runtime-eae853ff34b09481.js} +1 -1
  212. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{monitors-0fb48e1cc51f78b2.js → monitors-a057b17847b82468.js} +1 -1
  213. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs/{[run]-845c1f010d5ec380.js → [run]-99d11c86f8b15369.js} +1 -1
  214. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/runs-9255f85760e1f57a.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/runs-3f0980d8810a540b.js} +1 -1
  215. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{settings-187ac359020704e1.js → settings-9edf75d03460aaeb.js} +1 -1
  216. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/syncs-c52d0c49439ec620.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/syncs-e7692e54979f037d.js} +1 -1
  217. mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-7b3b57523b226a0e.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-cb6a3bcaf4fa1a81.js} +1 -1
  218. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-5eff96c149584e87.js +1 -0
  219. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/{pipelines-efc080913a247e99.js → pipelines-270b912e1ac189b5.js} +1 -1
  220. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-2058d022972cdea4.js +1 -0
  221. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions/[...slug]-1a95628ea8d0d846.js +1 -0
  222. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/permissions-cb1cdf5f8e5bf9c5.js +1 -0
  223. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-58978256db4efbda.js +1 -0
  224. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles/[...slug]-9ddd7eb842d5a911.js +1 -0
  225. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/roles-1694c5eb1acbcf30.js +1 -0
  226. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-6850e854fbedbb61.js +1 -0
  227. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users/[...slug]-5061c073e1c0de07.js +1 -0
  228. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-f551c5665bfd3494.js +1 -0
  229. mage_ai/server/{frontend_dist/_next/static/chunks/pages/sign-in-6fe3657070f98aca.js → frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-e779dbab123e626e.js} +1 -1
  230. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-935113d252ada806.js +1 -0
  231. mage_ai/server/{frontend_dist/_next/static/chunks/pages/templates-25b6d24cfa81c306.js → frontend_dist_base_path_template/_next/static/chunks/pages/templates-7079d637e396f2a8.js} +1 -1
  232. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-67fdb4a3be93aa14.js +1 -0
  233. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9c0374c7c783b34a.js +1 -0
  234. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-ea3a0d48e5822b42.js +1 -0
  235. mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
  236. mage_ai/server/frontend_dist_base_path_template/files.html +5 -5
  237. mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +5 -5
  238. mage_ai/server/frontend_dist_base_path_template/global-data-products.html +5 -5
  239. mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
  240. mage_ai/server/frontend_dist_base_path_template/manage/settings.html +5 -5
  241. mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +5 -5
  242. mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +5 -5
  243. mage_ai/server/frontend_dist_base_path_template/manage/users.html +5 -5
  244. mage_ai/server/frontend_dist_base_path_template/manage.html +5 -5
  245. mage_ai/server/frontend_dist_base_path_template/overview.html +5 -5
  246. mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +5 -5
  247. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +5 -5
  248. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +5 -5
  249. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +5 -5
  250. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
  251. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +5 -5
  252. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +5 -5
  253. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +5 -5
  254. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +5 -5
  255. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +5 -5
  256. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +5 -5
  257. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +5 -5
  258. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +5 -5
  259. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +5 -5
  260. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +5 -5
  261. mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
  262. mage_ai/server/frontend_dist_base_path_template/pipelines.html +5 -5
  263. mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +5 -5
  264. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +24 -0
  265. mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +24 -0
  266. mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +5 -5
  267. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +24 -0
  268. mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +24 -0
  269. mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +5 -5
  270. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +24 -0
  271. mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +5 -5
  272. mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
  273. mage_ai/server/frontend_dist_base_path_template/sign-in.html +13 -13
  274. mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +5 -5
  275. mage_ai/server/frontend_dist_base_path_template/templates.html +5 -5
  276. mage_ai/server/frontend_dist_base_path_template/terminal.html +5 -5
  277. mage_ai/server/frontend_dist_base_path_template/test.html +4 -4
  278. mage_ai/server/frontend_dist_base_path_template/triggers.html +5 -5
  279. mage_ai/server/frontend_dist_base_path_template/version-control.html +5 -5
  280. mage_ai/server/server.py +37 -2
  281. mage_ai/services/aws/__init__.py +19 -8
  282. mage_ai/services/spark/config.py +5 -1
  283. mage_ai/services/spark/spark.py +5 -4
  284. mage_ai/settings/__init__.py +2 -0
  285. mage_ai/streaming/constants.py +1 -0
  286. mage_ai/streaming/sinks/google_cloud_pubsub.py +66 -0
  287. mage_ai/streaming/sinks/sink_factory.py +6 -0
  288. mage_ai/tests/api/operations/test_operations.py +8 -2
  289. mage_ai/tests/api/operations/test_pipeline_schedules.py +3 -0
  290. mage_ai/tests/data_preparation/models/block/sql/utils/test_shared.py +130 -5
  291. mage_ai/tests/factory.py +2 -0
  292. mage_ai/tests/orchestration/test_pipeline_scheduler.py +20 -3
  293. mage_ai/tests/streaming/sinks/test_google_cloud_pubsub.py +28 -0
  294. mage_ai/usage_statistics/logger.py +4 -1
  295. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/METADATA +1 -1
  296. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/RECORD +302 -268
  297. mage_ai/api/policies/utils.py +0 -14
  298. mage_ai/server/frontend_dist/_next/static/cbN7L4Ms6UUPoANozEh9w/_buildManifest.js +0 -1
  299. mage_ai/server/frontend_dist/_next/static/chunks/1769-e2efd4df8d09481f.js +0 -1
  300. mage_ai/server/frontend_dist/_next/static/chunks/2327-2d8a1555605cf4af.js +0 -1
  301. mage_ai/server/frontend_dist/_next/static/chunks/2369-cb9cd97052e18d27.js +0 -1
  302. mage_ai/server/frontend_dist/_next/static/chunks/2677-aea54e655c4a727f.js +0 -1
  303. mage_ai/server/frontend_dist/_next/static/chunks/5457-994e1044953f1425.js +0 -1
  304. mage_ai/server/frontend_dist/_next/static/chunks/5597-c034402ee26af3b4.js +0 -1
  305. mage_ai/server/frontend_dist/_next/static/chunks/6333-d1f8db4e7d9656a5.js +0 -1
  306. mage_ai/server/frontend_dist/_next/static/chunks/6648-9bd31397f1d1b551.js +0 -1
  307. mage_ai/server/frontend_dist/_next/static/chunks/9161-0101571a3635a938.js +0 -1
  308. mage_ai/server/frontend_dist/_next/static/chunks/9264-7c4fcfed1200046a.js +0 -1
  309. mage_ai/server/frontend_dist/_next/static/chunks/9696-3e8ab7786f0e3a0e.js +0 -1
  310. mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-976b488b8aea327f.js +0 -1
  311. mage_ai/server/frontend_dist/_next/static/chunks/pages/files-1a6c6654bfc953ac.js +0 -1
  312. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/settings-49004d918b04c866.js +0 -1
  313. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/[user]-97177740fbb6eb9f.js +0 -1
  314. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users/new-5b6411c9eb8c4ba4.js +0 -1
  315. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage/users-0a7a746cc0998608.js +0 -1
  316. mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-606012a580245001.js +0 -1
  317. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipeline-runs-0801de3c9a2976ea.js +0 -1
  318. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0a5865d4cf012127.js +0 -1
  319. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-87b1a0601c4236cd.js +0 -1
  320. mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers-77ffaa2d721a0a4e.js +0 -1
  321. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/account/profile-4e72d40881ac0605.js +0 -1
  322. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-8da8f8e2fdb1c99f.js +0 -1
  323. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-be029347e99fcea2.js +0 -1
  324. mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/users-d4b07df78b3d6037.js +0 -1
  325. mage_ai/server/frontend_dist/_next/static/chunks/pages/templates/[...slug]-87c492e9edc6064e.js +0 -1
  326. mage_ai/server/frontend_dist/_next/static/chunks/pages/terminal-2ab9f3d0c50cce81.js +0 -1
  327. mage_ai/server/frontend_dist/_next/static/chunks/pages/triggers-0ba40243a030ddf4.js +0 -1
  328. mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-28a3a43d65074394.js +0 -1
  329. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1769-e2efd4df8d09481f.js +0 -1
  330. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2327-2d8a1555605cf4af.js +0 -1
  331. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2369-cb9cd97052e18d27.js +0 -1
  332. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2677-aea54e655c4a727f.js +0 -1
  333. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5457-994e1044953f1425.js +0 -1
  334. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5597-c034402ee26af3b4.js +0 -1
  335. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6333-d1f8db4e7d9656a5.js +0 -1
  336. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/6648-9bd31397f1d1b551.js +0 -1
  337. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9161-0101571a3635a938.js +0 -1
  338. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9264-7c4fcfed1200046a.js +0 -1
  339. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9696-3e8ab7786f0e3a0e.js +0 -1
  340. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-976b488b8aea327f.js +0 -1
  341. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/files-1a6c6654bfc953ac.js +0 -1
  342. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/settings-49004d918b04c866.js +0 -1
  343. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/[user]-97177740fbb6eb9f.js +0 -1
  344. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users/new-5b6411c9eb8c4ba4.js +0 -1
  345. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage/users-0a7a746cc0998608.js +0 -1
  346. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-606012a580245001.js +0 -1
  347. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-0801de3c9a2976ea.js +0 -1
  348. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-0a5865d4cf012127.js +0 -1
  349. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-87b1a0601c4236cd.js +0 -1
  350. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers-77ffaa2d721a0a4e.js +0 -1
  351. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/account/profile-4e72d40881ac0605.js +0 -1
  352. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-8da8f8e2fdb1c99f.js +0 -1
  353. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-be029347e99fcea2.js +0 -1
  354. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/users-d4b07df78b3d6037.js +0 -1
  355. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/templates/[...slug]-87c492e9edc6064e.js +0 -1
  356. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/terminal-2ab9f3d0c50cce81.js +0 -1
  357. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/triggers-0ba40243a030ddf4.js +0 -1
  358. mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-28a3a43d65074394.js +0 -1
  359. mage_ai/server/frontend_dist_base_path_template/_next/static/o_Yc57jh8Te2SI-VoipGQ/_buildManifest.js +0 -1
  360. /mage_ai/server/frontend_dist/_next/static/{cbN7L4Ms6UUPoANozEh9w → PBVuphyo_muEAj347ZP_b}/_ssgManifest.js +0 -0
  361. /mage_ai/server/frontend_dist_base_path_template/_next/static/{o_Yc57jh8Te2SI-VoipGQ → L-IKw5_bRZUs-wyjnpN_j}/_ssgManifest.js +0 -0
  362. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/LICENSE +0 -0
  363. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/WHEEL +0 -0
  364. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/entry_points.txt +0 -0
  365. {mage_ai-0.9.32.dist-info → mage_ai-0.9.34.dist-info}/top_level.txt +0 -0
@@ -551,6 +551,7 @@ class Block(DataIntegrationMixin):
551
551
  from mage_ai.data_preparation.models.block.sql.utils.shared import (
552
552
  extract_create_statement_table_name,
553
553
  extract_insert_statement_table_names,
554
+ extract_update_statement_table_names,
554
555
  )
555
556
 
556
557
  if not self.content:
@@ -561,6 +562,9 @@ class Block(DataIntegrationMixin):
561
562
  return table_name
562
563
 
563
564
  matches = extract_insert_statement_table_names(self.content)
565
+ if len(matches) == 0:
566
+ matches = extract_update_statement_table_names(self.content)
567
+
564
568
  if len(matches) == 0:
565
569
  return None
566
570
 
@@ -1036,7 +1040,7 @@ class Block(DataIntegrationMixin):
1036
1040
  variable_mapping,
1037
1041
  execution_partition=execution_partition,
1038
1042
  override_outputs=True,
1039
- spark=(global_vars or dict()).get('spark'),
1043
+ spark=self.__get_spark_session_from_global_vars(global_vars=global_vars),
1040
1044
  dynamic_block_uuid=dynamic_block_uuid,
1041
1045
  )
1042
1046
  except ValueError as e:
@@ -1612,6 +1616,34 @@ class Block(DataIntegrationMixin):
1612
1616
  variable_uuid=variable_uuid,
1613
1617
  )
1614
1618
 
1619
+ def get_raw_outputs(
1620
+ self,
1621
+ block_uuid: str,
1622
+ execution_partition: str = None,
1623
+ from_notebook: bool = False,
1624
+ global_vars: Dict = None,
1625
+ ) -> List[Any]:
1626
+ all_variables = self.get_variables_by_block(
1627
+ block_uuid=block_uuid,
1628
+ partition=execution_partition,
1629
+ )
1630
+
1631
+ outputs = []
1632
+
1633
+ for variable_uuid in all_variables:
1634
+ variable = self.pipeline.get_block_variable(
1635
+ block_uuid,
1636
+ variable_uuid,
1637
+ from_notebook=from_notebook,
1638
+ global_vars=global_vars,
1639
+ partition=execution_partition,
1640
+ raise_exception=True,
1641
+ spark=self.__get_spark_session_from_global_vars(global_vars),
1642
+ )
1643
+ outputs.append(variable)
1644
+
1645
+ return outputs
1646
+
1615
1647
  def get_outputs(
1616
1648
  self,
1617
1649
  execution_partition: str = None,
@@ -2237,21 +2269,12 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2237
2269
  if not test_functions or len(test_functions) == 0:
2238
2270
  return
2239
2271
 
2240
- variable_manager = self.pipeline.variable_manager
2241
- outputs = [
2242
- variable_manager.get_variable(
2243
- self.pipeline.uuid,
2244
- self.uuid,
2245
- variable,
2246
- partition=execution_partition,
2247
- spark=(global_vars or dict()).get('spark'),
2248
- )
2249
- for variable in self.output_variables(
2250
- execution_partition=execution_partition,
2251
- from_notebook=from_notebook,
2252
- global_vars=global_vars,
2253
- )
2254
- ]
2272
+ outputs = self.get_raw_outputs(
2273
+ dynamic_block_uuid or self.uuid,
2274
+ execution_partition=execution_partition,
2275
+ from_notebook=from_notebook,
2276
+ global_vars=global_vars,
2277
+ )
2255
2278
 
2256
2279
  if logger and 'logger' not in global_vars:
2257
2280
  global_vars['logger'] = logger
@@ -2468,6 +2491,23 @@ df = get_variable('{self.pipeline.uuid}', '{block_uuid}', 'df')
2468
2491
  self.spark_init = True
2469
2492
  return self.spark
2470
2493
 
2494
+ def __get_spark_session_from_global_vars(self, global_vars: Dict = None):
2495
+ if global_vars is None:
2496
+ global_vars = dict()
2497
+ spark = global_vars.get('spark')
2498
+ if self.pipeline and self.pipeline.spark_config:
2499
+ spark_config = self.pipeline.spark_config
2500
+ else:
2501
+ repo_config = RepoConfig(repo_path=self.repo_path)
2502
+ spark_config = repo_config.spark_config
2503
+ if not spark_config:
2504
+ return spark
2505
+ spark_config = SparkConfig.load(config=spark_config)
2506
+ if spark_config.use_custom_session:
2507
+ return global_vars.get('context', dict()).get(
2508
+ spark_config.custom_session_var_name, spark)
2509
+ return spark
2510
+
2471
2511
  def __store_variables_prepare(
2472
2512
  self,
2473
2513
  variable_mapping: Dict,
@@ -53,6 +53,12 @@ class DataIntegrationMixin:
53
53
 
54
54
  @property
55
55
  def inputs_only_uuids(self) -> List[str]:
56
+ """
57
+ Get a list of UUIDs of input streams that are marked as 'input_only'.
58
+
59
+ Returns:
60
+ List[str]: List of UUIDs.
61
+ """
56
62
  arr = []
57
63
 
58
64
  for stream_id, settings in self.data_integration_inputs.items():
@@ -63,6 +69,12 @@ class DataIntegrationMixin:
63
69
 
64
70
  @property
65
71
  def data_integration_inputs(self) -> Dict:
72
+ """
73
+ Get the data integration inputs configuration for this block.
74
+
75
+ Returns:
76
+ Dict: Data integration inputs configuration.
77
+ """
66
78
  mapping = {}
67
79
 
68
80
  if self.configuration_data_integration:
@@ -77,6 +89,12 @@ class DataIntegrationMixin:
77
89
 
78
90
  @property
79
91
  def uuids_for_inputs(self) -> List[str]:
92
+ """
93
+ Get a list of UUIDs associated with data integration inputs.
94
+
95
+ Returns:
96
+ List[str]: List of UUIDs.
97
+ """
80
98
  arr = []
81
99
 
82
100
  for stream_id, settings in self.data_integration_inputs.items():
@@ -87,12 +105,27 @@ class DataIntegrationMixin:
87
105
 
88
106
  @property
89
107
  def upstream_block_uuids_for_inputs(self) -> List[str]:
108
+ """
109
+ Get a list of upstream block UUIDs associated with data integration inputs.
110
+
111
+ Returns:
112
+ List[str]: List of upstream block UUIDs.
113
+ """
90
114
  if self.configuration_data_integration:
91
115
  inputs_combined = self.uuids_for_inputs + self.inputs_only_uuids
92
116
 
93
117
  return [up_uuid for up_uuid in self.upstream_block_uuids if up_uuid in inputs_combined]
94
118
 
95
119
  def get_block_data_integration_settings_directory_path(self, block_uuid: str = None) -> str:
120
+ """
121
+ Get the directory path for storing data integration settings associated with a block.
122
+
123
+ Args:
124
+ block_uuid (str, optional): UUID of the block. Defaults to None.
125
+
126
+ Returns:
127
+ str: Directory path for data integration settings.
128
+ """
96
129
  if not self.pipeline:
97
130
  return
98
131
 
@@ -104,12 +137,27 @@ class DataIntegrationMixin:
104
137
  )
105
138
 
106
139
  def get_catalog_file_path(self, block_uuid: str = None) -> str:
140
+ """
141
+ Get the file path for the data integration catalog file associated with a block.
142
+
143
+ Args:
144
+ block_uuid (str, optional): UUID of the block. Defaults to None.
145
+
146
+ Returns:
147
+ str: File path for the catalog file.
148
+ """
107
149
  return os.path.join(
108
150
  self.get_block_data_integration_settings_directory_path(block_uuid),
109
151
  BLOCK_CATALOG_FILENAME,
110
152
  )
111
153
 
112
154
  def get_catalog_from_file(self) -> Dict:
155
+ """
156
+ Read and return the data integration catalog from a file.
157
+
158
+ Returns:
159
+ Dict: Data integration catalog.
160
+ """
113
161
  catalog_full_path = self.get_catalog_file_path()
114
162
 
115
163
  if os.path.exists(catalog_full_path):
@@ -122,6 +170,12 @@ class DataIntegrationMixin:
122
170
  return
123
171
 
124
172
  async def get_catalog_from_file_async(self) -> Dict:
173
+ """
174
+ Asynchronously read and return the data integration catalog from a file.
175
+
176
+ Returns:
177
+ Dict: Data integration catalog.
178
+ """
125
179
  catalog_full_path = self.get_catalog_file_path()
126
180
 
127
181
  if os.path.exists(catalog_full_path):
@@ -133,7 +187,13 @@ class DataIntegrationMixin:
133
187
  except json.decoder.JSONDecodeError:
134
188
  return
135
189
 
136
- def update_catalog_file(self, catalog: Dict = None) -> Dict:
190
+ def update_catalog_file(self, catalog: Dict = None) -> None:
191
+ """
192
+ Update the data integration catalog file with the provided catalog.
193
+
194
+ Args:
195
+ catalog (Dict, optional): Data integration catalog to be saved. Defaults to None.
196
+ """
137
197
  catalog_full_path = self.get_catalog_file_path()
138
198
 
139
199
  os.makedirs(os.path.dirname(catalog_full_path), exist_ok=True)
@@ -143,6 +203,13 @@ class DataIntegrationMixin:
143
203
  f.write(json.dumps(catalog))
144
204
 
145
205
  def is_data_integration(self) -> bool:
206
+ """
207
+ Check if the block is a data integration block.
208
+ If the data_integration_in_batch_pipeline feature is not enabled, return False.
209
+
210
+ Returns:
211
+ bool: True if it's a data integration block, False otherwise.
212
+ """
146
213
  if not self.pipeline or not \
147
214
  Project(self.pipeline.repo_config).is_feature_enabled(
148
215
  FeatureUUID.DATA_INTEGRATION_IN_BATCH_PIPELINE,
@@ -185,6 +252,30 @@ class DataIntegrationMixin:
185
252
  partition: str = None,
186
253
  **kwargs,
187
254
  ) -> Dict:
255
+ """
256
+ Retrieve data integration settings for the current block.
257
+
258
+ Args:
259
+ data_integration_uuid_only (bool, optional): If True, retrieve only the data integration
260
+ UUID. Defaults to False.
261
+ dynamic_block_index (Union[int, None], optional): The index of the dynamic block, if
262
+ applicable. Defaults to None.
263
+ dynamic_upstream_block_uuids (Union[List[str], None], optional): List of upstream block
264
+ UUIDs, if applicable. Defaults to None.
265
+ from_notebook (bool, optional): Whether the request is made from a notebook context.
266
+ Defaults to False.
267
+ global_vars (Dict, optional): Global variables to be used in template rendering.
268
+ Defaults to None.
269
+ input_vars (List[Any], optional): Input variables for the block. Defaults to None.
270
+ partition (str, optional): Partition identifier, if applicable.
271
+ Defaults to None.
272
+ **kwargs: Additional keyword arguments.
273
+
274
+ Returns:
275
+ Dict: A dictionary containing the data integration settings, including catalog, config,
276
+ data_integration_uuid, selected_streams, and the appropriate key
277
+ (source or destination).
278
+ """
188
279
  if not self.is_data_integration():
189
280
  return
190
281
 
@@ -13,6 +13,7 @@ from mage_ai.data_integrations.logger.utils import (
13
13
  print_log_from_line,
14
14
  print_logs_from_output,
15
15
  )
16
+ from mage_ai.data_integrations.utils.config import get_batch_fetch_limit
16
17
  from mage_ai.data_integrations.utils.parsers import parse_logs_and_json
17
18
  from mage_ai.data_preparation.models.block.data_integration.constants import (
18
19
  EXECUTION_PARTITION_FROM_NOTEBOOK,
@@ -383,21 +384,20 @@ def execute_data_integration(
383
384
  # Handle incremental sync
384
385
  state_file_path = None
385
386
  if index is not None:
387
+ batch_fetch_limit = get_batch_fetch_limit(config)
386
388
  state_file_path = get_state_file_path(block, data_integration_uuid, stream)
387
-
388
389
  stream_catalogs = get_streams_from_catalog(catalog, [stream]) or []
390
+
389
391
  if len(stream_catalogs) == 1 and \
390
392
  REPLICATION_METHOD_INCREMENTAL == stream_catalogs[0].get('replication_method'):
391
393
  # Use the state to adjust the query
392
394
  # How do we write to the state when the source syncs can run in parallel?
393
395
  pass
394
396
  else:
395
- from mage_integrations.sources.constants import BATCH_FETCH_LIMIT
396
- query_data['_offset'] = BATCH_FETCH_LIMIT * index
397
+ query_data['_offset'] = batch_fetch_limit * index
397
398
 
398
399
  if not is_last_block_run:
399
- from mage_integrations.sources.constants import BATCH_FETCH_LIMIT
400
- query_data['_limit'] = BATCH_FETCH_LIMIT
400
+ query_data['_limit'] = batch_fetch_limit
401
401
 
402
402
  tags = dict(block_tags=dict(
403
403
  index=index,
@@ -10,7 +10,11 @@ import pandas as pd
10
10
 
11
11
  from mage_ai.data_cleaner.transformer_actions.utils import clean_column_name
12
12
  from mage_ai.data_integrations.logger.utils import print_log_from_line
13
- from mage_ai.data_integrations.utils.config import build_config, get_catalog_by_stream
13
+ from mage_ai.data_integrations.utils.config import (
14
+ build_config,
15
+ get_batch_fetch_limit,
16
+ get_catalog_by_stream,
17
+ )
14
18
  from mage_ai.data_preparation.models.block import Block
15
19
  from mage_ai.data_preparation.models.constants import PYTHON_COMMAND, BlockType
16
20
  from mage_ai.data_preparation.shared.stream import StreamToLogger
@@ -50,8 +54,6 @@ class IntegrationBlock(Block):
50
54
  runtime_arguments: Dict = None,
51
55
  **kwargs,
52
56
  ) -> List:
53
- from mage_integrations.sources.constants import BATCH_FETCH_LIMIT
54
-
55
57
  if logging_tags is None:
56
58
  logging_tags = dict()
57
59
 
@@ -106,10 +108,6 @@ class IntegrationBlock(Block):
106
108
  source_state_file_path,
107
109
  destination_state_file_path,
108
110
  )
109
- else:
110
- query_data['_offset'] = BATCH_FETCH_LIMIT * index
111
- if not is_last_block_run:
112
- query_data['_limit'] = BATCH_FETCH_LIMIT
113
111
 
114
112
  outputs = []
115
113
  if BlockType.DATA_LOADER == self.type:
@@ -120,6 +118,13 @@ class IntegrationBlock(Block):
120
118
  self.pipeline.data_loader.file_path,
121
119
  variables_dictionary_for_config,
122
120
  )
121
+ batch_fetch_limit = get_batch_fetch_limit(config)
122
+
123
+ if stream_catalog.get('replication_method') != 'INCREMENTAL':
124
+ query_data['_offset'] = batch_fetch_limit * index
125
+ if not is_last_block_run:
126
+ query_data['_limit'] = batch_fetch_limit
127
+
123
128
  args = [
124
129
  PYTHON_COMMAND,
125
130
  self.pipeline.source_file_path,
@@ -19,6 +19,7 @@ from mage_ai.data_preparation.models.block.sql import (
19
19
  from mage_ai.data_preparation.models.block.sql.utils.shared import (
20
20
  has_create_or_insert_statement,
21
21
  has_drop_statement,
22
+ has_update_statement,
22
23
  interpolate_vars,
23
24
  split_query_string,
24
25
  table_name_parts_from_query,
@@ -29,6 +30,7 @@ from mage_ai.io.config import ConfigFileLoader
29
30
  from mage_ai.settings.repo import get_repo_path
30
31
 
31
32
  PREVIEWABLE_BLOCK_TYPES = [
33
+ BlockType.CUSTOM,
32
34
  BlockType.DATA_EXPORTER,
33
35
  BlockType.DATA_LOADER,
34
36
  BlockType.DBT,
@@ -646,16 +648,17 @@ def execute_raw_sql(
646
648
 
647
649
  has_create_or_insert = has_create_or_insert_statement(query_string)
648
650
  has_drop = has_drop_statement(query_string)
651
+ has_update = has_update_statement(query_string)
649
652
 
650
653
  for query in split_query_string(query_string):
651
- if has_create_or_insert or has_drop:
654
+ if has_create_or_insert or has_drop or has_update:
652
655
  queries.append(query)
653
656
  fetch_query_at_indexes.append(False)
654
657
  else:
655
658
  queries.append(query)
656
659
  fetch_query_at_indexes.append(True)
657
660
 
658
- if should_query and has_create_or_insert:
661
+ if should_query and (has_create_or_insert or has_update) and block.full_table_name:
659
662
  queries.append(f'SELECT * FROM {block.full_table_name} LIMIT 1000')
660
663
  fetch_query_at_indexes.append(block.full_table_name)
661
664
 
@@ -494,7 +494,16 @@ def extract_insert_statement_table_names(text: str) -> List[str]:
494
494
 
495
495
  def extract_drop_statement_table_names(text: str) -> List[str]:
496
496
  matches = re.findall(
497
- r'drop table(?: if exists)*',
497
+ r'\bdrop\s+table(?:\s+if\s+exists)?\s+([\w.]+)',
498
+ remove_comments(text),
499
+ re.IGNORECASE,
500
+ )
501
+ return matches
502
+
503
+
504
+ def extract_update_statement_table_names(text: str) -> List[str]:
505
+ matches = re.findall(
506
+ r'\bupdate\b\s+([\w.]+)\s+set\s+[\s\S]*?\bwhere\b',
498
507
  remove_comments(text),
499
508
  re.IGNORECASE,
500
509
  )
@@ -515,6 +524,11 @@ def has_drop_statement(text: str) -> bool:
515
524
  return len(matches) >= 1
516
525
 
517
526
 
527
+ def has_update_statement(text: str) -> bool:
528
+ matches = extract_update_statement_table_names(text)
529
+ return len(matches) >= 1
530
+
531
+
518
532
  def split_query_string(query_string: str) -> List[str]:
519
533
  text_parts = []
520
534
 
@@ -326,6 +326,21 @@ class PipelineInteractions:
326
326
 
327
327
  await self.save()
328
328
 
329
+ async def variables(self) -> Dict:
330
+ variables = {}
331
+
332
+ interaction = await self.interaction()
333
+ if not interaction:
334
+ return variables
335
+
336
+ for block_interactions in (interaction.blocks or {}).values():
337
+ for block_interaction in block_interactions:
338
+ interaction_base = InteractionBase(block_interaction.uuid)
339
+ interaction_variables = await interaction_base.variables()
340
+ variables.update(interaction_variables)
341
+
342
+ return variables
343
+
329
344
  async def to_dict(self) -> Dict:
330
345
  interaction = await self.interaction()
331
346
 
@@ -155,6 +155,7 @@ def get_secret_value(
155
155
  pipeline_uuid: str = None,
156
156
  project_uuid: str = None,
157
157
  repo_name: str = None,
158
+ **kwargs,
158
159
  ) -> Optional[str]:
159
160
  from mage_ai.orchestration.db.models.secrets import Secret
160
161
  key, key_uuid = _get_encryption_key(
@@ -189,29 +190,17 @@ def get_secret_value(
189
190
  return fernet.decrypt(secret_legacy.value.encode('utf-8')).decode('utf-8')
190
191
  except InvalidToken:
191
192
  pass
193
+ if not kwargs.get('suppress_warning', False):
194
+ print(f'WARNING: Could not find secret value for secret {name}.')
192
195
 
193
- print(f'WARNING: Could not find secret value for secret {name}.')
194
196
 
195
-
196
- def get_secret_value_db_safe(
197
- name: str,
198
- entity: Entity = Entity.GLOBAL,
199
- pipeline_uuid: str = None,
200
- project_uuid: str = None,
201
- repo_name: str = None,
202
- ) -> Optional[str]:
197
+ def get_secret_value_db_safe(name: str, **kwargs) -> Optional[str]:
203
198
  """
204
199
  Calls get_secret_value only if the db has already been initialized.
205
200
  """
206
201
  from mage_ai.orchestration.db import db_connection
207
202
  if db_connection.session and db_connection.session.is_active:
208
- return get_secret_value(
209
- name,
210
- entity=entity,
211
- pipeline_uuid=pipeline_uuid,
212
- project_uuid=project_uuid,
213
- repo_name=repo_name,
214
- )
203
+ return get_secret_value(name, **kwargs)
215
204
  else:
216
205
  return None
217
206
 
@@ -222,6 +211,7 @@ def delete_secret(
222
211
  entity: Entity = Entity.GLOBAL,
223
212
  pipeline_uuid: str = None,
224
213
  project_uuid: str = None,
214
+ **kwargs,
225
215
  ) -> None:
226
216
  from mage_ai.orchestration.db.models.secrets import Secret
227
217
  secret = None
@@ -242,7 +232,7 @@ def delete_secret(
242
232
 
243
233
  if secret:
244
234
  secret.delete()
245
- else:
235
+ elif not kwargs.get('suppress_warning', False):
246
236
  print(f'WARNING: Could not find secret {name}')
247
237
 
248
238
 
@@ -0,0 +1,5 @@
1
+ connector_type: google_cloud_pubsub
2
+ project_id: my-project-id
3
+ topic_id: my-topic-id
4
+ pubsub_emulator_host: null # "host.docker.internal:8085"
5
+ path_to_credentials_json_file: "./google_credentials.json"
@@ -37,6 +37,11 @@ spark_config:
37
37
  # List of key-value pairs to be set in SparkConf
38
38
  # e.g., others: {'spark.executor.memory': '4g', 'spark.executor.cores': '2'}
39
39
  others: {}
40
+ # Wehther to create custom SparkSession via code and set in kwargs['context']
41
+ use_custom_session: false
42
+ # The variable name to set in kwargs['context'],
43
+ # e.g. kwargs['context']['spark'] = spark_session
44
+ custom_session_var_name: 'spark'
40
45
 
41
46
  notification_config:
42
47
  alert_on:
mage_ai/io/druid.py CHANGED
@@ -1,9 +1,12 @@
1
- from mage_ai.io.base import ExportWritePolicy, QUERY_ROW_LIMIT
1
+ from typing import IO, Any, Dict, List, Union
2
+
3
+ from pandas import DataFrame, Series
4
+ from pydruid.db.api import Connection
5
+ from pydruid.db.api import Cursor as CursorParent
6
+
7
+ from mage_ai.io.base import QUERY_ROW_LIMIT, ExportWritePolicy
2
8
  from mage_ai.io.config import BaseConfigLoader, ConfigKey
3
9
  from mage_ai.io.sql import BaseSQL
4
- from pandas import DataFrame, Series
5
- from pydruid.db.api import Connection, Cursor as CursorParent
6
- from typing import Dict, List, Union, Any, IO
7
10
 
8
11
  WRITE_NOT_SUPPORTED_EXCEPTION = Exception('write operations are not supported.')
9
12
 
@@ -138,7 +141,8 @@ class Druid(BaseSQL):
138
141
  db_dtypes: List[str],
139
142
  dtypes: List[str],
140
143
  full_table_name: str,
141
- buffer: Union[IO, None] = None) -> None:
144
+ buffer: Union[IO, None] = None,
145
+ **kwargs) -> None:
142
146
  raise WRITE_NOT_SUPPORTED_EXCEPTION
143
147
 
144
148
  def export(self,
mage_ai/io/mssql.py CHANGED
@@ -4,8 +4,9 @@ import numpy as np
4
4
  import pyodbc
5
5
  import simplejson
6
6
  from pandas import DataFrame, Series
7
+ from sqlalchemy import create_engine
7
8
 
8
- from mage_ai.io.base import QUERY_ROW_LIMIT
9
+ from mage_ai.io.base import QUERY_ROW_LIMIT, ExportWritePolicy
9
10
  from mage_ai.io.config import BaseConfigLoader, ConfigKey
10
11
  from mage_ai.io.export_utils import PandasTypes
11
12
  from mage_ai.io.sql import BaseSQL
@@ -21,6 +22,7 @@ class MSSQL(BaseSQL):
21
22
  user: str,
22
23
  schema: str = None,
23
24
  port: int = 1433,
25
+ verbose: bool = True,
24
26
  **kwargs,
25
27
  ) -> None:
26
28
  super().__init__(
@@ -30,24 +32,25 @@ class MSSQL(BaseSQL):
30
32
  password=password,
31
33
  schema=schema,
32
34
  port=port,
35
+ verbose=verbose,
33
36
  **kwargs
34
37
  )
35
38
 
36
- def _enforce_limit(self, query: str, limit: int = QUERY_ROW_LIMIT) -> str:
37
- # MSSQL doesn't support WITH statements in subqueries, so if the user uses a WITH
38
- # statement in their query, it would break if we tried to enforce a limit.
39
- return query
40
-
41
- @classmethod
42
- def with_config(cls, config: BaseConfigLoader) -> 'MSSQL':
43
- return cls(
44
- database=config[ConfigKey.MSSQL_DATABASE],
45
- schema=config[ConfigKey.MSSQL_SCHEMA],
46
- driver=config[ConfigKey.MSSQL_DRIVER],
47
- host=config[ConfigKey.MSSQL_HOST],
48
- password=config[ConfigKey.MSSQL_PASSWORD],
49
- port=config[ConfigKey.MSSQL_PORT],
50
- user=config[ConfigKey.MSSQL_USER],
39
+ @property
40
+ def connection_string(self):
41
+ driver = self.settings['driver']
42
+ server = self.settings['server']
43
+ database = self.settings['database']
44
+ username = self.settings['user']
45
+ password = self.settings['password']
46
+ return (
47
+ f'DRIVER={{{driver}}};'
48
+ f'SERVER={server};'
49
+ f'DATABASE={database};'
50
+ f'UID={username};'
51
+ f'PWD={password};'
52
+ 'ENCRYPT=yes;'
53
+ 'TrustServerCertificate=yes;'
51
54
  )
52
55
 
53
56
  def default_schema(self) -> str:
@@ -55,21 +58,9 @@ class MSSQL(BaseSQL):
55
58
 
56
59
  def open(self) -> None:
57
60
  with self.printer.print_msg('Opening connection to MSSQL database'):
58
- driver = self.settings['driver']
59
- server = self.settings['server']
60
- database = self.settings['database']
61
- username = self.settings['user']
62
- password = self.settings['password']
63
- connection_string = (
64
- f'DRIVER={{{driver}}};'
65
- f'SERVER={server};'
66
- f'DATABASE={database};'
67
- f'UID={username};'
68
- f'PWD={password};'
69
- 'ENCRYPT=yes;'
70
- 'TrustServerCertificate=yes;'
61
+ self._ctx = pyodbc.connect(
62
+ self.connection_string,
71
63
  )
72
- self._ctx = pyodbc.connect(connection_string)
73
64
 
74
65
  def build_create_schema_command(
75
66
  self,
@@ -149,6 +140,20 @@ class MSSQL(BaseSQL):
149
140
  sql = f'INSERT INTO {full_table_name} VALUES ({values_placeholder})'
150
141
  cursor.executemany(sql, values)
151
142
 
143
+ def upload_dataframe_fast(
144
+ self,
145
+ df: DataFrame,
146
+ schema_name: str,
147
+ table_name: str,
148
+ if_exists: ExportWritePolicy = ExportWritePolicy.REPLACE,
149
+
150
+ ):
151
+ engine = create_engine(
152
+ f'mssql+pyodbc://?odbc_connect={self.connection_string}',
153
+ fast_executemany=True,
154
+ )
155
+ df.to_sql(table_name, engine, schema=schema_name, if_exists=if_exists, index=False)
156
+
152
157
  def get_type(self, column: Series, dtype: str) -> str:
153
158
  if dtype in (
154
159
  PandasTypes.MIXED,
@@ -199,3 +204,20 @@ class MSSQL(BaseSQL):
199
204
  print(f'Invalid datatype provided: {dtype}')
200
205
 
201
206
  return 'char(255)'
207
+
208
+ @classmethod
209
+ def with_config(cls, config: BaseConfigLoader) -> 'MSSQL':
210
+ return cls(
211
+ database=config[ConfigKey.MSSQL_DATABASE],
212
+ schema=config[ConfigKey.MSSQL_SCHEMA],
213
+ driver=config[ConfigKey.MSSQL_DRIVER],
214
+ host=config[ConfigKey.MSSQL_HOST],
215
+ password=config[ConfigKey.MSSQL_PASSWORD],
216
+ port=config[ConfigKey.MSSQL_PORT],
217
+ user=config[ConfigKey.MSSQL_USER],
218
+ )
219
+
220
+ def _enforce_limit(self, query: str, limit: int = QUERY_ROW_LIMIT) -> str:
221
+ # MSSQL doesn't support WITH statements in subqueries, so if the user uses a WITH
222
+ # statement in their query, it would break if we tried to enforce a limit.
223
+ return query