Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/4478.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
tencentcloud_dlc_internal_table
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ require (
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dbbrain v1.3.26
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dc v1.0.633
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dcdb v1.0.673
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.156
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.170
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.3.124
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/domain v1.0.414
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dts v1.3.153
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1028,8 +1028,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dc v1.0.633 h1:Ul5iNhXo
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dc v1.0.633/go.mod h1:tc6Hvf03M1cBtMC1IKSa5mlOn3kpxWOwhWU1fRy+KEE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dcdb v1.0.673 h1:YyjGLjvPDKNlpbGt89WLFif7TjId0fHzcrGOaHSQRNQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dcdb v1.0.673/go.mod h1:hXPMop1kJFqAvHj+7TyxxxXS/HGUP4SuKx5gGoAl0Zc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.156 h1:78OGMhGeGrjml9AAqmeqPfCQXiLz/tM9EjoR3+dVPL0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.156/go.mod h1:/CbfEeeufmoAvXiehDi34baOOE9j/tewKIEq69PxxGY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.170 h1:VsoGS3U0Fk7wBBlnoUb2GPtEqD98rEVd0l6bYKfYfWw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dlc v1.3.170/go.mod h1:LE2EeLNcCSlSHFP8WuPBtZLPsGmZOEKqEokr/WXxiNQ=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.3.124 h1:atQm8S0BLrr3E+Yn2383KQUEEXpQ6z31MsprAKzMDDE=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.3.124/go.mod h1:zxlW9EAn0Hqx6Eub6MMocnHZz/gHundkyI7CdbIhU3o=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/domain v1.0.414 h1:egwjvOEUKBaxsoRVn/YSEhp2E8qdh77Ous9A/wftDo0=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-02
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Context

The DLC (Data Lake Compute) product exposes `GenerateInternalTable` to create a managed/internal table (Iceberg, Hive, etc.) and `DeleteTable` / `AlterTableComment` / `DescribeTable` to manage it. There is no single "table id" returned by the create API; a table is uniquely identified within a datasource by `DatabaseName` + `TableName` (and optionally `DatasourceConnectionName`). The provider already hosts DLC resources under `tencentcloud/services/dlc/` (e.g. `tencentcloud_dlc_data_engine`, `tencentcloud_dlc_user`), so the new resource follows the same package and the established `tencentcloud_igtm_strategy` code style.

All four APIs are synchronous: `GenerateInternalTable` returns the generated SQL (`Execution.SQL`) and an `IsTIcebergSql` flag, but no task/job id. No async polling is required.

## Goals / Non-Goals

**Goals:**
- Provide a `tencentcloud_dlc_internal_table` resource with full CRUD: create via `GenerateInternalTable`, read via `DescribeTable`, update via `AlterTableComment`, delete via `DeleteTable`.
- Surface every cloud-API parameter mapped in the proposal as a Terraform schema field, including the deeply nested smart-governance policy tree (`smart_policy` → `base_info` / `policy` → `resources` / `written` / `lifecycle` / `index` / `change_table` / `table_expiration`).
- Support import using the composite `database_name#table_name` id.
- Keep the implementation consistent with provider conventions: `tccommon` retry on reads, nil-checks before `d.Set`, `NonRetryableError` on empty create results, composite id with `tccommon.FILED_SP`.

**Non-Goals:**
- No support for altering columns/partitions/properties after creation. `AlterTableComment` only updates `TableBaseInfo` (comment/type/format/etc.); structural changes (columns, partitions, properties, smart policy) are therefore `ForceNew` to avoid drift, because no cloud API exists to update them in place. (This matches the code-gen requirement: only fields present in the update API are mutable; the rest are immutable.)
- No async-job polling (no async APIs involved).
- No data-source variant in this change.

## Decisions

### 1. Composite resource ID = `database_name#table_name`
**Rationale**: `GenerateInternalTable` does not return an opaque id; `DescribeTable` queries by `DatabaseName` + `TableName`. Joining them with `tccommon.FILED_SP` (`#`) gives a stable, import-friendly id.
**Alternative considered**: Including `datasource_connection_name` in the id. Rejected because it is optional and frequently empty; keeping the id to the two required keys is simpler and matches how `DescribeTable` is typically called. `datasource_connection_name` remains a regular (non-id) schema field passed to each API.

### 2. Update scope limited to `AlterTableComment`-editable fields
**Rationale**: The only update API is `AlterTableComment`, which accepts a `TableBaseInfo`. Only the scalar `TableBaseInfo` fields that the cloud API actually honors for an alter are mutable in Update; everything else (columns, partitions, properties, upsert_keys, smart_policy, primary_keys) is `ForceNew`. Per the code-gen rules, fields not supported by the update API must not appear in the update path.
**Alternative considered**: Calling `GenerateInternalTable` again to "recreate". Rejected — that would be delete+create, which Terraform already does via `ForceNew`.

### 3. Read populates read-only computed fields from `DescribeTable`
`DescribeTable` returns extra computed-only fields (`location`, `modified_time`, `create_time`, `input_format`, `storage_size`, `record_count`, `map_materialized_view_name`, `heat_value`, `input_format_short`, per-column `nullable`/`create_time`/`modified_time`/`data_mask_strategy_info`/`type_text`, per-partition `create_time`, and the create-response `execution`/`is_t_iceberg_sql`). These are exposed as `Computed` schema attributes so users can reference them, but they are never sent on create/update.

### 4. Schema structure follows the JsonPath mapping (flattened, not re-nested)
The `table_base_info` object is flattened to top-level scalars (`database_name`, `table_name`, `datasource_connection_name`, `table_comment`, `type`, `table_format`, `user_alias`, `user_sub_uin`, `govern_policy.*`, `db_govern_policy_is_disable`, `smart_policy.*`, `primary_keys`). `columns`, `partitions`, `properties`, `upsert_keys` are top-level lists. This matches the provider convention of flattening nested request objects and avoids redundant wrapper blocks. The deprecated `govern_policy` / `db_govern_policy_is_disable` fields are still exposed (optional) because the cloud API accepts them.

### 5. Smart-governance policy tree modeled with `TypeList`/`MaxItems: 1` blocks
`smart_policy`, `base_info`, `policy`, `resources` (list, since the API uses `[]*ResourceInfo`), `favor` (list), `resource_conf`, `written`, `advance_policy`, `sort_orders` (list), `lifecycle`, `index`, `change_table`, `table_expiration` are nested `TypeList` blocks with `MaxItems: 1` where the API field is a single struct, and plain `TypeList` where the API field is a slice. Required sub-fields (`base_info.uin`, `table_expiration.enabled`, `table_expiration.expiration`) are `Required` inside their blocks.

## Risks / Trade-offs

- **[Risk] Structural changes require recreation** → Mitigation: documented as `ForceNew` for columns/partitions/properties/smart_policy/primary_keys/upsert_keys; the `AlterTableComment` path handles only the mutable `TableBaseInfo` scalars. Acceptable because the cloud API offers no in-place column/partition update.
- **[Risk] `datasource_connection_name` not in id may collide across datasources** → Mitigation: rare in practice; documented in the `.md` import section that the id is `database_name#table_name` and that cross-datasource tables should rely on distinct database names. If needed later, the id scheme can be extended without breaking existing state (the two-part id remains a valid prefix).
- **[Risk] Deprecated fields (`govern_policy`, `db_govern_policy_is_disable`, `drop_table`) may be removed by the cloud API** → Mitigation: kept optional and clearly documented as deprecated; their absence from a response is handled by nil-checks before `d.Set`.
- **[Trade-off] Large nested schema** → The smart-policy tree is deep, but flattening would lose the API's structure and make the HCL harder to write. Nested blocks are the lesser evil and match the JsonPath mapping exactly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Why

Tencent Cloud DLC (Data Lake Compute) supports creating internal tables (e.g. Iceberg/Hive managed tables) via the `GenerateInternalTable` API, but there is currently no Terraform resource to manage the full lifecycle of an internal table. Users who manage their lakehouse schema through Terraform have no way to declare DLC internal tables as code, forcing them to use the console or ad-hoc SQL. Adding a `tencentcloud_dlc_internal_table` resource closes this gap and lets DLC tables be version-controlled, drift-detected, and destroyed alongside other infrastructure.

## What Changes

- Add a new RESOURCE_KIND_GENERAL resource `tencentcloud_dlc_internal_table` under `tencentcloud/services/dlc/`.
- Implement Create (`GenerateInternalTable`), Read (`DescribeTable`), Update (`AlterTableComment`), and Delete (`DeleteTable`) handlers.
- Expose the table base info, columns, partitions, properties, upsert keys, and the (optional) smart-governance policy tree as schema fields, matching the cloud API parameter mapping.
- Register the resource in `tencentcloud/provider.go` and document it in `tencentcloud/provider.md` + a `resource_tc_dlc_internal_table.md` example.
- Add unit tests (`resource_tc_dlc_internal_table_test.go`) using gomonkey mocks (no Terraform acc suite).

## Capabilities

### New Capabilities
- `dlc-internal-table`: Manages the full CRUD lifecycle of a DLC internal table (base info, columns, partitions, properties, smart governance policy).

### Modified Capabilities
<!-- None. Existing DLC specs (data-engine, user/work-group policy attachments) are unaffected. -->

## Impact

- **New files**:
- `tencentcloud/services/dlc/resource_tc_dlc_internal_table.go`
- `tencentcloud/services/dlc/resource_tc_dlc_internal_table_test.go`
- `tencentcloud/services/dlc/resource_tc_dlc_internal_table.md`
- **Modified files**:
- `tencentcloud/provider.go` (register resource in `resourcesMap`)
- `tencentcloud/provider.md` (auto-generated doc entry, produced via `make doc`)
- **Cloud APIs** (dlc v20210125): `GenerateInternalTable`, `DescribeTable`, `AlterTableComment`, `DeleteTable`. All four are synchronous (no async polling required).
- **Resource ID**: composite `database_name#table_name` (joined with `tccommon.FILED_SP`), since `GenerateInternalTable` does not return a single unique ID and `DescribeTable` queries by `DatabaseName` + `TableName` (+ optional `DatasourceConnectionName`). Supports import via the composite ID.
- **Backward compatibility**: Pure addition; no existing schema or state is affected.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
## ADDED Requirements

### Requirement: Resource manages DLC internal table lifecycle
The provider SHALL provide a `tencentcloud_dlc_internal_table` resource that manages the full lifecycle (create, read, update, delete) of a Tencent Cloud DLC internal table via the `GenerateInternalTable`, `DescribeTable`, `AlterTableComment`, and `DeleteTable` cloud APIs (dlc v20210125).

#### Scenario: Create a new internal table
- **WHEN** a user applies a `tencentcloud_dlc_internal_table` configuration with required fields `database_name`, `table_name`, and `columns` (each column with `name` and `type`)
- **THEN** the provider SHALL call `GenerateInternalTable` with `TableBaseInfo`, `Columns`, and any provided `Partitions`/`Properties`/`UpsertKeys`, and SHALL set the Terraform resource id to `database_name#table_name` (joined with `tccommon.FILED_SP`)

#### Scenario: Read an existing internal table
- **WHEN** the provider reads the resource state
- **THEN** the provider SHALL call `DescribeTable` using `TableName`, `DatabaseName`, and (if set) `DatasourceConnectionName` derived from the composite id and schema, and SHALL populate all schema fields from the returned `Table` object after nil-checking each field

#### Scenario: Table not found during read
- **WHEN** `DescribeTable` returns an empty response (no `Table` / `TableBaseInfo`)
- **THEN** the provider SHALL log `log.Printf("[CRUD] dlc_internal_table id=%s", d.Id())` preserving the id, then call `d.SetId("")` to remove the resource from state

#### Scenario: Delete an internal table
- **WHEN** the user destroys the resource
- **THEN** the provider SHALL call `DeleteTable` with a `TableBaseInfo` populated from the composite id (`database_name`, `table_name`) and the `datasource_connection_name` if set

#### Scenario: Empty create result is rejected
- **WHEN** `GenerateInternalTable` succeeds but returns an empty response (nil `Response` or empty result)
- **THEN** the provider SHALL return a `NonRetryableError` rather than writing an empty id, after logging the current logId and `d.Id()`

### Requirement: Resource id is a composite of database and table name
The resource id SHALL be the composite `database_name#table_name` joined with `tccommon.FILED_SP`, because `GenerateInternalTable` returns no opaque id and `DescribeTable` is queried by `DatabaseName` + `TableName`.

#### Scenario: Import by composite id
- **WHEN** a user runs `terraform import tencentcloud_dlc_internal_table.foo <database_name>#<table_name>`
- **THEN** the provider SHALL split the id on `tccommon.FILED_SP` to recover `database_name` and `table_name` and SHALL populate the schema from a subsequent `DescribeTable` read

### Requirement: Update is limited to AlterTableComment-editable fields
The provider SHALL handle updates by calling `AlterTableComment` with the updated `TableBaseInfo`. Only the scalar `TableBaseInfo` fields accepted by `AlterTableComment` (`table_comment`, `type`, `table_format`, `user_alias`, `user_sub_uin`, `datasource_connection_name`, and the smart-governance policy tree rooted at `table_base_info`) SHALL be mutable in place.

#### Scenario: Update mutable table base info
- **WHEN** a user changes `table_comment`, `type`, `table_format`, `user_alias`, `user_sub_uin`, or the `govern_policy`/`smart_policy` subtree
- **THEN** the provider SHALL call `AlterTableComment` with the new `TableBaseInfo` and SHALL refresh state from `DescribeTable`

#### Scenario: Structural change forces recreation
- **WHEN** a user changes `database_name`, `table_name`, `columns`, `partitions`, `properties`, `upsert_keys`, or `primary_keys`
- **THEN** the provider SHALL treat the change as `ForceNew` (destroy + create), because no cloud API supports in-place structural updates

### Requirement: Schema exposes all mapped cloud-API parameters
The resource schema SHALL expose every cloud-API parameter mapped in the proposal: the flattened `table_base_info` scalars (`database_name`, `table_name`, `datasource_connection_name`, `table_comment`, `type`, `table_format`, `user_alias`, `user_sub_uin`, `govern_policy.*`, `db_govern_policy_is_disable`, `smart_policy.*`, `primary_keys`), the `columns` list (`name`, `type`, `comment`, `default`, `not_null`, `precision`, `scale`, `position`, `is_partition`), the `partitions` list, the `properties` list (`key`, `value`), and `upsert_keys`.

#### Scenario: Required fields enforced
- **WHEN** a user omits `database_name`, `table_name`, or `columns` (or a column's `name`/`type`), or `smart_policy.base_info.uin`, or `smart_policy.policy.table_expiration.enabled`/`expiration`
- **THEN** Terraform SHALL reject the configuration before apply because these fields are `Required`

#### Scenario: Optional fields default to unset
- **WHEN** a user omits optional fields such as `partitions`, `properties`, `upsert_keys`, `datasource_connection_name`, or any optional smart-policy sub-field
- **THEN** the provider SHALL not send those fields to the cloud API on create

### Requirement: Read populates computed-only fields from DescribeTable
The resource schema SHALL expose computed-only attributes returned by `DescribeTable` but never sent on create/update: `location`, `modified_time`, `create_time`, `input_format`, `storage_size`, `record_count`, `map_materialized_view_name`, `heat_value`, `input_format_short`, per-column `nullable`/`create_time`/`modified_time`/`data_mask_strategy_info`/`type_text`, per-partition `create_time`, and the create response `execution`/`is_t_iceberg_sql`.

#### Scenario: Computed fields populated after read
- **WHEN** `DescribeTable` returns a `Table` with `Location`, `StorageSize`, `RecordCount`, etc.
- **THEN** the provider SHALL set the corresponding computed schema fields, after nil-checking each, so they are available in state

### Requirement: Retry and error handling follow provider conventions
All cloud-API calls in Read SHALL be wrapped with `tccommon.ReadRetryTimeout` retry; failures SHALL be wrapped with `tccommon.RetryError()`. Setting ids/state SHALL happen outside the retry block. Create SHALL check the API result for emptiness and return `NonRetryableError` when empty.

#### Scenario: Transient read failure is retried
- **WHEN** `DescribeTable` fails with a transient error
- **THEN** the provider SHALL retry up to `tccommon.ReadRetryTimeout` before surfacing the error wrapped in `tccommon.RetryError()`

#### Scenario: Id/state set outside retry
- **WHEN** a Read succeeds inside the retry block
- **THEN** the provider SHALL perform `d.SetId()` and `d.Set()` calls outside the retry block, after the retry returns
Loading
Loading