apache-airflow-providers-common-sql 2.1.1__tar.gz → 2.2.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 (107) hide show
  1. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/PKG-INFO +6 -6
  2. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/README.rst +3 -3
  3. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/changelog.rst +31 -0
  4. apache_airflow_providers_common_sql-2.2.0/docs/connections.rst +164 -0
  5. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/dialects.rst +6 -4
  6. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/index.rst +3 -3
  7. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/operators.rst +22 -1
  8. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/provider.yaml +2 -1
  9. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/pyproject.toml +3 -3
  10. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/__init__.py +1 -1
  11. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/config.py +16 -3
  12. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/engine.py +35 -0
  13. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/object_storage_provider.py +58 -3
  14. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/example_dags/example_analytics.py +13 -0
  15. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/sensors/sql.py +1 -1
  16. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_bulk_load.py +2 -2
  17. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_execute_query.py +2 -2
  18. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_insert_rows.py +2 -2
  19. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_threshold_check.py +2 -2
  20. apache_airflow_providers_common_sql-2.2.0/tests/unit/common/sql/datafusion/test_base.py +74 -0
  21. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/datafusion/test_engine.py +114 -1
  22. apache_airflow_providers_common_sql-2.2.0/tests/unit/common/sql/datafusion/test_exceptions.py +40 -0
  23. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/datafusion/test_format_handlers.py +1 -1
  24. apache_airflow_providers_common_sql-2.2.0/tests/unit/common/sql/datafusion/test_object_storage_provider.py +165 -0
  25. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/decorators/test_sql.py +2 -5
  26. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/operators/test_sql.py +1 -2
  27. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/sensors/test_sql.py +2 -3
  28. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/test_config.py +29 -0
  29. apache_airflow_providers_common_sql-2.1.1/docs/connections.rst +0 -32
  30. apache_airflow_providers_common_sql-2.1.1/tests/unit/common/sql/datafusion/test_object_storage_provider.py +0 -74
  31. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/.gitignore +0 -0
  32. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/LICENSE +0 -0
  33. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/NOTICE +0 -0
  34. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/.latest-doc-only-change.txt +0 -0
  35. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/commits.rst +0 -0
  36. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/conf.py +0 -0
  37. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/dataframes.rst +0 -0
  38. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/installing-providers-from-sources.rst +0 -0
  39. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/integration-logos/sql.png +0 -0
  40. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/security.rst +0 -0
  41. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/docs/supported-database-types.rst +0 -0
  42. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/__init__.py +0 -0
  43. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/__init__.py +0 -0
  44. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/__init__.py +0 -0
  45. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/README_API.md +0 -0
  46. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/__init__.py +0 -0
  47. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/base.py +0 -0
  48. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/exceptions.py +0 -0
  49. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/datafusion/format_handlers.py +0 -0
  50. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/decorators/__init__.py +0 -0
  51. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/decorators/analytics.py +0 -0
  52. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/decorators/sql.py +0 -0
  53. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/dialects/__init__.py +0 -0
  54. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/dialects/dialect.py +0 -0
  55. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/dialects/dialect.pyi +0 -0
  56. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/doc/adr/0001-record-architecture-decisions.md +0 -0
  57. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/doc/adr/0002-return-common-data-structure-from-dbapihook-derived-hooks.md +0 -0
  58. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/doc/adr/0003-introduce-notion-of-dialects-in-dbapihook.md +0 -0
  59. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/example_dags/__init__.py +0 -0
  60. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/get_provider_info.py +0 -0
  61. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/get_provider_info.pyi +0 -0
  62. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/__init__.py +0 -0
  63. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/handlers.py +0 -0
  64. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/handlers.pyi +0 -0
  65. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/lineage.py +0 -0
  66. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/lineage.pyi +0 -0
  67. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/sql.py +0 -0
  68. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/hooks/sql.pyi +0 -0
  69. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/operators/__init__.py +0 -0
  70. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/operators/analytics.py +0 -0
  71. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/operators/generic_transfer.py +0 -0
  72. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/operators/generic_transfer.pyi +0 -0
  73. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/operators/sql.py +0 -0
  74. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/sensors/__init__.py +0 -0
  75. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/sensors/sql.pyi +0 -0
  76. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/triggers/__init__.py +0 -0
  77. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/triggers/sql.py +0 -0
  78. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/triggers/sql.pyi +0 -0
  79. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/src/airflow/providers/common/sql/version_compat.py +0 -0
  80. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/conftest.py +0 -0
  81. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/__init__.py +0 -0
  82. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/__init__.py +0 -0
  83. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/__init__.py +0 -0
  84. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_generic_transfer.py +0 -0
  85. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_column_table_check.py +0 -0
  86. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/system/common/sql/example_sql_value_check.py +0 -0
  87. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/__init__.py +0 -0
  88. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/__init__.py +0 -0
  89. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/__init__.py +0 -0
  90. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/datafusion/__init__.py +0 -0
  91. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/decorators/__init__.py +0 -0
  92. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/decorators/test_analytics.py +0 -0
  93. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/dialects/__init__.py +0 -0
  94. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/dialects/test_dialect.py +0 -0
  95. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/__init__.py +0 -0
  96. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/test_dbapi.py +0 -0
  97. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/test_handlers.py +0 -0
  98. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/test_lineage.py +0 -0
  99. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/test_sql.py +0 -0
  100. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/hooks/test_sqlparse.py +0 -0
  101. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/operators/__init__.py +0 -0
  102. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/operators/test_analytics.py +0 -0
  103. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/operators/test_generic_transfer.py +0 -0
  104. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/operators/test_sql_execute.py +0 -0
  105. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/sensors/__init__.py +0 -0
  106. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/triggers/__init__.py +0 -0
  107. {apache_airflow_providers_common_sql-2.1.1 → apache_airflow_providers_common_sql-2.2.0}/tests/unit/common/sql/triggers/test_sql.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: apache-airflow-providers-common-sql
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: Provider package apache-airflow-providers-common-sql for Apache Airflow
5
5
  Keywords: airflow-provider,common.sql,airflow,integration
6
6
  Author-email: Apache Software Foundation <dev@airflow.apache.org>
@@ -39,8 +39,8 @@ Requires-Dist: polars>=1.26.0 ; extra == "polars"
39
39
  Requires-Dist: pyiceberg-core>=0.8.0 ; extra == "pyiceberg-core"
40
40
  Requires-Dist: sqlalchemy>=1.4.54 ; extra == "sqlalchemy"
41
41
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
42
- Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/changelog.html
43
- Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1
42
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/changelog.html
43
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0
44
44
  Project-URL: Mastodon, https://fosstodon.org/@airflow
45
45
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
46
46
  Project-URL: Source Code, https://github.com/apache/airflow
@@ -83,7 +83,7 @@ Import-Namespace: airflow.providers.common
83
83
 
84
84
  Package ``apache-airflow-providers-common-sql``
85
85
 
86
- Release: ``2.1.1``
86
+ Release: ``2.2.0``
87
87
 
88
88
 
89
89
  `Common SQL Provider <https://en.wikipedia.org/wiki/SQL>`__
@@ -96,7 +96,7 @@ This is a provider package for ``common.sql`` provider. All classes for this pro
96
96
  are in ``airflow.providers.common.sql`` python package.
97
97
 
98
98
  You can find package information and changelog for the provider
99
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/>`_.
99
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/>`_.
100
100
 
101
101
  Installation
102
102
  ------------
@@ -158,5 +158,5 @@ Extra Dependencies
158
158
  ================== =======================================================================================================================================================================
159
159
 
160
160
  The changelog for the provider package can be found in the
161
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/changelog.html>`_.
161
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/changelog.html>`_.
162
162
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  Package ``apache-airflow-providers-common-sql``
25
25
 
26
- Release: ``2.1.1``
26
+ Release: ``2.2.0``
27
27
 
28
28
 
29
29
  `Common SQL Provider <https://en.wikipedia.org/wiki/SQL>`__
@@ -36,7 +36,7 @@ This is a provider package for ``common.sql`` provider. All classes for this pro
36
36
  are in ``airflow.providers.common.sql`` python package.
37
37
 
38
38
  You can find package information and changelog for the provider
39
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/>`_.
39
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/>`_.
40
40
 
41
41
  Installation
42
42
  ------------
@@ -98,4 +98,4 @@ Extra Dependencies
98
98
  ================== =======================================================================================================================================================================
99
99
 
100
100
  The changelog for the provider package can be found in the
101
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/changelog.html>`_.
101
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/changelog.html>`_.
@@ -25,6 +25,37 @@
25
25
  Changelog
26
26
  ---------
27
27
 
28
+ 2.2.0
29
+ .....
30
+
31
+ Features
32
+ ~~~~~~~~
33
+
34
+ * ``Template every connection id accepted by provider operators (#73286)``
35
+ * ``Add GCS support to the DataFusion object storage layer (#73370)``
36
+ * ``Allow DataSourceConfig to represent a plain database table (#73273)``
37
+
38
+ Bug Fixes
39
+ ~~~~~~~~~
40
+
41
+ * ``Read the legacy extra__google_cloud_platform__ prefix for GCS credentials (#73613)``
42
+ * ``Fix DataFusion storage-type error message on Python 3.11+ (#73413)``
43
+ * ``Reject a DataSourceConfig with no uri or format in DataFusionEngine (#73287)``
44
+ * ``Fix exception messages rendering as a tuple instead of the value (#73296)``
45
+
46
+ Doc-only
47
+ ~~~~~~~~
48
+
49
+ * ``Rewrite the 'common.sql' connections guide and fix the dialect extra name (#73605)``
50
+ * ``Fix grammar in the dialects documentation (#73145)``
51
+
52
+ .. Below changes are excluded from the changelog. Move them to
53
+ appropriate section above if needed. Do not delete the lines(!):
54
+ * ``Revert "[main] Upgrade important CI environment (#73308)" (#73621)``
55
+ * ``[main] Upgrade important CI environment (#73308)``
56
+ * ``Add tests for DataFusion base classes (#71983)``
57
+ * ``Use common.compat.sdk for the remaining provider timezone imports (#71209)``
58
+
28
59
  2.1.1
29
60
  .....
30
61
 
@@ -0,0 +1,164 @@
1
+ .. Licensed to the Apache Software Foundation (ASF) under one
2
+ or more contributor license agreements. See the NOTICE file
3
+ distributed with this work for additional information
4
+ regarding copyright ownership. The ASF licenses this file
5
+ to you under the Apache License, Version 2.0 (the
6
+ "License"); you may not use this file except in compliance
7
+ with the License. You may obtain a copy of the License at
8
+
9
+ .. http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ .. Unless required by applicable law or agreed to in writing,
12
+ software distributed under the License is distributed on an
13
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ KIND, either express or implied. See the License for the
15
+ specific language governing permissions and limitations
16
+ under the License.
17
+
18
+ .. _howto/connection:sql:
19
+
20
+ Connecting to SQL Databases
21
+ ===========================
22
+
23
+ The common SQL provider has no connection type of its own. Its operators, sensor and
24
+ ``@task.sql`` decorator work with a connection from a database provider: a ``postgres``
25
+ connection from the Postgres provider, a ``snowflake`` connection from the Snowflake provider,
26
+ and so on. Create the connection as the database provider documents it, then pass its ID to
27
+ the operator.
28
+
29
+ .. code-block:: python
30
+
31
+ from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator
32
+
33
+ create_table = SQLExecuteQueryOperator(
34
+ task_id="create_table",
35
+ conn_id="my_postgres",
36
+ sql="CREATE TABLE IF NOT EXISTS users (id INT, name TEXT)",
37
+ )
38
+
39
+ The same task runs against MySQL, Snowflake or Trino by pointing ``conn_id`` at a connection
40
+ of that type. The SQL itself still has to be valid for that database.
41
+
42
+ Setting up the connection
43
+ -------------------------
44
+
45
+ 1. Install the provider for your database, for example ``apache-airflow-providers-postgres``.
46
+ :doc:`supported-database-types` lists the providers that work with common SQL.
47
+ 2. Create a connection of that provider's type. The fields and extras are described on the
48
+ provider's own connection page, such as
49
+ :doc:`the Postgres connection <apache-airflow-providers-postgres:connections/postgres>`.
50
+ 3. Pass the connection ID as ``conn_id``. There is no default connection ID, so every task has
51
+ to name one. ``GenericTransfer`` takes two: ``source_conn_id`` and ``destination_conn_id``.
52
+
53
+ How the hook is chosen
54
+ ----------------------
55
+
56
+ At run time, a common SQL operator looks up the connection and instantiates the hook class that
57
+ the database provider registered for that connection type. The hook has to be a subclass of
58
+ :class:`~airflow.providers.common.sql.hooks.sql.DbApiHook`, otherwise the task fails with an
59
+ error naming the hook class it found.
60
+
61
+ To see which hook a connection type resolves to, run:
62
+
63
+ .. code-block:: bash
64
+
65
+ airflow providers hooks
66
+
67
+ If a connection type is missing from that list, its provider is not installed in the
68
+ environment the task runs in. If the hook is listed but is not a ``DbApiHook``, that connection
69
+ type cannot be used with common SQL (an HTTP or cloud storage connection, for example).
70
+
71
+ Overriding connection settings per task
72
+ ---------------------------------------
73
+
74
+ To change how one task connects without editing a connection other tasks share, set
75
+ ``database`` or ``hook_params`` on the operator.
76
+
77
+ ``database``
78
+ Runs the task against a different database from the one set in the connection. For most
79
+ connection types this replaces the connection's ``schema`` field, which is where Postgres,
80
+ MySQL and similar providers store the database name.
81
+
82
+ ``hook_params``
83
+ A dictionary passed as keyword arguments to the hook's constructor. Which keys a hook
84
+ accepts is up to the database provider. For example, the Snowflake hook accepts
85
+ ``warehouse`` and ``role``:
86
+
87
+ .. code-block:: python
88
+
89
+ SQLExecuteQueryOperator(
90
+ task_id="nightly_rollup",
91
+ conn_id="snowflake_default",
92
+ sql="CALL rollup_daily_sales()",
93
+ hook_params={"warehouse": "REPORTING_WH", "role": "REPORTER"},
94
+ )
95
+
96
+ For operators in ``airflow.providers.common.sql.operators.sql``, the connection's extras are
97
+ merged into ``hook_params`` before the hook is built, and a key set in ``hook_params`` wins
98
+ over the same key in the extras. ``SQLSensor`` takes ``hook_params`` too, and
99
+ ``GenericTransfer`` takes ``source_hook_params`` and ``destination_hook_params``.
100
+
101
+ Connection extras read by common SQL
102
+ ------------------------------------
103
+
104
+ Every ``DbApiHook`` reads the following keys from the connection's extras, on top of whatever
105
+ the database provider documents. They control the SQL that ``insert_rows``,
106
+ ``SQLInsertRowsOperator`` and ``GenericTransfer`` generate, and they matter most for generic
107
+ connection types such as ODBC and JDBC, where Airflow cannot tell from the connection alone
108
+ which database is on the other end.
109
+
110
+ .. list-table::
111
+ :header-rows: 1
112
+ :widths: 25 20 55
113
+
114
+ * - Extra
115
+ - Default
116
+ - Effect
117
+ * - ``placeholder``
118
+ - ``%s``
119
+ - Parameter placeholder in generated statements. Only ``%s`` and ``?`` are accepted; any
120
+ other value is ignored with a warning. Some hooks set their own default, such as ``?``
121
+ for SQLite.
122
+ * - ``sqlalchemy_scheme``
123
+ - none (``mssql+pyodbc`` for ODBC)
124
+ - SQLAlchemy scheme for connection types that have none of their own, such as ODBC and
125
+ JDBC. The :doc:`dialect <dialects>` is taken from it, so ``mssql+pyodbc`` selects the
126
+ ``mssql`` dialect. It takes precedence over ``dialect``.
127
+ * - ``dialect``
128
+ - ``default``
129
+ - Name of the dialect to use, such as ``mssql`` or ``postgresql``. Read only when neither the
130
+ connection URI nor ``sqlalchemy_scheme`` gives a dialect, as with a JDBC connection whose
131
+ ``host`` holds a ``jdbc:`` URL.
132
+ * - ``insert_statement_format``
133
+ - ``INSERT INTO {} {} VALUES ({})``
134
+ - Template for insert statements. The three fields are the table, the column list and the
135
+ placeholders.
136
+ * - ``replace_statement_format``
137
+ - ``REPLACE INTO {} {} VALUES ({})``
138
+ - Template for upsert statements, used when ``replace=True``.
139
+ * - ``escape_word_format``
140
+ - ``"{}"``
141
+ - How a column name is quoted when it needs escaping, for example ``[{}]`` for SQL Server.
142
+ * - ``escape_column_names``
143
+ - ``false``
144
+ - Quote every column name. By default only reserved words and names containing special
145
+ characters are quoted.
146
+
147
+ For example, an ODBC connection to SQL Server that uses pyodbc's ``?`` placeholders and
148
+ bracket-quoted column names can set:
149
+
150
+ .. code-block:: json
151
+
152
+ {
153
+ "sqlalchemy_scheme": "mssql+pyodbc",
154
+ "placeholder": "?",
155
+ "escape_word_format": "[{}]"
156
+ }
157
+
158
+ ``mssql+pyodbc`` is already the ODBC default. An ODBC connection to any other database has to
159
+ change it, because setting ``dialect`` alone keeps the ``mssql`` dialect.
160
+
161
+ The ``mssql`` and ``postgresql`` dialects are registered by the Microsoft SQL Server and Postgres
162
+ providers. If the provider for a dialect is not installed, the hook uses the default dialect
163
+ without a warning, so an upsert through ODBC to SQL Server generates ``REPLACE INTO`` instead of
164
+ ``MERGE`` unless ``apache-airflow-providers-microsoft-mssql`` is installed.
@@ -25,7 +25,7 @@ and the :class:`~airflow.providers.jdbc.hooks.jdbc.JdbcHook` are generic hooks w
25
25
  database that has a driver for it, it needed an abstraction layer which allows us to run specialized queries
26
26
  depending of the database to which we connect and that's why dialects where introduced.
27
27
 
28
- The default :class:`~airflow.providers.common.sql.dialects.dialect.Dialect` class has following operations
28
+ The default :class:`~airflow.providers.common.sql.dialects.dialect.Dialect` class has the following operations
29
29
  available which underneath use SQLAlchemy to execute, but can be overloaded with specialized implementations
30
30
  per database:
31
31
 
@@ -45,11 +45,13 @@ At the moment there are only 3 dialects available:
45
45
  - ``mssql`` :class:`~airflow.providers.microsoft.mssql.dialects.mssql.MsSqlDialect` specialized for Microsoft SQL Server;
46
46
  - ``postgresql`` :class:`~airflow.providers.postgres.dialects.postgres.PostgresDialect` specialized for PostgreSQL;
47
47
 
48
- The dialect to be used will be derived from the connection string, which sometimes won't be possible. There is always
49
- the possibility to specify the dialect name through the extra options of the connection:
48
+ The dialect is derived from the connection URI. ODBC and JDBC connections have no database in their connection type, so
49
+ set the ``sqlalchemy_scheme`` extra to name it, for example ``mssql+pyodbc``. The dialect is the part before the
50
+ ``+``. The ``dialect`` extra is read only when neither the URI nor ``sqlalchemy_scheme`` gives a dialect, as with a JDBC
51
+ connection whose ``host`` holds a ``jdbc:`` URL:
50
52
 
51
53
  .. code-block::
52
54
 
53
- dialect_name: 'mssql'
55
+ dialect: 'mssql'
54
56
 
55
57
  If a specific dialect isn't available for a database, the default one will be used, same when a non-existing dialect name is specified.
@@ -79,7 +79,7 @@ apache-airflow-providers-common-sql package
79
79
  `Common SQL Provider <https://en.wikipedia.org/wiki/SQL>`__
80
80
 
81
81
 
82
- Release: 2.1.1
82
+ Release: 2.2.0
83
83
 
84
84
  Provider package
85
85
  ----------------
@@ -160,5 +160,5 @@ Downloading official packages
160
160
  You can download officially released packages and verify their checksums and signatures from the
161
161
  `Official Apache Download site <https://downloads.apache.org/airflow/providers/>`_
162
162
 
163
- * `The apache-airflow-providers-common-sql 2.1.1 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1.tar.gz.sha512>`__)
164
- * `The apache-airflow-providers-common-sql 2.1.1 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.1.1-py3-none-any.whl.sha512>`__)
163
+ * `The apache-airflow-providers-common-sql 2.2.0 sdist package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0.tar.gz>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0.tar.gz.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0.tar.gz.sha512>`__)
164
+ * `The apache-airflow-providers-common-sql 2.2.0 wheel package <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0-py3-none-any.whl>`_ (`asc <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0-py3-none-any.whl.asc>`__, `sha512 <https://downloads.apache.org/airflow/providers/apache_airflow_providers_common_sql-2.2.0-py3-none-any.whl.sha512>`__)
@@ -296,10 +296,11 @@ The Analytics Operator is ideal for performing efficient, high-performance analy
296
296
  Supported Storage Systems
297
297
  -------------------------
298
298
  - S3
299
+ - GCS
299
300
  - Local File System
300
301
 
301
302
  .. note::
302
- GCS, Azure, HTTP, Delta, Iceberg are not yet supported but will be added in the future.
303
+ Azure, HTTP, Delta are not yet supported but will be added in the future.
303
304
 
304
305
 
305
306
 
@@ -341,6 +342,26 @@ S3 Storage
341
342
  :start-after: [START howto_analytics_operator_with_s3]
342
343
  :end-before: [END howto_analytics_operator_with_s3]
343
344
 
345
+ GCS Storage
346
+ -----------
347
+ Use a ``conn_id`` pointing to a ``google_cloud_platform`` connection. Credentials are
348
+ resolved in this order:
349
+
350
+ 1. ``key_path`` -- a service account JSON file on disk
351
+ 2. ``keyfile_dict`` -- the service account JSON contents, inline
352
+ 3. the ``GOOGLE_APPLICATION_CREDENTIALS`` environment variable
353
+ 4. Application Default Credentials -- the local ``gcloud`` CLI credentials file, or
354
+ the GCE/GKE metadata server
355
+
356
+ ``key_path`` and ``keyfile_dict`` are mutually exclusive. ``key_secret_name``,
357
+ ``credential_config_file``, and ``impersonation_chain`` are not supported.
358
+
359
+ .. exampleinclude:: /../../sql/src/airflow/providers/common/sql/example_dags/example_analytics.py
360
+ :language: python
361
+ :dedent: 4
362
+ :start-after: [START howto_analytics_operator_with_gcs]
363
+ :end-before: [END howto_analytics_operator_with_gcs]
364
+
344
365
  Local File System Storage
345
366
  -------------------------
346
367
  .. exampleinclude:: /../../sql/src/airflow/providers/common/sql/example_dags/example_analytics.py
@@ -23,12 +23,13 @@ description: |
23
23
 
24
24
  state: ready
25
25
  lifecycle: production
26
- source-date-epoch: 1787011200
26
+ source-date-epoch: 1790023613
27
27
  # Note that those versions are maintained by release manager - do not update them manually
28
28
  # with the exception of case where other provider in sources has >= new provider version.
29
29
  # In such case adding >= NEW_VERSION and bumping to NEW_VERSION in a provider have
30
30
  # to be done in the same PR
31
31
  versions:
32
+ - 2.2.0
32
33
  - 2.1.1
33
34
  - 2.1.0
34
35
  - 2.0.3
@@ -25,7 +25,7 @@ build-backend = "flit_core.buildapi"
25
25
 
26
26
  [project]
27
27
  name = "apache-airflow-providers-common-sql"
28
- version = "2.1.1"
28
+ version = "2.2.0"
29
29
  description = "Provider package apache-airflow-providers-common-sql for Apache Airflow"
30
30
  readme = "README.rst"
31
31
  license = "Apache-2.0"
@@ -148,8 +148,8 @@ apache-airflow-providers-common-sql = {workspace = true}
148
148
  apache-airflow-providers-standard = {workspace = true}
149
149
 
150
150
  [project.urls]
151
- "Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1"
152
- "Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.1.1/changelog.html"
151
+ "Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0"
152
+ "Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-common-sql/2.2.0/changelog.html"
153
153
  "Bug Tracker" = "https://github.com/apache/airflow/issues"
154
154
  "Source Code" = "https://github.com/apache/airflow"
155
155
  "Slack Chat" = "https://s.apache.org/airflow-slack"
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "2.1.1"
32
+ __version__ = "2.2.0"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "2.11.0"
@@ -47,6 +47,7 @@ class StorageType(str, Enum):
47
47
  """Storage types for Data Fusion."""
48
48
 
49
49
  S3 = "s3"
50
+ GCS = "gcs"
50
51
  LOCAL = "local"
51
52
 
52
53
 
@@ -62,6 +63,9 @@ class DataSourceConfig:
62
63
  require ``uri`` or ``storage_type``; they use ``conn_id`` and format-specific
63
64
  keys in ``options`` (e.g. ``catalog_table_name`` for Iceberg).
64
65
 
66
+ **Plain database tables** (neither ``uri`` nor ``format`` set) are not
67
+ object-store backed either; ``storage_type`` stays ``None`` and is not inferred.
68
+
65
69
  :param conn_id: The connection ID to use for accessing the data source.
66
70
  :param uri: The URI of the data source (e.g., file path, S3 bucket, etc.).
67
71
  Not required for catalog-managed formats.
@@ -88,22 +92,31 @@ class DataSourceConfig:
88
92
  return bool(self.format and self.format.lower() in TABLE_PROVIDERS)
89
93
 
90
94
  def __post_init__(self):
95
+ if not self.table_name or not self.table_name.strip():
96
+ raise ValueError("Table name must be provided for storage type")
97
+
91
98
  if self.is_table_provider:
92
99
  if self.db_name is None:
93
100
  raise ValueError(f"Database name must be provided for table providers {TABLE_PROVIDERS}")
94
101
  return
95
102
 
103
+ if not self.format and not self.uri:
104
+ # Plain database table: no object store involved, so storage_type stays unset.
105
+ return
106
+
107
+ if not self.uri:
108
+ raise ValueError("URI must be provided when format is set")
109
+
96
110
  if self.storage_type is None:
97
111
  self.storage_type = self._extract_storage_type
98
112
 
99
- if self.storage_type is not None and (not self.table_name or not self.table_name.strip()):
100
- raise ValueError("Table name must be provided for storage type")
101
-
102
113
  @property
103
114
  def _extract_storage_type(self) -> StorageType | None:
104
115
  """Extract storage type."""
105
116
  if self.uri.startswith("s3://"):
106
117
  return StorageType.S3
118
+ if self.uri.startswith("gs://"):
119
+ return StorageType.GCS
107
120
  if self.uri.startswith("file://"):
108
121
  return StorageType.LOCAL
109
122
  raise ValueError(f"Unsupported storage type for URI: {self.uri}")
@@ -16,6 +16,7 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
+ import os
19
20
  from typing import TYPE_CHECKING, Any
20
21
 
21
22
  from datafusion import SessionContext
@@ -50,6 +51,12 @@ class DataFusionEngine(LoggingMixin):
50
51
  if not isinstance(datasource_config, DataSourceConfig):
51
52
  raise ValueError("datasource_config must be of type DataSourceConfig")
52
53
 
54
+ if not datasource_config.is_table_provider and datasource_config.storage_type is None:
55
+ raise ValueError(
56
+ f"DataSourceConfig for table {datasource_config.table_name!r} has no uri or format; "
57
+ "DataFusionEngine only registers object-store or catalog-managed sources."
58
+ )
59
+
53
60
  if not datasource_config.is_table_provider:
54
61
  if datasource_config.storage_type == StorageType.LOCAL:
55
62
  connection_config = None
@@ -146,6 +153,14 @@ class DataFusionEngine(LoggingMixin):
146
153
  conf[key] = conn.extra_dejson[key]
147
154
  return conf
148
155
 
156
+ def _get_gcp_extra_field(extra_dejson: dict[str, Any], field_name: str) -> Any:
157
+ # Older Airflow connection UIs wrote custom extra fields as
158
+ # extra__google_cloud_platform__<field_name> instead of the bare key; GoogleBaseHook
159
+ # still reads that legacy spelling as a fallback, so this must too.
160
+ if field_name in extra_dejson:
161
+ return extra_dejson[field_name]
162
+ return extra_dejson.get(f"extra__google_cloud_platform__{field_name}")
163
+
149
164
  match conn.conn_type:
150
165
  case "aws":
151
166
  try:
@@ -169,6 +184,26 @@ class DataFusionEngine(LoggingMixin):
169
184
  credentials = self._remove_none_values(credentials)
170
185
  extra_config = _fetch_extra_configs(["region", "endpoint"])
171
186
 
187
+ case "google_cloud_platform":
188
+ extra_dejson = conn.extra_dejson
189
+ for unsupported_field in ("key_secret_name", "credential_config_file", "impersonation_chain"):
190
+ if _get_gcp_extra_field(extra_dejson, unsupported_field):
191
+ raise ValueError(
192
+ f"Connection field {unsupported_field!r} is not supported for DataFusion "
193
+ "GCS access; only key_path, keyfile_dict, GOOGLE_APPLICATION_CREDENTIALS, or "
194
+ "ambient credentials (gcloud ADC file / metadata server) are used."
195
+ )
196
+ key_path = _get_gcp_extra_field(extra_dejson, "key_path") or None
197
+ keyfile_dict = _get_gcp_extra_field(extra_dejson, "keyfile_dict") or None
198
+ if key_path and keyfile_dict:
199
+ raise ValueError(
200
+ "The `keyfile_dict` and `key_path` fields are mutually exclusive. "
201
+ "Please provide only one value."
202
+ )
203
+ if not key_path and not keyfile_dict:
204
+ key_path = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")
205
+ credentials = self._remove_none_values({"key_path": key_path, "keyfile_dict": keyfile_dict})
206
+
172
207
  case _:
173
208
  raise ValueError(f"Unknown connection type {conn.conn_type}")
174
209
  return credentials, extra_config
@@ -16,7 +16,11 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
- from datafusion.object_store import AmazonS3, LocalFileSystem
19
+ import json
20
+ import tempfile
21
+ from pathlib import Path
22
+
23
+ from datafusion.object_store import AmazonS3, GoogleCloud, LocalFileSystem
20
24
 
21
25
  from airflow.providers.common.sql.config import ConnectionConfig, StorageType
22
26
  from airflow.providers.common.sql.datafusion.base import ObjectStorageProvider
@@ -34,7 +38,7 @@ class S3ObjectStorageProvider(ObjectStorageProvider):
34
38
  def create_object_store(self, path: str, connection_config: ConnectionConfig | None = None):
35
39
  """Create an S3 object store using DataFusion's AmazonS3."""
36
40
  if connection_config is None:
37
- raise ValueError("connection_config must be provided for %s", self.get_storage_type)
41
+ raise ValueError(f"connection_config must be provided for {self.get_storage_type.value}")
38
42
 
39
43
  try:
40
44
  credentials = connection_config.credentials
@@ -53,6 +57,56 @@ class S3ObjectStorageProvider(ObjectStorageProvider):
53
57
  return "s3://"
54
58
 
55
59
 
60
+ class GCSObjectStorageProvider(ObjectStorageProvider):
61
+ """GCS Object Storage Provider using DataFusion's GoogleCloud."""
62
+
63
+ @property
64
+ def get_storage_type(self) -> StorageType:
65
+ """Return the storage type."""
66
+ return StorageType.GCS
67
+
68
+ def create_object_store(self, path: str, connection_config: ConnectionConfig | None = None):
69
+ """Create a GCS object store using DataFusion's GoogleCloud."""
70
+ if connection_config is None:
71
+ raise ValueError(f"connection_config must be provided for {self.get_storage_type.value}")
72
+
73
+ credentials = connection_config.credentials
74
+ key_path = credentials.get("key_path")
75
+ keyfile_dict = credentials.get("keyfile_dict")
76
+ temp_key_path: str | None = None
77
+
78
+ try:
79
+ bucket = self.get_bucket(path)
80
+
81
+ if not key_path and keyfile_dict:
82
+ # GoogleCloud only accepts a file path, not inline JSON; safe to delete once
83
+ # constructed since the credentials are read only at construction time.
84
+ key_content = keyfile_dict if isinstance(keyfile_dict, str) else json.dumps(keyfile_dict)
85
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as key_file:
86
+ key_file.write(key_content)
87
+ temp_key_path = key_path = key_file.name
88
+
89
+ if key_path is not None and not Path(key_path).is_file():
90
+ raise FileNotFoundError(f"Service account key file not found: {key_path}")
91
+
92
+ gcs_store = GoogleCloud(bucket_name=bucket, service_account_path=key_path)
93
+ self.log.info("Created GCS object store for bucket %s", bucket)
94
+
95
+ return gcs_store
96
+
97
+ except BaseException as e:
98
+ # A bad key file panics as pyo3_runtime.PanicException, not a plain Exception.
99
+ raise ObjectStoreCreationException(f"Failed to create GCS object store: {e}")
100
+
101
+ finally:
102
+ if temp_key_path is not None:
103
+ Path(temp_key_path).unlink(missing_ok=True)
104
+
105
+ def get_scheme(self) -> str:
106
+ """Return the scheme for GCS."""
107
+ return "gs://"
108
+
109
+
56
110
  class LocalObjectStorageProvider(ObjectStorageProvider):
57
111
  """Local Object Storage Provider using DataFusion's LocalFileSystem."""
58
112
 
@@ -72,9 +126,10 @@ class LocalObjectStorageProvider(ObjectStorageProvider):
72
126
 
73
127
  def get_object_storage_provider(storage_type: StorageType) -> ObjectStorageProvider:
74
128
  """Get an object storage provider based on the storage type."""
75
- # TODO: Add support for GCS, Azure, HTTP: https://datafusion.apache.org/python/autoapi/datafusion/object_store/index.html
129
+ # TODO: Add support for Azure, HTTP: https://datafusion.apache.org/python/autoapi/datafusion/object_store/index.html
76
130
  providers: dict[StorageType, type] = {
77
131
  StorageType.S3: S3ObjectStorageProvider,
132
+ StorageType.GCS: GCSObjectStorageProvider,
78
133
  StorageType.LOCAL: LocalObjectStorageProvider,
79
134
  }
80
135
 
@@ -30,6 +30,10 @@ datasource_config_local = DataSourceConfig(
30
30
  conn_id="", table_name="users_data", uri="file:///path/to/", format="parquet"
31
31
  )
32
32
 
33
+ datasource_config_gcs = DataSourceConfig(
34
+ conn_id="google_cloud_default", table_name="users_data", uri="gs://bucket/path/", format="parquet"
35
+ )
36
+
33
37
  datasource_config_iceberg = DataSourceConfig(
34
38
  conn_id="iceberg_default",
35
39
  table_name="users_data",
@@ -72,6 +76,15 @@ with DAG(
72
76
 
73
77
  # [END howto_analytics_operator_with_s3]
74
78
 
79
+ # [START howto_analytics_operator_with_gcs]
80
+ analytics_with_gcs = AnalyticsOperator(
81
+ task_id="analytics_with_gcs",
82
+ datasource_configs=[datasource_config_gcs],
83
+ queries=["SELECT * FROM users_data", "SELECT count(*) FROM users_data"],
84
+ )
85
+ analytics_with_s3 >> analytics_with_gcs
86
+ # [END howto_analytics_operator_with_gcs]
87
+
75
88
  # [START howto_analytics_operator_with_local]
76
89
  analytics_with_local = AnalyticsOperator(
77
90
  task_id="analytics_with_local",
@@ -57,7 +57,7 @@ class SqlSensor(BaseSensorOperator):
57
57
  Should match the desired hook constructor params.
58
58
  """
59
59
 
60
- template_fields: Sequence[str] = ("sql", "hook_params", "parameters")
60
+ template_fields: Sequence[str] = ("sql", "hook_params", "parameters", "conn_id")
61
61
  template_ext: Sequence[str] = (".hql", ".sql")
62
62
  ui_color = "#7c7287"
63
63