From 09a7751bd421a52064cc71fb6805ef5aa418e69e Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 23 Feb 2022 13:37:48 +1100 Subject: [PATCH 1/6] tabledesc: ensure no descending order PKs for row level TTL Supporting descending order PKs is complex as it requires a more complicated pagination algorithm. As such, let's not support such a table for now. Release note: None --- pkg/sql/catalog/tabledesc/validate.go | 28 ++++++++++++++----- .../testdata/logic_test/row_level_ttl | 22 ++++++++++++++- pkg/sql/ttl/ttljob/ttljob.go | 1 - 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/pkg/sql/catalog/tabledesc/validate.go b/pkg/sql/catalog/tabledesc/validate.go index ef9231b8eedd..1b4b5f4482bc 100644 --- a/pkg/sql/catalog/tabledesc/validate.go +++ b/pkg/sql/catalog/tabledesc/validate.go @@ -186,14 +186,28 @@ func (desc *wrapper) ValidateCrossReferences( vea.Report(desc.validateInboundTableRef(by, vdg)) } - // Check foreign keys. - if desc.HasRowLevelTTL() && (len(desc.OutboundFKs) > 0 || len(desc.InboundFKs) > 0) { - vea.Report(unimplemented.NewWithIssuef( - 76407, - `foreign keys to/from table with TTL "%s" are not permitted`, - desc.Name, - )) + // For row-level TTL, only ascending PKs are permitted. + if desc.HasRowLevelTTL() { + pk := desc.GetPrimaryIndex() + for i := 0; i < pk.NumKeyColumns(); i++ { + dir := pk.GetKeyColumnDirection(i) + if dir != descpb.IndexDescriptor_ASC { + vea.Report(unimplemented.NewWithIssuef( + 76912, + `non-ascending ordering on PRIMARY KEYs are not supported`, + )) + } + } + if len(desc.OutboundFKs) > 0 || len(desc.InboundFKs) > 0 { + vea.Report(unimplemented.NewWithIssuef( + 76407, + `foreign keys to/from table with TTL "%s" are not permitted`, + desc.Name, + )) + } } + + // Check foreign keys. for i := range desc.OutboundFKs { vea.Report(desc.validateOutboundFK(&desc.OutboundFKs[i], vdg)) } diff --git a/pkg/sql/logictest/testdata/logic_test/row_level_ttl b/pkg/sql/logictest/testdata/logic_test/row_level_ttl index ab3e4effd7e2..50308e7b29a0 100644 --- a/pkg/sql/logictest/testdata/logic_test/row_level_ttl +++ b/pkg/sql/logictest/testdata/logic_test/row_level_ttl @@ -402,6 +402,27 @@ CREATE TABLE ttl_become_table (id INT PRIMARY KEY, ref INT REFERENCES ref_table statement error foreign keys to/from table with TTL "ttl_become_table" are not permitted ALTER TABLE ttl_become_table SET (ttl_expire_after = '10 minutes') +# Check non-ascending PKs are not permitted. + +statement ok +DROP TABLE tbl + +statement error non-ascending ordering on PRIMARY KEYs are not supported +CREATE TABLE tbl (id INT, text TEXT, PRIMARY KEY (id, text DESC)) WITH (ttl_expire_after = '10 minutes') + +statement ok +CREATE TABLE tbl (id INT, text TEXT, PRIMARY KEY (id, text DESC)) + +statement error non-ascending ordering on PRIMARY KEYs are not supported +ALTER TABLE tbl SET (ttl_expire_after = '10 minutes') + +statement ok +DROP TABLE tbl; +CREATE TABLE tbl (id INT, text TEXT, PRIMARY KEY (id, text)) WITH (ttl_expire_after = '10 minutes') + +statement error non-ascending ordering on PRIMARY KEYs are not supported +ALTER TABLE tbl ALTER PRIMARY KEY USING COLUMNS (id, text DESC) + # Create a table without a TTL. Add the TTL to the table and ensure # the schedule and TTL is setup correctly. statement ok @@ -423,7 +444,6 @@ ALTER TABLE tbl RESET (ttl_select_batch_size) statement ok ROLLBACK - statement error cannot modify TTL settings while another schema change on the table is being processed BEGIN; CREATE INDEX tbl_idx ON tbl (text); diff --git a/pkg/sql/ttl/ttljob/ttljob.go b/pkg/sql/ttl/ttljob/ttljob.go index 3e27ab747c44..178ae029c88d 100644 --- a/pkg/sql/ttl/ttljob/ttljob.go +++ b/pkg/sql/ttl/ttljob/ttljob.go @@ -244,7 +244,6 @@ func (t rowLevelTTLResumer) Resume(ctx context.Context, execCtx interface{}) err var initialVersion descpb.DescriptorVersion // TODO(#75428): feature flag check, ttl pause check. - // TODO(#75428): only allow ascending order PKs for now schema side. var ttlSettings catpb.RowLevelTTL var pkColumns []string var pkTypes []*types.T From 4cdf7a307efb5ad7fa08cd1bb2d560a6cc4c9d57 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 23 Feb 2022 13:39:58 +1100 Subject: [PATCH 2/6] CODEOWNERS: add sql-experience to CODEOWNERS for row-level TTL Release note: None --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6bd5c0a3970d..54de5e589c2f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -46,6 +46,7 @@ /pkg/sql/sessiondata/ @cockroachdb/sql-experience /pkg/sql/tests/rsg_test.go @cockroachdb/sql-experience +/pkg/sql/ttl @cockroachdb/sql-experience /pkg/ccl/schemachangerccl/ @cockroachdb/sql-schema /pkg/sql/catalog/ @cockroachdb/sql-schema From b7bd99800eb96f057e3a8a7bd6291a22d347b4fb Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 23 Feb 2022 17:09:51 +1100 Subject: [PATCH 3/6] tabledesc: ensure crdb_internal_expiration column expressions match Release note: None --- pkg/sql/catalog/tabledesc/validate.go | 23 ++++++++++++++++++ pkg/sql/create_table.go | 1 - .../testdata/logic_test/row_level_ttl | 24 ++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pkg/sql/catalog/tabledesc/validate.go b/pkg/sql/catalog/tabledesc/validate.go index 1b4b5f4482bc..f1dbbb2f4dd2 100644 --- a/pkg/sql/catalog/tabledesc/validate.go +++ b/pkg/sql/catalog/tabledesc/validate.go @@ -189,6 +189,29 @@ func (desc *wrapper) ValidateCrossReferences( // For row-level TTL, only ascending PKs are permitted. if desc.HasRowLevelTTL() { pk := desc.GetPrimaryIndex() + if col, err := desc.FindColumnWithName(colinfo.TTLDefaultExpirationColumnName); err != nil { + vea.Report(errors.Wrapf(err, "expected column %s", colinfo.TTLDefaultExpirationColumnName)) + } else { + intervalExpr := desc.GetRowLevelTTL().DurationExpr + expectedStr := `current_timestamp():::TIMESTAMPTZ + ` + string(intervalExpr) + if col.GetDefaultExpr() != expectedStr { + vea.Report(pgerror.Newf( + pgcode.InvalidTableDefinition, + "expected DEFAULT expression of %s to be %s", + colinfo.TTLDefaultExpirationColumnName, + expectedStr, + )) + } + if col.GetOnUpdateExpr() != expectedStr { + vea.Report(pgerror.Newf( + pgcode.InvalidTableDefinition, + "expected ON UPDATE expression of %s to be %s", + colinfo.TTLDefaultExpirationColumnName, + expectedStr, + )) + } + } + for i := 0; i < pk.NumKeyColumns(); i++ { dir := pk.GetKeyColumnDirection(i) if dir != descpb.IndexDescriptor_ASC { diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index 67b62fc0e201..d0711dac2338 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -1457,7 +1457,6 @@ func NewTableDesc( types.TimestampTZ.SQLString(), ) } - // TODO(#75428): decide whether we need DefaultExpr/UpdateExpr to match. hasRowLevelTTLColumn = true break } diff --git a/pkg/sql/logictest/testdata/logic_test/row_level_ttl b/pkg/sql/logictest/testdata/logic_test/row_level_ttl index 50308e7b29a0..488509282c45 100644 --- a/pkg/sql/logictest/testdata/logic_test/row_level_ttl +++ b/pkg/sql/logictest/testdata/logic_test/row_level_ttl @@ -10,6 +10,22 @@ CREATE TABLE tbl (id INT PRIMARY KEY, text TEXT) WITH (ttl = 'on') statement error "ttl_expire_after" must be set if "ttl_automatic_column" is set CREATE TABLE tbl (id INT PRIMARY KEY, text TEXT) WITH (ttl_automatic_column = 'on') +statement error expected DEFAULT expression of crdb_internal_expiration to be current_timestamp\(\):::TIMESTAMPTZ \+ '00:10:00':::INTERVAL +CREATE TABLE tbl ( + id INT PRIMARY KEY, + text TEXT, + crdb_internal_expiration TIMESTAMPTZ, + FAMILY (id, text) +) WITH (ttl_expire_after = '10 minutes') + +statement error expected ON UPDATE expression of crdb_internal_expiration to be current_timestamp\(\):::TIMESTAMPTZ \+ '00:10:00':::INTERVAL +CREATE TABLE tbl ( + id INT PRIMARY KEY, + text TEXT, + crdb_internal_expiration TIMESTAMPTZ DEFAULT current_timestamp() + '10 minutes'::interval, + FAMILY (id, text) +) WITH (ttl_expire_after = '10 minutes') + statement ok CREATE TABLE tbl ( id INT PRIMARY KEY, @@ -31,6 +47,12 @@ CREATE TABLE public.tbl ( statement error resetting "ttl_expire_after" is not permitted\nHINT: use `RESET \(ttl_automatic_column\)` to remove the automatic column or use `RESET \(ttl\)` to remove TTL from the table ALTER TABLE tbl RESET (ttl_expire_after) +statement error expected DEFAULT expression of crdb_internal_expiration to be current_timestamp\(\):::TIMESTAMPTZ \+ '00:10:00':::INTERVAL +ALTER TABLE tbl ALTER COLUMN crdb_internal_expiration SET DEFAULT current_timestamp() + +statement error expected ON UPDATE expression of crdb_internal_expiration to be current_timestamp\(\):::TIMESTAMPTZ \+ '00:10:00':::INTERVAL +ALTER TABLE tbl ALTER COLUMN crdb_internal_expiration SET ON UPDATE current_timestamp() + statement error cannot drop column crdb_internal_expiration while row-level TTL is active ALTER TABLE tbl DROP COLUMN crdb_internal_expiration @@ -225,7 +247,7 @@ WHERE label = 'row-level-ttl-$table_id' query T SELECT create_statement FROM [SHOW CREATE SCHEDULE $schedule_id] ---- -ALTER TABLE [120 as T] WITH (expire_after = ...) +ALTER TABLE [122 as T] WITH (expire_after = ...) statement ok DROP TABLE tbl From 56756f8245544f515eee89c0e9ea3841d2c5111f Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 23 Feb 2022 17:26:59 +1100 Subject: [PATCH 4/6] sql: do not allow SET (ttl_expire_after) if automatic column defined Note we allow this for CREATE TABLE so SHOW CREATE TABLE round trips. But we should never need this for the ALTER TABLE ... SET ... Release note: None --- pkg/sql/alter_table.go | 7 +++++++ pkg/sql/logictest/testdata/logic_test/row_level_ttl | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/pkg/sql/alter_table.go b/pkg/sql/alter_table.go index c2ff6a860c3c..5c5267676a6c 100644 --- a/pkg/sql/alter_table.go +++ b/pkg/sql/alter_table.go @@ -1870,6 +1870,13 @@ func handleTTLStorageParamChange( // Adding a TTL requires adding the automatic column and deferring the TTL // addition to after the column is successfully added. tableDesc.RowLevelTTL = nil + if _, err := tableDesc.FindColumnWithName(colinfo.TTLDefaultExpirationColumnName); err == nil { + return pgerror.Newf( + pgcode.InvalidTableDefinition, + "cannot add TTL to table with the %s column already defined", + colinfo.TTLDefaultExpirationColumnName, + ) + } col, err := rowLevelTTLAutomaticColumnDef(after) if err != nil { return err diff --git a/pkg/sql/logictest/testdata/logic_test/row_level_ttl b/pkg/sql/logictest/testdata/logic_test/row_level_ttl index 488509282c45..1cff28137ce3 100644 --- a/pkg/sql/logictest/testdata/logic_test/row_level_ttl +++ b/pkg/sql/logictest/testdata/logic_test/row_level_ttl @@ -399,6 +399,19 @@ CREATE TABLE public.tbl ( FAMILY fam_0_id_text_crdb_internal_expiration (id, text, crdb_internal_expiration) ) WITH (ttl = 'on', ttl_automatic_column = 'on', ttl_expire_after = '00:10:00':::INTERVAL) +# Test adding to TTL table with crdb_internal_expiration already defined. +statement ok +DROP TABLE tbl; +CREATE TABLE tbl ( + id INT PRIMARY KEY, + text TEXT, + crdb_internal_expiration TIMESTAMPTZ, + FAMILY (id, text) +) + +statement error cannot add TTL to table with the crdb_internal_expiration column already defined +ALTER TABLE tbl SET (ttl_expire_after = '10 minutes') + # Test we cannot add FKs to a TTL table. statement ok CREATE TABLE ref_table (id INT PRIMARY KEY, ref INT) From 8b356c43de90fa473be4c43d1e81e67880afd178 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 23 Feb 2022 20:36:48 +1100 Subject: [PATCH 5/6] sql: ensure SHOW CREATE TABLE matches pg_class.reloptions Release note: None --- pkg/sql/catalog/descriptor.go | 2 + pkg/sql/catalog/tabledesc/structured.go | 39 +++++++++++++++++++ .../testdata/logic_test/row_level_ttl | 17 +++++--- pkg/sql/pg_catalog.go | 8 ++-- pkg/sql/show_create.go | 34 +--------------- 5 files changed, 58 insertions(+), 42 deletions(-) diff --git a/pkg/sql/catalog/descriptor.go b/pkg/sql/catalog/descriptor.go index e0dab6ab443f..bfa1fd0f4a5f 100644 --- a/pkg/sql/catalog/descriptor.go +++ b/pkg/sql/catalog/descriptor.go @@ -670,6 +670,8 @@ type TableDescriptor interface { // GetExcludeDataFromBackup returns true if the table's row data is configured // to be excluded during backup. GetExcludeDataFromBackup() bool + // GetStorageParams returns a list of storage parameters for the table. + GetStorageParams(spaceBetweenEqual bool) []string } // TypeDescriptor will eventually be called typedesc.Descriptor. diff --git a/pkg/sql/catalog/tabledesc/structured.go b/pkg/sql/catalog/tabledesc/structured.go index db339aee6941..0d7f8b6788c1 100644 --- a/pkg/sql/catalog/tabledesc/structured.go +++ b/pkg/sql/catalog/tabledesc/structured.go @@ -2523,6 +2523,45 @@ func (desc *wrapper) GetExcludeDataFromBackup() bool { return desc.ExcludeDataFromBackup } +// GetStorageParams implements the TableDescriptor interface. +func (desc *wrapper) GetStorageParams(spaceBetweenEqual bool) []string { + var storageParams []string + var spacing string + if spaceBetweenEqual { + spacing = ` ` + } + appendStorageParam := func(key, value string) { + storageParams = append(storageParams, key+spacing+`=`+spacing+value) + } + if ttl := desc.GetRowLevelTTL(); ttl != nil { + appendStorageParam(`ttl`, `'on'`) + appendStorageParam(`ttl_automatic_column`, `'on'`) + appendStorageParam(`ttl_expire_after`, string(ttl.DurationExpr)) + if bs := ttl.SelectBatchSize; bs != 0 { + appendStorageParam(`ttl_select_batch_size`, fmt.Sprintf(`%d`, bs)) + } + if bs := ttl.DeleteBatchSize; bs != 0 { + appendStorageParam(`ttl_delete_batch_size`, fmt.Sprintf(`%d`, bs)) + } + if cron := ttl.DeletionCron; cron != "" { + appendStorageParam(`ttl_job_cron`, fmt.Sprintf(`'%s'`, cron)) + } + if rc := ttl.RangeConcurrency; rc != 0 { + appendStorageParam(`ttl_range_concurrency`, fmt.Sprintf(`%d`, rc)) + } + if rl := ttl.DeleteRateLimit; rl != 0 { + appendStorageParam(`ttl_delete_rate_limit`, fmt.Sprintf(`%d`, rl)) + } + if pause := ttl.Pause; pause { + appendStorageParam(`ttl_pause`, fmt.Sprintf(`%t`, pause)) + } + } + if exclude := desc.GetExcludeDataFromBackup(); exclude { + appendStorageParam(`exclude_data_from_backup`, `true`) + } + return storageParams +} + // GetMultiRegionEnumDependency returns true if the given table has an "implicit" // dependency on the multi-region enum. An implicit dependency exists for // REGIONAL BY TABLE table's which are homed in an explicit region diff --git a/pkg/sql/logictest/testdata/logic_test/row_level_ttl b/pkg/sql/logictest/testdata/logic_test/row_level_ttl index 1cff28137ce3..c11bbe12fd46 100644 --- a/pkg/sql/logictest/testdata/logic_test/row_level_ttl +++ b/pkg/sql/logictest/testdata/logic_test/row_level_ttl @@ -59,7 +59,7 @@ ALTER TABLE tbl DROP COLUMN crdb_internal_expiration query T SELECT reloptions FROM pg_class WHERE relname = 'tbl' ---- -{ttl_expire_after='00:10:00':::INTERVAL} +{ttl='on',ttl_automatic_column='on',ttl_expire_after='00:10:00':::INTERVAL} query I SELECT count(1) FROM [SHOW SCHEDULES] @@ -266,11 +266,11 @@ query T SELECT create_statement FROM [SHOW CREATE TABLE tbl] ---- CREATE TABLE public.tbl ( - id INT8 NOT NULL, - text STRING NULL, - crdb_internal_expiration TIMESTAMPTZ NOT VISIBLE NOT NULL DEFAULT current_timestamp():::TIMESTAMPTZ + '00:10:00':::INTERVAL ON UPDATE current_timestamp():::TIMESTAMPTZ + '00:10:00':::INTERVAL, - CONSTRAINT tbl_pkey PRIMARY KEY (id ASC), - FAMILY fam_0_id_text_crdb_internal_expiration (id, text, crdb_internal_expiration) + id INT8 NOT NULL, + text STRING NULL, + crdb_internal_expiration TIMESTAMPTZ NOT VISIBLE NOT NULL DEFAULT current_timestamp():::TIMESTAMPTZ + '00:10:00':::INTERVAL ON UPDATE current_timestamp():::TIMESTAMPTZ + '00:10:00':::INTERVAL, + CONSTRAINT tbl_pkey PRIMARY KEY (id ASC), + FAMILY fam_0_id_text_crdb_internal_expiration (id, text, crdb_internal_expiration) ) WITH (ttl = 'on', ttl_automatic_column = 'on', ttl_expire_after = '00:10:00':::INTERVAL, ttl_job_cron = '@daily') let $table_id @@ -348,6 +348,11 @@ CREATE TABLE tbl ( FAMILY (id, text) ) WITH (ttl_expire_after = '10 minutes', ttl_select_batch_size = 50, ttl_range_concurrency = 2, ttl_delete_rate_limit = 100, ttl_pause = true) +query T +SELECT reloptions FROM pg_class WHERE relname = 'tbl' +---- +{ttl='on',ttl_automatic_column='on',ttl_expire_after='00:10:00':::INTERVAL,ttl_select_batch_size=50,ttl_range_concurrency=2,ttl_delete_rate_limit=100,ttl_pause=true} + query T SELECT create_statement FROM [SHOW CREATE TABLE tbl] ---- diff --git a/pkg/sql/pg_catalog.go b/pkg/sql/pg_catalog.go index 44827affbd9a..d0ccf874a772 100644 --- a/pkg/sql/pg_catalog.go +++ b/pkg/sql/pg_catalog.go @@ -624,10 +624,12 @@ https://www.postgresql.org/docs/9.5/catalog-pg-class.html`, relPersistence = relPersistenceTemporary } var relOptions tree.Datum = tree.DNull - if ttl := table.GetRowLevelTTL(); ttl != nil { + if storageParams := table.GetStorageParams(false /* spaceBetweenEqual */); len(storageParams) > 0 { relOptionsArr := tree.NewDArray(types.String) - if err := relOptionsArr.Append(tree.NewDString(fmt.Sprintf("ttl_expire_after=%s", ttl.DurationExpr))); err != nil { - return err + for _, storageParam := range storageParams { + if err := relOptionsArr.Append(tree.NewDString(storageParam)); err != nil { + return err + } } relOptions = relOptionsArr } diff --git a/pkg/sql/show_create.go b/pkg/sql/show_create.go index f3fd3fe669bc..2fb9f74a4fbb 100644 --- a/pkg/sql/show_create.go +++ b/pkg/sql/show_create.go @@ -13,7 +13,6 @@ package sql import ( "bytes" "context" - "fmt" "strings" "github.com/cockroachdb/cockroach/pkg/sql/catalog" @@ -177,38 +176,7 @@ func ShowCreateTable( return "", err } - var storageParams []string - if ttl := desc.GetRowLevelTTL(); ttl != nil { - storageParams = append( - storageParams, - `ttl = 'on'`, - `ttl_automatic_column = 'on'`, - fmt.Sprintf(`ttl_expire_after = %s`, ttl.DurationExpr), - ) - if bs := ttl.SelectBatchSize; bs != 0 { - storageParams = append(storageParams, fmt.Sprintf(`ttl_select_batch_size = %d`, bs)) - } - if bs := ttl.DeleteBatchSize; bs != 0 { - storageParams = append(storageParams, fmt.Sprintf(`ttl_delete_batch_size = %d`, bs)) - } - if cron := ttl.DeletionCron; cron != "" { - storageParams = append(storageParams, fmt.Sprintf(`ttl_job_cron = '%s'`, cron)) - } - if rc := ttl.RangeConcurrency; rc != 0 { - storageParams = append(storageParams, fmt.Sprintf(`ttl_range_concurrency = %d`, rc)) - } - if rc := ttl.DeleteRateLimit; rc != 0 { - storageParams = append(storageParams, fmt.Sprintf(`ttl_delete_rate_limit = %d`, rc)) - } - if pause := ttl.Pause; pause { - storageParams = append(storageParams, fmt.Sprintf(`ttl_pause = %t`, pause)) - } - } - if exclude := desc.GetExcludeDataFromBackup(); exclude { - storageParams = append(storageParams, `exclude_data_from_backup = true`) - } - - if len(storageParams) > 0 { + if storageParams := desc.GetStorageParams(true /* spaceBetweenEqual */); len(storageParams) > 0 { f.Buffer.WriteString(` WITH (`) f.Buffer.WriteString(strings.Join(storageParams, ", ")) f.Buffer.WriteString(`)`) From 1c9369d4636371ca8409d73788699743b79e9472 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Sat, 26 Feb 2022 07:15:40 +1100 Subject: [PATCH 6/6] ttl: annotate post-v22.1 TODOs This commit fixes up any post-v22.1 TODOs with unimplemented messages or comments annotations for the future. Release note (sql change): We introduce a new row level TTL feature to CockroachDB, which is available as a beta feature. This allows users to use a special syntax to automatically mark rows for deletion. Rows are deleted using a SCHEDULED JOB. A user can create a table with TTL using: ``` CREATE TABLE t (id INT PRIMARY KEY) WITH (ttl_expire_after = '10 mins') ``` Where `ttl_expire_after` is a duration expression. A user can also add TTL to an existing table using ``` ALTER TABLE t SET (ttl_expire_after = '10 mins') ``` This creates a new column, `crdb_internal_expiration`, which automatically is set to `now() + ttl_expire_after` when inserted by default or on update. The scheduled job will delete any rows which exceed this timestamp as of the beginning of the job run. The TTL job is configurable in a few ways using the WITH/SET syntax: * ttl_select_batch_size: how many rows to select at once (by default it is cluster setting sql.ttl.default_select_batch_size) * ttl_delete_batch_size: how many rows to delete at once (default cluster setting sql.ttl.default_select_batch_size) * ttl_delete_rate_limit: maximum rows to delete per second for the given table (default cluster setting sql.default.default_delete_rate_limit) * ttl_pause: pauses the TTL job (also globally pausable with `sql.ttl.job.enabled`). Using `ALTER TABLE table_name RESET ()` will reset the parameter to re-use the default, or `RESET(ttl)` will disable the TTL job for the table and remove the `crdb_internal_expiration` column. --- pkg/sql/logictest/testdata/logic_test/row_level_ttl | 2 +- pkg/sql/paramparse/paramobserver.go | 6 +++--- pkg/sql/ttl/ttljob/ttljob.go | 3 ++- pkg/sql/ttl/ttlschedule/ttlschedule.go | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/sql/logictest/testdata/logic_test/row_level_ttl b/pkg/sql/logictest/testdata/logic_test/row_level_ttl index c11bbe12fd46..f73159bb03b3 100644 --- a/pkg/sql/logictest/testdata/logic_test/row_level_ttl +++ b/pkg/sql/logictest/testdata/logic_test/row_level_ttl @@ -44,7 +44,7 @@ CREATE TABLE public.tbl ( FAMILY fam_0_id_text_crdb_internal_expiration (id, text, crdb_internal_expiration) ) WITH (ttl = 'on', ttl_automatic_column = 'on', ttl_expire_after = '00:10:00':::INTERVAL) -statement error resetting "ttl_expire_after" is not permitted\nHINT: use `RESET \(ttl_automatic_column\)` to remove the automatic column or use `RESET \(ttl\)` to remove TTL from the table +statement error resetting "ttl_expire_after" is not permitted\nHINT: use `RESET \(ttl\)` to remove TTL from the table ALTER TABLE tbl RESET (ttl_expire_after) statement error expected DEFAULT expression of crdb_internal_expiration to be current_timestamp\(\):::TIMESTAMPTZ \+ '00:10:00':::INTERVAL diff --git a/pkg/sql/paramparse/paramobserver.go b/pkg/sql/paramparse/paramobserver.go index f89284f96a4f..47ddfdb543f6 100644 --- a/pkg/sql/paramparse/paramobserver.go +++ b/pkg/sql/paramparse/paramobserver.go @@ -208,12 +208,12 @@ var tableParams = map[string]tableParam{ po.setAutomaticColumn = true } if !setTrue && po.tableDesc.RowLevelTTL != nil { - return unimplemented.NewWithIssue(75428, "unsetting TTL automatic column not yet implemented") + return unimplemented.NewWithIssue(76916, "unsetting TTL automatic column not yet implemented") } return nil }, onReset: func(po *TableStorageParamObserver, evalCtx *tree.EvalContext, key string) error { - return unimplemented.NewWithIssue(75428, "unsetting TTL automatic column not yet implemented") + return unimplemented.NewWithIssue(76916, "unsetting TTL automatic column not yet implemented") }, }, `ttl_expire_after`: { @@ -258,7 +258,7 @@ var tableParams = map[string]tableParam{ pgcode.InvalidParameterValue, `resetting "ttl_expire_after" is not permitted`, ), - "use `RESET (ttl_automatic_column)` to remove the automatic column or use `RESET (ttl)` to remove TTL from the table", + "use `RESET (ttl)` to remove TTL from the table", ) }, }, diff --git a/pkg/sql/ttl/ttljob/ttljob.go b/pkg/sql/ttl/ttljob/ttljob.go index 178ae029c88d..02df1a5b2f34 100644 --- a/pkg/sql/ttl/ttljob/ttljob.go +++ b/pkg/sql/ttl/ttljob/ttljob.go @@ -495,7 +495,8 @@ func runTTLOnRange( ie := execCfg.InternalExecutor db := execCfg.DB - // TODO(#75428): look at using a dist sql flow job + // TODO(#76914): look at using a dist sql flow job, utilize any existing index + // on crdb_internal_expiration. selectBuilder := makeSelectQueryBuilder( details.TableID, diff --git a/pkg/sql/ttl/ttlschedule/ttlschedule.go b/pkg/sql/ttl/ttlschedule/ttlschedule.go index 1fc3ef82ba21..476df185d952 100644 --- a/pkg/sql/ttl/ttlschedule/ttlschedule.go +++ b/pkg/sql/ttl/ttlschedule/ttlschedule.go @@ -149,7 +149,7 @@ func (s rowLevelTTLExecutor) GetCreateScheduleStatement( return "", err } - // TODO(#75428): consider using table name instead - we would need to pass in descCol from the planner. + // TODO(#76915): consider using table name instead - we would need to pass in descCol from the planner. return fmt.Sprintf("ALTER TABLE [%d as T] WITH (expire_after = ...)", args.TableID), nil }