diff --git a/.ci/e2e_integration_test/pipeline.yml b/.ci/e2e_integration_test/pipeline.yml
index 181e73577..8117a5418 100644
--- a/.ci/e2e_integration_test/pipeline.yml
+++ b/.ci/e2e_integration_test/pipeline.yml
@@ -42,6 +42,7 @@ steps:
     AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString)
     AzureWebJobsEventHubConnectionString: $(AzureWebJobsEventHubConnectionString)
     AzureWebJobsServiceBusConnectionString: $(AzureWebJobsServiceBusConnectionString)
+    AzureWebJobsSqlConnectionString: $(AzureWebJobsSqlConnectionString)
     AzureWebJobsEventGridTopicUri: $(AzureWebJobsEventGridTopicUri)
     AzureWebJobsEventGridConnectionKey: $(AzureWebJobsEventGridConnectionKey)
     PythonVersion: $(PYTHON_VERSION)
diff --git a/.ci/linux_devops_e2e_tests.sh b/.ci/linux_devops_e2e_tests.sh
index 83939d38e..7a5d5105e 100644
--- a/.ci/linux_devops_e2e_tests.sh
+++ b/.ci/linux_devops_e2e_tests.sh
@@ -5,6 +5,7 @@ export AzureWebJobsStorage=$LINUXSTORAGECONNECTIONSTRING
 export AzureWebJobsCosmosDBConnectionString=$LINUXCOSMOSDBCONNECTIONSTRING
 export AzureWebJobsEventHubConnectionString=$LINUXEVENTHUBCONNECTIONSTRING
 export AzureWebJobsServiceBusConnectionString=$LINUXSERVICEBUSCONNECTIONSTRING
+export AzureWebJobsSqlConnectionString=$LINUXSQLCONNECTIONSTRING
 export AzureWebJobsEventGridTopicUri=$LINUXEVENTGRIDTOPICURI
 export AzureWebJobsEventGridConnectionKey=$LINUXEVENTGRIDTOPICCONNECTIONKEY
 
diff --git a/.github/workflows/ci_e2e_workflow.yml b/.github/workflows/ci_e2e_workflow.yml
index 6b783c582..10b86bfc6 100644
--- a/.github/workflows/ci_e2e_workflow.yml
+++ b/.github/workflows/ci_e2e_workflow.yml
@@ -76,6 +76,7 @@ jobs:
           AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString36 }}
           AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString36 }}
           AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString36 }}
+          AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString36 }}
           AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString36 }}
           AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString36 }}
         run: |
@@ -87,6 +88,7 @@ jobs:
           AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString37 }}
           AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString37 }}
           AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString37 }}
+          AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString37 }}
           AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString37 }}
           AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString37 }}
         run: |
@@ -98,6 +100,7 @@ jobs:
           AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString38 }}
           AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString38 }}
           AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString38 }}
+          AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString38 }}
           AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString38 }}
           AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString38 }}
         run: |
@@ -109,6 +112,7 @@ jobs:
           AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString39 }}
           AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString39 }}
           AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString39 }}
+          AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString39 }}
           AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString39 }}
           AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString39 }}
         run: |
@@ -120,6 +124,7 @@ jobs:
           AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString310 }}
           AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString310 }}
           AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString310 }}
+          AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString310 }}
           AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString310 }}
           AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString310 }}
         run: |
diff --git a/azure_functions_worker/testutils.py b/azure_functions_worker/testutils.py
index 62e14a2e3..6641e2701 100644
--- a/azure_functions_worker/testutils.py
+++ b/azure_functions_worker/testutils.py
@@ -878,6 +878,10 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None):
         if servicebus:
             extra_env['AzureWebJobsServiceBusConnectionString'] = servicebus
 
+        sql = testconfig['azure'].get('sql_key')
+        if sql:
+            extra_env['AzureWebJobsSqlConnectionString'] = sql
+
         eventgrid_topic_uri = testconfig['azure'].get('eventgrid_topic_uri')
         if eventgrid_topic_uri:
             extra_env['AzureWebJobsEventGridTopicUri'] = eventgrid_topic_uri
diff --git a/setup.py b/setup.py
index 0ffe06ec9..9d4b50482 100644
--- a/setup.py
+++ b/setup.py
@@ -50,6 +50,8 @@
         Version="4.0.5" />
       
+      
       
diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py
new file mode 100644
index 000000000..a8e3c3b3b
--- /dev/null
+++ b/tests/endtoend/sql_functions/sql_input/__init__.py
@@ -0,0 +1,15 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+import json
+import azure.functions as func
+
+
+def main(req: func.HttpRequest, products: func.SqlRowList) -> func.HttpResponse:
+    rows = list(map(lambda r: json.loads(r.to_json()), products))
+
+    return func.HttpResponse(
+        json.dumps(rows),
+        status_code=200,
+        mimetype="application/json"
+    )
diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json
new file mode 100644
index 000000000..1c5e9d552
--- /dev/null
+++ b/tests/endtoend/sql_functions/sql_input/function.json
@@ -0,0 +1,27 @@
+{
+  "scriptFile": "__init__.py",
+  "bindings": [
+    {
+      "authLevel": "function",
+      "name": "req",
+      "type": "httpTrigger",
+      "direction": "in",
+      "methods": [
+        "get"
+      ]
+    },
+    {
+      "name": "$return",
+      "type": "http",
+      "direction": "out"
+    },
+    {
+      "name": "products",
+      "type": "sql",
+      "direction": "in",
+      "commandText": "SELECT * FROM Products",
+      "commandType": "Text",
+      "connectionStringSetting": "AzureWebJobsSqlConnectionString"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py
new file mode 100644
index 000000000..1a8226b51
--- /dev/null
+++ b/tests/endtoend/sql_functions/sql_output/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+import json
+import azure.functions as func
+
+
+def main(req: func.HttpRequest, r: func.Out[func.SqlRow]) -> func.HttpResponse:
+    body = json.loads(req.get_body())
+    row = func.SqlRow.from_dict(body)
+    r.set(row)
+
+    return func.HttpResponse(
+        body=req.get_body(),
+        status_code=201,
+        mimetype="application/json"
+    )
diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json
new file mode 100644
index 000000000..e58c18cfd
--- /dev/null
+++ b/tests/endtoend/sql_functions/sql_output/function.json
@@ -0,0 +1,26 @@
+{
+  "scriptFile": "__init__.py",
+  "bindings": [
+    {
+      "authLevel": "function",
+      "name": "req",
+      "type": "httpTrigger",
+      "direction": "in",
+      "methods": [
+        "post"
+      ]
+    },
+    {
+      "name": "$return",
+      "type": "http",
+      "direction": "out"
+    },
+    {
+      "name": "r",
+      "type": "sql",
+      "direction": "out",
+      "commandText": "[dbo].[Products]",
+      "connectionStringSetting": "AzureWebJobsSqlConnectionString"
+    }
+  ]
+}
diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py
new file mode 100644
index 000000000..ba8c442c4
--- /dev/null
+++ b/tests/endtoend/test_sql_functions.py
@@ -0,0 +1,24 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+import json
+
+from azure_functions_worker import testutils
+
+
+class TestSqlFunctions(testutils.WebHostTestCase):
+
+    @classmethod
+    def get_script_dir(cls):
+        return testutils.E2E_TESTS_FOLDER / 'sql_functions'
+
+    @testutils.retryable_test(3, 5)
+    def test_sql_output_and_input(self):
+        row = {"ProductId": 0, "Name": "test", "Cost": 100}
+        r = self.webhost.request('POST', 'sql_output',
+                                 data=json.dumps(row))
+        self.assertEqual(r.status_code, 201)
+
+        r = self.webhost.request('GET', 'sql_input')
+        self.assertEqual(r.status_code, 200)
+        expectedText = "[{\"ProductId\": 0, \"Name\": \"test\", \"Cost\": 100}]"
+        self.assertEqual(r.text, expectedText)