Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Support for identity column in sql table #3709

Closed
wants to merge 3 commits into from
Closed
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
31 changes: 26 additions & 5 deletions catalog/resource_sql_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@ import (
var MaxSqlExecWaitTimeout = 50

type SqlColumnInfo struct {
Name string `json:"name"`
Type string `json:"type_text,omitempty" tf:"alias:type,computed"`
Comment string `json:"comment,omitempty"`
Nullable bool `json:"nullable,omitempty" tf:"default:true"`
Name string `json:"name"`
Type string `json:"type_text,omitempty" tf:"alias:type,computed"`
Identity IdentityColumn `json:"identity,omitempty"`
Comment string `json:"comment,omitempty"`
Nullable bool `json:"nullable,omitempty" tf:"default:true"`
}

type IdentityColumn string

const IdentityColumnAlways IdentityColumn = "always"
const IdentityColumnDefault IdentityColumn = "default"

type SqlTableInfo struct {
Name string `json:"name"`
CatalogName string `json:"catalog_name" tf:"force_new"`
Expand Down Expand Up @@ -171,7 +177,22 @@ func (ti *SqlTableInfo) getOrCreateCluster(clusterName string, clustersAPI clust
return aclCluster.ClusterID, nil
}

func (ci *SqlColumnInfo) getColumnType() string {
var colType string
switch ci.Identity {
case IdentityColumnAlways:
colType = "BIGINT GENERATED ALWAYS AS IDENTITY"
case IdentityColumnDefault:
colType = "BIGINT GENERATED BY DEFAULT AS IDENTITY"
default:
colType = ci.Type
}
return colType
}

func (ti *SqlTableInfo) serializeColumnInfo(col SqlColumnInfo) string {
var colType = col.getColumnType()

notNull := ""
if !col.Nullable {
notNull = " NOT NULL"
Expand All @@ -181,7 +202,7 @@ func (ti *SqlTableInfo) serializeColumnInfo(col SqlColumnInfo) string {
if col.Comment != "" {
comment = fmt.Sprintf(" COMMENT '%s'", parseComment(col.Comment))
}
return fmt.Sprintf("%s %s%s%s", col.getWrappedColumnName(), col.Type, notNull, comment) // id INT NOT NULL COMMENT 'something'
return fmt.Sprintf("%s %s%s%s", col.getWrappedColumnName(), colType, notNull, comment) // id INT NOT NULL COMMENT 'something'
}

func (ti *SqlTableInfo) serializeColumnInfos() string {
Expand Down
101 changes: 101 additions & 0 deletions catalog/resource_sql_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,36 @@ func TestResourceSqlTableCreateStatement_External(t *testing.T) {
assert.Contains(t, stmt, "COMMENT 'terraform managed'")
}

func TestResourceSqlTableCreateStatement_IdentityColumn(t *testing.T) {
ti := &SqlTableInfo{
Name: "bar",
CatalogName: "main",
SchemaName: "foo",
TableType: "EXTERNAL",
DataSourceFormat: "DELTA",
StorageLocation: "s3://ext-main/foo/bar1",
StorageCredentialName: "somecred",
Comment: "terraform managed",
ColumnInfos: []SqlColumnInfo{
{
Name: "id",
Type: "bigint",
Identity: "default",
},
{
Name: "name",
Comment: "a comment",
},
},
}
stmt := ti.buildTableCreateStatement()
assert.Contains(t, stmt, "CREATE EXTERNAL TABLE `main`.`foo`.`bar`")
assert.Contains(t, stmt, "USING DELTA")
assert.Contains(t, stmt, "(`id` BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, `name` NOT NULL COMMENT 'a comment')")
assert.Contains(t, stmt, "LOCATION 's3://ext-main/foo/bar1' WITH (CREDENTIAL `somecred`)")
assert.Contains(t, stmt, "COMMENT 'terraform managed'")
}
hshahconsulting marked this conversation as resolved.
Show resolved Hide resolved

func TestResourceSqlTableCreateStatement_View(t *testing.T) {
ti := &SqlTableInfo{
Name: "bar",
Expand Down Expand Up @@ -1334,6 +1364,77 @@ func TestResourceSqlTableCreateTable_ExistingSQLWarehouse(t *testing.T) {
assert.NoError(t, err)
}

func TestResourceSqlTableCreateTableWithIdentityColumn_ExistingSQLWarehouse(t *testing.T) {
_, err := qa.ResourceFixture{
CommandMock: func(commandStr string) common.CommandResults {
return common.CommandResults{
ResultType: "",
Data: nil,
}
},
HCL: `
name = "bar"
catalog_name = "main"
schema_name = "foo"
table_type = "MANAGED"
data_source_format = "DELTA"
storage_location = "abfss://container@account/somepath"
warehouse_id = "existingwarehouse"

column {
name = "id"
type = "bigint"
identity = "default"
}
column {
name = "name"
type = "string"
comment = "name of thing"
}
comment = "this table is managed by terraform"
`,
Fixtures: []qa.HTTPFixture{
{
Method: "POST",
Resource: "/api/2.0/sql/statements/",
ExpectedRequest: sql.ExecuteStatementRequest{
Statement: "CREATE TABLE `main`.`foo`.`bar` (`id` BIGINT GENERATED BY DEFAULT AS IDENTITY, `name` string COMMENT 'name of thing')\nUSING DELTA\nCOMMENT 'this table is managed by terraform'\nLOCATION 'abfss://container@account/somepath';",
WaitTimeout: "50s",
WarehouseId: "existingwarehouse",
OnWaitTimeout: sql.ExecuteStatementRequestOnWaitTimeoutCancel,
},
Response: sql.StatementResponse{
StatementId: "statement1",
Status: &sql.StatementStatus{
State: "SUCCEEDED",
},
},
},
{
Method: "GET",
Resource: "/api/2.1/unity-catalog/tables/main.foo.bar",
Response: SqlTableInfo{
Name: "bar",
CatalogName: "main",
SchemaName: "foo",
TableType: "EXTERNAL",
DataSourceFormat: "DELTA",
StorageLocation: "s3://ext-main/foo/bar1",
StorageCredentialName: "somecred",
Comment: "terraform managed",
Properties: map[string]string{
"one": "two",
"three": "four",
},
},
},
},
Create: true,
Resource: ResourceSqlTable(),
}.Apply(t)
assert.NoError(t, err)
}

func TestResourceSqlTableCreateTable_OnlyManagedProperties(t *testing.T) {
qa.ResourceFixture{
CommandMock: func(commandStr string) common.CommandResults {
Expand Down
42 changes: 42 additions & 0 deletions docs/resources/sql_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,46 @@ resource "databricks_sql_table" "thing_view" {
}
```

## Use an Identity Column

```hcl
resource "databricks_catalog" "sandbox" {
name = "sandbox"
comment = "this catalog is managed by terraform"
properties = {
purpose = "testing"
}
}
resource "databricks_schema" "things" {
catalog_name = databricks_catalog.sandbox.id
name = "things"
comment = "this database is managed by terraform"
properties = {
kind = "various"
}
}
resource "databricks_sql_table" "thing" {
provider = databricks.workspace
name = "quickstart_table"
catalog_name = databricks_catalog.sandbox.name
schema_name = databricks_schema.things.name
table_type = "MANAGED"
data_source_format = "DELTA"
storage_location = ""
column {
name = "id"
type = "bigint"
identity = "default"
}
column {
name = "name"
type = "string"
comment = "name of thing"
}
comment = "this table is managed by terraform"
}
```

## Argument Reference

The following arguments are supported:
Expand Down Expand Up @@ -137,6 +177,8 @@ Currently, changing the column definitions for a table will require dropping and

* `name` - User-visible name of column
* `type` - Column type spec (with metadata) as SQL text. Not supported for `VIEW` table_type.
* `identity` - (Optional) Whether field is an identity column. Can be `default`, `always` or unset.
It is unset by default.
* `comment` - (Optional) User-supplied free-form text.
* `nullable` - (Optional) Whether field is nullable (Default: `true`)

Expand Down
64 changes: 64 additions & 0 deletions internal/acceptance/sql_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,70 @@ func TestUcAccResourceSqlTable_Managed(t *testing.T) {
})
}

func TestUcAccResourceSqlTableWithIdentityColumn_Managed(t *testing.T) {
if os.Getenv("GOOGLE_CREDENTIALS") != "" {
skipf(t)("databricks_sql_table resource not available on GCP")
}
unityWorkspaceLevel(t, step{
Template: `
resource "databricks_schema" "this" {
name = "{var.STICKY_RANDOM}"
catalog_name = "main"
}

resource "databricks_sql_table" "this" {
name = "bar"
catalog_name = "main"
schema_name = databricks_schema.this.name
table_type = "MANAGED"
properties = {
this = "that"
something = "else"
}

column {
name = "id"
type = "bigint"
identity = "default"
}
column {
name = "name"
type = "string"
}
comment = "this table is managed by terraform"
owner = "account users"
}`,
}, step{
Template: `
resource "databricks_schema" "this" {
name = "{var.STICKY_RANDOM}"
catalog_name = "main"
}

resource "databricks_sql_table" "this" {
name = "bar"
catalog_name = "main"
schema_name = databricks_schema.this.name
table_type = "MANAGED"
properties = {
that = "this"
something = "else2"
}

column {
name = "id"
type = "bigint"
identity = "default"
}
column {
name = "name"
type = "string"
}
comment = "this table is managed by terraform..."
}`,
})
}

func TestUcAccResourceSqlTable_External(t *testing.T) {
unityWorkspaceLevel(t, step{
Template: `
Expand Down