Repository: boto/botocore
Pull request #2606 (2022-02-03 22:56:45)

Adding recursion detection headers
Adding request headers for lambda recursion detection.

codecov-commenter: # [Codecov](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto) Report
> Merging [#2606](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto) (88d170d) into [develop](https://codecov.io/gh/boto/botocore/commit/46092d50885096efc69738c66d94fc283e6212c6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto) (46092d5) will **decrease** coverage by `0.06%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/boto/botocore/pull/2606/graphs/tree.svg?width=650&height=150&src=pr&token=QOLXw6XSza&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto)](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto)

```diff
@@             Coverage Diff             @@
##           develop    #2606      +/-   ##
===========================================
- Coverage    95.44%   95.37%   -0.07%     
===========================================
  Files           59       59              
  Lines        11849    11916      +67     
===========================================
+ Hits         11309    11365      +56     
- Misses         540      551      +11     
```


| [Impacted Files](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto) | Coverage Δ | |
|---|---|---|
| [botocore/handlers.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvaGFuZGxlcnMucHk=) | `96.26% <100.00%> (-0.09%)` | :arrow_down: |
| [botocore/compat.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvY29tcGF0LnB5) | `72.13% <0.00%> (-1.10%)` | :arrow_down: |
| [botocore/endpoint.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvZW5kcG9pbnQucHk=) | `97.77% <0.00%> (-0.92%)` | :arrow_down: |
| [botocore/retryhandler.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvcmV0cnloYW5kbGVyLnB5) | `98.78% <0.00%> (-0.58%)` | :arrow_down: |
| [botocore/retries/standard.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvcmV0cmllcy9zdGFuZGFyZC5weQ==) | `99.01% <0.00%> (-0.49%)` | :arrow_down: |
| [botocore/httpsession.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvaHR0cHNlc3Npb24ucHk=) | `90.94% <0.00%> (-0.42%)` | :arrow_down: |
| [botocore/response.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvcmVzcG9uc2UucHk=) | `92.77% <0.00%> (-0.19%)` | :arrow_down: |
| [botocore/utils.py](https://codecov.io/gh/boto/botocore/pull/2606/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto#diff-Ym90b2NvcmUvdXRpbHMucHk=) | `95.50% <0.00%> (-0.16%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto). Last update [46092d5...88d170d](https://codecov.io/gh/boto/botocore/pull/2606?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=boto).


Patch:
diff --git a/.changes/1.23.49.json b/.changes/1.23.49.json
new file mode 100644
index 000000000..d2bdfe1ee
--- /dev/null
+++ b/.changes/1.23.49.json
@@ -0,0 +1,17 @@
+[
+  {
+    "category": "``athena``",
+    "description": "You can now optionally specify the account ID that you expect to be the owner of your query results output location bucket in Athena. If the account ID of the query results bucket owner does not match the specified account ID, attempts to output to the bucket will fail with an S3 permissions error.",
+    "type": "api-change"
+  },
+  {
+    "category": "``rds``",
+    "description": "updates for RDS Custom for Oracle 12.1 support",
+    "type": "api-change"
+  },
+  {
+    "category": "``lakeformation``",
+    "description": "Add support for calling Update Table Objects without a TransactionId.",
+    "type": "api-change"
+  }
+]
\ No newline at end of file
diff --git a/.changes/next-release/enhancement-RequestHeader-82118.json b/.changes/next-release/enhancement-RequestHeader-82118.json
new file mode 100644
index 000000000..f6641feda
--- /dev/null
+++ b/.changes/next-release/enhancement-RequestHeader-82118.json
@@ -0,0 +1,5 @@
+{
+  "type": "enhancement",
+  "category": "Lambda Request Header",
+  "description": "Adding request header for Lambda recursion detection."
+}
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index df44d64fd..22d59f071 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,14 @@
 CHANGELOG
 =========
 
+1.23.49
+=======
+
+* api-change:``athena``: You can now optionally specify the account ID that you expect to be the owner of your query results output location bucket in Athena. If the account ID of the query results bucket owner does not match the specified account ID, attempts to output to the bucket will fail with an S3 permissions error.
+* api-change:``rds``: updates for RDS Custom for Oracle 12.1 support
+* api-change:``lakeformation``: Add support for calling Update Table Objects without a TransactionId.
+
+
 1.23.48
 =======
 
diff --git a/botocore/__init__.py b/botocore/__init__.py
index 5437ee67d..9b398f55d 100644
--- a/botocore/__init__.py
+++ b/botocore/__init__.py
@@ -16,7 +16,7 @@ import os
 import re
 import logging
 
-__version__ = '1.23.48'
+__version__ = '1.23.49'
 
 
 class NullHandler(logging.Handler):
diff --git a/botocore/configloader.py b/botocore/configloader.py
index 5e0002ce4..f516ea111 100644
--- a/botocore/configloader.py
+++ b/botocore/configloader.py
@@ -147,9 +147,9 @@ def raw_config_parse(config_filename, parse_subsections=True):
         cp = six.moves.configparser.RawConfigParser()
         try:
             cp.read([path])
-        except (six.moves.configparser.Error, UnicodeDecodeError):
+        except (six.moves.configparser.Error, UnicodeDecodeError) as e:
             raise botocore.exceptions.ConfigParseError(
-                path=_unicode_path(path))
+                path=_unicode_path(path), error=e) from None
         else:
             for section in cp.sections():
                 config[section] = {}
@@ -161,9 +161,9 @@ def raw_config_parse(config_filename, parse_subsections=True):
                         # of nesting for now.
                         try:
                             config_value = _parse_nested(config_value)
-                        except ValueError:
+                        except ValueError as e:
                             raise botocore.exceptions.ConfigParseError(
-                                path=_unicode_path(path))
+                                path=_unicode_path(path), error=e) from None
                     config[section][option] = config_value
     return config
 
diff --git a/botocore/data/athena/2017-05-18/service-2.json b/botocore/data/athena/2017-05-18/service-2.json
index dc7abff11..8e5b3fc34 100644
--- a/botocore/data/athena/2017-05-18/service-2.json
+++ b/botocore/data/athena/2017-05-18/service-2.json
@@ -240,7 +240,7 @@
         {"shape":"InternalServerException"},
         {"shape":"InvalidRequestException"}
       ],
-      "documentation":"<p>Streams the results of a single query execution specified by <code>QueryExecutionId</code> from the Athena query results location in Amazon S3. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/querying.html\">Query Results</a> in the <i>Amazon Athena User Guide</i>. This request does not execute the query but returns results. Use <a>StartQueryExecution</a> to run a query.</p> <p>To stream query results successfully, the IAM principal with permission to call <code>GetQueryResults</code> also must have permissions to the Amazon S3 <code>GetObject</code> action for the Athena query results location.</p> <important> <p>IAM principals with permission to the Amazon S3 <code>GetObject</code> action for the query results location are able to retrieve query results from Amazon S3 even if permission to the <code>GetQueryResults</code> action is denied. To restrict user or role access, ensure that Amazon S3 permissions to the Athena query location are denied.</p> </important>"
+      "documentation":"<p>Streams the results of a single query execution specified by <code>QueryExecutionId</code> from the Athena query results location in Amazon S3. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/querying.html\">Query Results</a> in the <i>Amazon Athena User Guide</i>. This request does not execute the query but returns results. Use <a>StartQueryExecution</a> to run a query.</p> <p>If the original query execution ran using an <a>ResultConfiguration$ExpectedBucketOwner</a> setting, the setting also applies to Amazon S3 read operations when <code>GetQueryResults</code> is called. If an expected bucket owner has been specified and the query results are in an Amazon S3 bucket whose owner account ID is different from the expected bucket owner, the <code>GetQueryResults</code> call fails with an Amazon S3 permissions error.</p> <p>To stream query results successfully, the IAM principal with permission to call <code>GetQueryResults</code> also must have permissions to the Amazon S3 <code>GetObject</code> action for the Athena query results location.</p> <important> <p>IAM principals with permission to the Amazon S3 <code>GetObject</code> action for the query results location are able to retrieve query results from Amazon S3 even if permission to the <code>GetQueryResults</code> action is denied. To restrict user or role access, ensure that Amazon S3 permissions to the Athena query location are denied.</p> </important>"
     },
     "GetTableMetadata":{
       "name":"GetTableMetadata",
@@ -1867,6 +1867,10 @@
         "EncryptionConfiguration":{
           "shape":"EncryptionConfiguration",
           "documentation":"<p>If query results are encrypted in Amazon S3, indicates the encryption option used (for example, <code>SSE-KMS</code> or <code>CSE-KMS</code>) and key information. This is a client-side setting. If workgroup settings override client-side settings, then the query uses the encryption configuration that is specified for the workgroup, and also uses the location for storing query results specified in the workgroup. See <a>WorkGroupConfiguration$EnforceWorkGroupConfiguration</a> and <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
+        },
+        "ExpectedBucketOwner":{
+          "shape":"String",
+          "documentation":"<p>The Amazon Web Services account ID that you expect to be the owner of the Amazon S3 bucket specified by <a>ResultConfiguration$OutputLocation</a>. If set, Athena uses the value for <code>ExpectedBucketOwner</code> when it makes Amazon S3 calls to your specified output location. If the <code>ExpectedBucketOwner</code> Amazon Web Services account ID does not match the actual owner of the Amazon S3 bucket, the call fails with a permissions error.</p> <p>This is a client-side setting. If workgroup settings override client-side settings, then the query uses the <code>ExpectedBucketOwner</code> setting that is specified for the workgroup, and also uses the location for storing query results specified in the workgroup. See <a>WorkGroupConfiguration$EnforceWorkGroupConfiguration</a> and <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
         }
       },
       "documentation":"<p>The location in Amazon S3 where query results are stored and the encryption option, if any, used for query results. These are known as \"client-side settings\". If workgroup settings override client-side settings, then the query uses the workgroup settings.</p>"
@@ -1880,7 +1884,7 @@
         },
         "RemoveOutputLocation":{
           "shape":"BoxedBoolean",
-          "documentation":"<p>If set to \"true\", indicates that the previously-specified query results location (also known as a client-side setting) for queries in this workgroup should be ignored and set to null. If set to \"false\" or not set, and a value is present in the <code>OutputLocation</code> in <code>ResultConfigurationUpdates</code> (the client-side setting), the <code>OutputLocation</code> in the workgroup's <code>ResultConfiguration</code> will be updated with the new value. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
+          "documentation":"<p>If set to \"true\", indicates that the previously-specified query results location (also known as a client-side setting) for queries in this workgroup should be ignored and set to null. If set to \"false\" or not set, and a value is present in the <code>OutputLocation</code> in <code>ResultConfigurationUpdates</code> (the client-side setting), the <code>OutputLocation</code> in the workgroup's <code>ResultConfiguration</code> is updated with the new value. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
         },
         "EncryptionConfiguration":{
           "shape":"EncryptionConfiguration",
@@ -1888,7 +1892,15 @@
         },
         "RemoveEncryptionConfiguration":{
           "shape":"BoxedBoolean",
-          "documentation":"<p>If set to \"true\", indicates that the previously-specified encryption configuration (also known as the client-side setting) for queries in this workgroup should be ignored and set to null. If set to \"false\" or not set, and a value is present in the <code>EncryptionConfiguration</code> in <code>ResultConfigurationUpdates</code> (the client-side setting), the <code>EncryptionConfiguration</code> in the workgroup's <code>ResultConfiguration</code> will be updated with the new value. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
+          "documentation":"<p>If set to \"true\", indicates that the previously-specified encryption configuration (also known as the client-side setting) for queries in this workgroup should be ignored and set to null. If set to \"false\" or not set, and a value is present in the <code>EncryptionConfiguration</code> in <code>ResultConfigurationUpdates</code> (the client-side setting), the <code>EncryptionConfiguration</code> in the workgroup's <code>ResultConfiguration</code> is updated with the new value. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
+        },
+        "ExpectedBucketOwner":{
+          "shape":"String",
+          "documentation":"<p>The Amazon Web Services account ID that you expect to be the owner of the Amazon S3 bucket specified by <a>ResultConfiguration$OutputLocation</a>. If set, Athena uses the value for <code>ExpectedBucketOwner</code> when it makes Amazon S3 calls to your specified output location. If the <code>ExpectedBucketOwner</code> Amazon Web Services account ID does not match the actual owner of the Amazon S3 bucket, the call fails with a permissions error.</p> <p>If workgroup settings override client-side settings, then the query uses the <code>ExpectedBucketOwner</code> setting that is specified for the workgroup, and also uses the location for storing query results specified in the workgroup. See <a>WorkGroupConfiguration$EnforceWorkGroupConfiguration</a> and <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
+        },
+        "RemoveExpectedBucketOwner":{
+          "shape":"BoxedBoolean",
+          "documentation":"<p>If set to \"true\", removes the Amazon Web Services account ID previously specified for <a>ResultConfiguration$ExpectedBucketOwner</a>. If set to \"false\" or not set, and a value is present in the <code>ExpectedBucketOwner</code> in <code>ResultConfigurationUpdates</code> (the client-side setting), the <code>ExpectedBucketOwner</code> in the workgroup's <code>ResultConfiguration</code> is updated with the new value. For more information, see <a href=\"https://docs.aws.amazon.com/athena/latest/ug/workgroups-settings-override.html\">Workgroup Settings Override Client-Side Settings</a>.</p>"
         }
       },
       "documentation":"<p>The information about the updates in the query results, such as output location and encryption configuration for the query results.</p>"
diff --git a/botocore/data/endpoints.json b/botocore/data/endpoints.json
index 534d655f2..92dd063f3 100644
--- a/botocore/data/endpoints.json
+++ b/botocore/data/endpoints.json
@@ -2676,6 +2676,7 @@
       },
       "connect" : {
         "endpoints" : {
+          "af-south-1" : { },
           "ap-northeast-1" : { },
           "ap-northeast-2" : { },
           "ap-southeast-1" : { },
@@ -12989,6 +12990,12 @@
           "cn-northwest-1" : { }
         }
       },
+      "codepipeline" : {
+        "endpoints" : {
+          "cn-north-1" : { },
+          "cn-northwest-1" : { }
+        }
+      },
       "cognito-identity" : {
         "endpoints" : {
           "cn-north-1" : { }
diff --git a/botocore/data/lakeformation/2017-03-31/service-2.json b/botocore/data/lakeformation/2017-03-31/service-2.json
index 4e13a548f..a0248d6b7 100644
--- a/botocore/data/lakeformation/2017-03-31/service-2.json
+++ b/botocore/data/lakeformation/2017-03-31/service-2.json
@@ -3422,7 +3422,6 @@
       "required":[
         "DatabaseName",
         "TableName",
-        "TransactionId",
         "WriteOperations"
       ],
       "members":{
diff --git a/botocore/data/rds/2014-10-31/service-2.json b/botocore/data/rds/2014-10-31/service-2.json
index 97c4e99b3..c45b0bbb6 100644
--- a/botocore/data/rds/2014-10-31/service-2.json
+++ b/botocore/data/rds/2014-10-31/service-2.json
@@ -276,7 +276,7 @@
         {"shape":"CustomDBEngineVersionQuotaExceededFault"},
         {"shape":"KMSKeyNotAccessibleFault"}
       ],
-      "documentation":"<p>Creates a custom DB engine version (CEV). A CEV is a binary volume snapshot of a database engine and specific AMI. The only supported engine is Oracle Database 19c Enterprise Edition with the January 2021 or later RU/RUR.</p> <p>Amazon RDS, which is a fully managed service, supplies the Amazon Machine Image (AMI) and database software. The Amazon RDS database software is preinstalled, so you need only select a DB engine and version, and create your database. With Amazon RDS Custom for Oracle, you upload your database installation files in Amazon S3.</p> <p>When you create a custom engine version, you specify the files in a JSON document called a CEV manifest. This document describes installation .zip files stored in Amazon S3. RDS Custom creates your CEV from the installation files that you provided. This service model is called Bring Your Own Media (BYOM).</p> <p>Creation takes approximately two hours. If creation fails, RDS Custom issues <code>RDS-EVENT-0196</code> with the message <code>Creation failed for custom engine version</code>, and includes details about the failure. For example, the event prints missing files.</p> <p>After you create the CEV, it is available for use. You can create multiple CEVs, and create multiple RDS Custom instances from any CEV. You can also change the status of a CEV to make it available or inactive.</p> <note> <p>The MediaImport service that imports files from Amazon S3 to create CEVs isn't integrated with Amazon Web Services CloudTrail. If you turn on data logging for Amazon RDS in CloudTrail, calls to the <code>CreateCustomDbEngineVersion</code> event aren't logged. However, you might see calls from the API gateway that accesses your Amazon S3 bucket. These calls originate from the MediaImport service for the <code>CreateCustomDbEngineVersion</code> event.</p> </note> <p>For more information, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-cev.html#custom-cev.create\"> Creating a CEV</a> in the <i>Amazon RDS User Guide</i>.</p>"
+      "documentation":"<p>Creates a custom DB engine version (CEV). A CEV is a binary volume snapshot of a database engine and specific AMI. The supported engines are the following:</p> <ul> <li> <p> Oracle Database 12.1 Enterprise Edition with the January 2021 or later RU/RUR</p> </li> <li> <p> Oracle Database 19c Enterprise Edition with the January 2021 or later RU/RUR</p> </li> </ul> <p>Amazon RDS, which is a fully managed service, supplies the Amazon Machine Image (AMI) and database software. The Amazon RDS database software is preinstalled, so you need only select a DB engine and version, and create your database. With Amazon RDS Custom for Oracle, you upload your database installation files in Amazon S3.</p> <p>When you create a custom engine version, you specify the files in a JSON document called a CEV manifest. This document describes installation .zip files stored in Amazon S3. RDS Custom creates your CEV from the installation files that you provided. This service model is called Bring Your Own Media (BYOM).</p> <p>Creation takes approximately two hours. If creation fails, RDS Custom issues <code>RDS-EVENT-0196</code> with the message <code>Creation failed for custom engine version</code>, and includes details about the failure. For example, the event prints missing files.</p> <p>After you create the CEV, it is available for use. You can create multiple CEVs, and create multiple RDS Custom instances from any CEV. You can also change the status of a CEV to make it available or inactive.</p> <note> <p>The MediaImport service that imports files from Amazon S3 to create CEVs isn't integrated with Amazon Web Services CloudTrail. If you turn on data logging for Amazon RDS in CloudTrail, calls to the <code>CreateCustomDbEngineVersion</code> event aren't logged. However, you might see calls from the API gateway that accesses your Amazon S3 bucket. These calls originate from the MediaImport service for the <code>CreateCustomDbEngineVersion</code> event.</p> </note> <p>For more information, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-cev.html#custom-cev.create\"> Creating a CEV</a> in the <i>Amazon RDS User Guide</i>.</p>"
     },
     "CreateDBCluster":{
       "name":"CreateDBCluster",
@@ -3474,7 +3474,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>A DB subnet group to associate with this DB cluster.</p> <p>This setting is required to create a Multi-AZ DB cluster.</p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mySubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
+          "documentation":"<p>A DB subnet group to associate with this DB cluster.</p> <p>This setting is required to create a Multi-AZ DB cluster.</p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mydbsubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
         },
         "Engine":{
           "shape":"String",
@@ -3740,7 +3740,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>A DB subnet group to associate with this DB instance.</p> <p>If there is no DB subnet group, then it is a non-VPC DB instance.</p>"
+          "documentation":"<p>A DB subnet group to associate with this DB instance.</p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "PreferredMaintenanceWindow":{
           "shape":"String",
@@ -3955,7 +3955,7 @@
         "Tags":{"shape":"TagList"},
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>Specifies a DB subnet group for the DB instance. The new DB instance is created in the VPC associated with the DB subnet group. If no DB subnet group is specified, then the new DB instance isn't created in a VPC.</p> <p>Constraints:</p> <ul> <li> <p>Can only be specified if the source DB instance identifier specifies a DB instance in another Amazon Web Services Region.</p> </li> <li> <p>If supplied, must match the name of an existing DBSubnetGroup.</p> </li> <li> <p>The specified DB subnet group must be in the same Amazon Web Services Region in which the operation is running.</p> </li> <li> <p>All read replicas in one Amazon Web Services Region that are created from the same source DB instance must either:&gt;</p> <ul> <li> <p>Specify DB subnet groups from the same VPC. All these read replicas are created in the same VPC.</p> </li> <li> <p>Not specify a DB subnet group. All these read replicas are created outside of any VPC.</p> </li> </ul> </li> </ul> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>Specifies a DB subnet group for the DB instance. The new DB instance is created in the VPC associated with the DB subnet group. If no DB subnet group is specified, then the new DB instance isn't created in a VPC.</p> <p>Constraints:</p> <ul> <li> <p>Can only be specified if the source DB instance identifier specifies a DB instance in another Amazon Web Services Region.</p> </li> <li> <p>If supplied, must match the name of an existing DBSubnetGroup.</p> </li> <li> <p>The specified DB subnet group must be in the same Amazon Web Services Region in which the operation is running.</p> </li> <li> <p>All read replicas in one Amazon Web Services Region that are created from the same source DB instance must either:&gt;</p> <ul> <li> <p>Specify DB subnet groups from the same VPC. All these read replicas are created in the same VPC.</p> </li> <li> <p>Not specify a DB subnet group. All these read replicas are created outside of any VPC.</p> </li> </ul> </li> </ul> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "VpcSecurityGroupIds":{
           "shape":"VpcSecurityGroupIdList",
@@ -4248,7 +4248,7 @@
       "members":{
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The name for the DB subnet group. This value is stored as a lowercase string.</p> <p>Constraints: Must contain no more than 255 letters, numbers, periods, underscores, spaces, or hyphens. Must not be default.</p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>The name for the DB subnet group. This value is stored as a lowercase string.</p> <p>Constraints:</p> <ul> <li> <p>Must contain no more than 255 letters, numbers, periods, underscores, spaces, or hyphens.</p> </li> <li> <p>Must not be default.</p> </li> <li> <p>First character must be a letter.</p> </li> </ul> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "DBSubnetGroupDescription":{
           "shape":"String",
@@ -7348,7 +7348,7 @@
       "members":{
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The name of the database subnet group to delete.</p> <note> <p>You can't delete the default subnet group.</p> </note> <p>Constraints:</p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>The name of the database subnet group to delete.</p> <note> <p>You can't delete the default subnet group.</p> </note> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         }
       },
       "documentation":"<p/>"
@@ -10227,7 +10227,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The new DB subnet group for the DB instance. You can use this parameter to move your DB instance to a different VPC. If your DB instance isn't in a VPC, you can also use this parameter to move your DB instance into a VPC. For more information, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Non-VPC2VPC\">Working with a DB instance in a VPC</a> in the <i>Amazon RDS User Guide</i>. </p> <p>Changing the subnet group causes an outage during the change. The change is applied during the next maintenance window, unless you enable <code>ApplyImmediately</code>.</p> <p> This parameter doesn't apply to RDS Custom.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mySubnetGroup</code> </p>"
+          "documentation":"<p>The new DB subnet group for the DB instance. You can use this parameter to move your DB instance to a different VPC. If your DB instance isn't in a VPC, you can also use this parameter to move your DB instance into a VPC. For more information, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.WorkingWithRDSInstanceinaVPC.html#USER_VPC.Non-VPC2VPC\">Working with a DB instance in a VPC</a> in the <i>Amazon RDS User Guide</i>. </p> <p>Changing the subnet group causes an outage during the change. The change is applied during the next maintenance window, unless you enable <code>ApplyImmediately</code>.</p> <p> This parameter doesn't apply to RDS Custom.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "DBSecurityGroups":{
           "shape":"DBSecurityGroupNameList",
@@ -10601,7 +10601,7 @@
       "members":{
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The name for the DB subnet group. This value is stored as a lowercase string. You can't modify the default subnet group. </p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>The name for the DB subnet group. This value is stored as a lowercase string. You can't modify the default subnet group. </p> <p>Constraints: Must match the name of an existing DBSubnetGroup. Must not be default.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "DBSubnetGroupDescription":{
           "shape":"String",
@@ -12184,7 +12184,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>A DB subnet group to associate with the restored DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup. </p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>A DB subnet group to associate with the restored DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "Engine":{
           "shape":"String",
@@ -12317,7 +12317,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The name of the DB subnet group to use for the new DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DB subnet group.</p> <p>Example: <code>mySubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
+          "documentation":"<p>The name of the DB subnet group to use for the new DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DB subnet group.</p> <p>Example: <code>mydbsubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
         },
         "DatabaseName":{
           "shape":"String",
@@ -12437,7 +12437,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The DB subnet group name to use for the new DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mySubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
+          "documentation":"<p>The DB subnet group name to use for the new DB cluster.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p> <p>Valid for: Aurora DB clusters and Multi-AZ DB clusters</p>"
         },
         "OptionGroupName":{
           "shape":"String",
@@ -12546,7 +12546,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The DB subnet group name to use for the new instance.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>The DB subnet group name to use for the new instance.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "MultiAZ":{
           "shape":"BooleanOptional",
@@ -12603,7 +12603,7 @@
         },
         "CopyTagsToSnapshot":{
           "shape":"BooleanOptional",
-          "documentation":"<p>A value that indicates whether to copy all tags from the restored DB instance to snapshots of the DB instance. By default, tags are not copied.</p>"
+          "documentation":"<p>A value that indicates whether to copy all tags from the restored DB instance to snapshots of the DB instance.</p> <p>In most cases, tags aren't copied by default. However, when you restore a DB instance from a DB snapshot, RDS checks whether you specify new tags. If yes, the new tags are added to the restored DB instance. If there are no new tags, RDS looks for the tags from the source DB instance for the DB snapshot, and then adds those tags to the restored DB instance.</p> <p>For more information, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Tagging.html#USER_Tagging.CopyTags\"> Copying tags to DB instance snapshots</a> in the <i>Amazon RDS User Guide</i>.</p>"
         },
         "DomainIAMRoleName":{
           "shape":"String",
@@ -12639,7 +12639,7 @@
         },
         "CustomIamInstanceProfile":{
           "shape":"String",
-          "documentation":"<p>The instance profile associated with the underlying Amazon EC2 instance of an RDS Custom DB instance. The instance profile must meet the following requirements:</p> <ul> <li> <p>The profile must exist in your account.</p> </li> <li> <p>The profile must have an IAM role that Amazon EC2 has permissions to assume.</p> </li> <li> <p>The instance profile name and the associated IAM role name must start with the prefix <code>AWSRDSCustom</code>.</p> </li> </ul> <p>For the list of permissions required for the IAM role, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-setup-orcl.html#custom-setup-orcl.iam-vpc\"> Configure IAM and your VPC</a> in the <i>Amazon Relational Database Service User Guide</i>.</p> <p>This setting is required for RDS Custom.</p>"
+          "documentation":"<p>The instance profile associated with the underlying Amazon EC2 instance of an RDS Custom DB instance. The instance profile must meet the following requirements:</p> <ul> <li> <p>The profile must exist in your account.</p> </li> <li> <p>The profile must have an IAM role that Amazon EC2 has permissions to assume.</p> </li> <li> <p>The instance profile name and the associated IAM role name must start with the prefix <code>AWSRDSCustom</code>.</p> </li> </ul> <p>For the list of permissions required for the IAM role, see <a href=\"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/custom-setup-orcl.html#custom-setup-orcl.iam-vpc\"> Configure IAM and your VPC</a> in the <i>Amazon RDS User Guide</i>.</p> <p>This setting is required for RDS Custom.</p>"
         },
         "BackupTarget":{
           "shape":"String",
@@ -12708,7 +12708,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>A DB subnet group to associate with this DB instance.</p>"
+          "documentation":"<p>A DB subnet group to associate with this DB instance.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "PreferredMaintenanceWindow":{
           "shape":"String",
@@ -12884,7 +12884,7 @@
         },
         "DBSubnetGroupName":{
           "shape":"String",
-          "documentation":"<p>The DB subnet group name to use for the new instance.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mySubnetgroup</code> </p>"
+          "documentation":"<p>The DB subnet group name to use for the new instance.</p> <p>Constraints: If supplied, must match the name of an existing DBSubnetGroup.</p> <p>Example: <code>mydbsubnetgroup</code> </p>"
         },
         "MultiAZ":{
           "shape":"BooleanOptional",
@@ -13055,7 +13055,7 @@
       "type":"structure",
       "members":{
       },
-      "documentation":"<p>SNS has responded that there is a problem with the SND topic specified.</p>",
+      "documentation":"<p>SNS has responded that there is a problem with the SNS topic specified.</p>",
       "error":{
         "code":"SNSInvalidTopic",
         "httpStatusCode":400,
diff --git a/botocore/handlers.py b/botocore/handlers.py
index 65bd6a0b2..14f16943a 100644
--- a/botocore/handlers.py
+++ b/botocore/handlers.py
@@ -16,6 +16,7 @@
 This module contains builtin handlers for events emitted by botocore.
 """
 
+import os
 import base64
 import logging
 import copy
@@ -26,7 +27,7 @@ import uuid
 from botocore.compat import (
     unquote, json, six, unquote_str, ensure_bytes, get_md5,
     OrderedDict, urlsplit, urlunsplit, XMLParseError,
-    ETree,
+    ETree, quote,
 )
 from botocore.docs.utils import AutoPopulatedParam
 from botocore.docs.utils import HideParamFromOperations
@@ -84,6 +85,15 @@ def handle_service_name_alias(service_name, **kwargs):
     return SERVICE_NAME_ALIASES.get(service_name, service_name)
 
 
+def add_recursion_detection_header(params, **kwargs):
+    has_lambda_name = 'AWS_LAMBDA_FUNCTION_NAME' in os.environ
+    trace_id = os.environ.get('_X_AMZ_TRACE_ID')
+    if has_lambda_name and trace_id:
+        headers = params['headers']
+        if 'X-Amzn-Trace-Id' not in headers:
+            headers['X-Amzn-Trace-Id'] = quote(trace_id)
+
+
 def escape_xml_payload(params, **kwargs):
     # Replace \r and \n with the escaped sequence over the whole XML document
     # to avoid linebreak normalization modifying customer input when the
@@ -1001,6 +1011,7 @@ BUILTIN_HANDLERS = [
     ('docs.*.s3.CopyObject.complete-section', document_copy_source_form),
     ('docs.*.s3.UploadPartCopy.complete-section', document_copy_source_form),
 
+    ('before-call', add_recursion_detection_header),
     ('before-call.s3', add_expect_header),
     ('before-call.glacier', add_glacier_version),
     ('before-call.apigateway', add_accept_header),
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 3d6a4d70f..0d403b99f 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -54,7 +54,7 @@ copyright = u'2013, Mitch Garnaat'
 # The short X.Y version.
 version = '1.23.'
 # The full version, including alpha/beta/rc tags.
-release = '1.23.48'
+release = '1.23.49'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


Test patch:
diff --git a/tests/unit/test_handlers.py b/tests/unit/test_handlers.py
index 527434229..201796902 100644
--- a/tests/unit/test_handlers.py
+++ b/tests/unit/test_handlers.py
@@ -17,6 +17,7 @@ import base64
 import copy
 import os
 import json
+import pytest
 
 import botocore
 import botocore.session
@@ -1368,3 +1369,24 @@ class TestPrependToHost(unittest.TestCase):
     def test_does_validate_host_with_illegal_char(self):
         with self.assertRaises(ParamValidationError):
             self._prepend_to_host('https://example.com/path', 'host#name')
+
+
+@pytest.mark.parametrize(
+    'environ, header_before, header_after',
+    [
+        ({'AWS_LAMBDA_FUNCTION_NAME': 'foo'}, {}, {}),
+        ({'_X_AMZ_TRACE_ID': 'bar'}, {}, {}),
+        ({'AWS_LAMBDA_FUNCTION_NAME': 'foo', '_X_AMZ_TRACE_ID': 'bar'},
+         {}, {'X-Amzn-Trace-Id': 'bar'}),
+        ({'AWS_LAMBDA_FUNCTION_NAME': 'foo', '_X_AMZ_TRACE_ID': 'bar'},
+         {'X-Amzn-Trace-Id': 'fizz'}, {'X-Amzn-Trace-Id': 'fizz'}),
+        ({'AWS_LAMBDA_FUNCTION_NAME': 'foo',
+          '_X_AMZ_TRACE_ID': 'first\nsecond'},
+         {}, {'X-Amzn-Trace-Id': 'first%0Asecond'})
+    ]
+)
+def test_add_recursion_detection_header(environ, header_before, header_after):
+    request_dict = {'headers': header_before}
+    with mock.patch('os.environ', environ):
+        handlers.add_recursion_detection_header(request_dict)
+        assert request_dict['headers'] == header_after