Skip to content
Permalink
Browse files

sql: support `COMMENT ON COLUMN`

This patch introduces support for table column comments.

The syntax to set or delete a comment is the same as postgres:
`COMMENT ON COLUMN ... IS ...`. See:
https://www.postgresql.org/docs/9.1/sql-comment.html

Release note (sql change): CockroachDB now supports associating
comments to SQL table column using PostgreSQL's `COMMENT ON COLIMN`
syntax. This also provides proper support for pg's
`pg_catalog.pg_description` and built-in function `col_description()`.
  • Loading branch information
hueypark committed Dec 31, 2018
1 parent 6dac732 commit 52fa9cb3f9edc175fa953f2eb8c323fe6acafe4e
@@ -47,6 +47,7 @@ copy_from_stmt ::=
comment_stmt ::=
'COMMENT' 'ON' 'DATABASE' database_name 'IS' comment_text
| 'COMMENT' 'ON' 'TABLE' table_name 'IS' comment_text
| 'COMMENT' 'ON' 'COLUMN' column_path 'IS' comment_text

execute_stmt ::=
'EXECUTE' table_alias_name execute_param_clause
@@ -207,6 +208,10 @@ comment_text ::=
'SCONST'
| 'NULL'

column_path ::=
name
| prefixed_column_path

table_alias_name ::=
name

@@ -536,6 +541,11 @@ db_object_name ::=
simple_db_object_name
| complex_db_object_name

prefixed_column_path ::=
db_object_name_component '.' unrestricted_name
| db_object_name_component '.' unrestricted_name '.' unrestricted_name
| db_object_name_component '.' unrestricted_name '.' unrestricted_name '.' unrestricted_name

expr_list ::=
( a_expr ) ( ( ',' a_expr ) )*

@@ -1145,18 +1155,6 @@ complex_db_object_name ::=
db_object_name_component '.' unrestricted_name
| db_object_name_component '.' unrestricted_name '.' unrestricted_name

non_reserved_word ::=
'identifier'
| unreserved_keyword
| col_name_keyword
| type_func_name_keyword

kv_option ::=
name '=' string_or_placeholder
| name
| 'SCONST' '=' string_or_placeholder
| 'SCONST'

db_object_name_component ::=
name
| cockroachdb_extra_type_func_name_keyword
@@ -1169,6 +1167,18 @@ unrestricted_name ::=
| type_func_name_keyword
| reserved_keyword

non_reserved_word ::=
'identifier'
| unreserved_keyword
| col_name_keyword
| type_func_name_keyword

kv_option ::=
name '=' string_or_placeholder
| name
| 'SCONST' '=' string_or_placeholder
| 'SCONST'

transaction_mode ::=
transaction_user_priority
| transaction_read_mode
@@ -1481,6 +1491,13 @@ single_set_clause ::=
multiple_set_clause ::=
'(' insert_column_list ')' '=' in_expr

cockroachdb_extra_type_func_name_keyword ::=
'FAMILY'

cockroachdb_extra_reserved_keyword ::=
'INDEX'
| 'NOTHING'

type_func_name_keyword ::=
'COLLATION'
| 'CROSS'
@@ -1500,13 +1517,6 @@ type_func_name_keyword ::=
| 'SIMILAR'
| cockroachdb_extra_type_func_name_keyword

cockroachdb_extra_type_func_name_keyword ::=
'FAMILY'

cockroachdb_extra_reserved_keyword ::=
'INDEX'
| 'NOTHING'

reserved_keyword ::=
'ALL'
| 'ANALYSE'
@@ -1921,10 +1931,6 @@ opt_interval ::=
interval_qualifier
|

column_path ::=
name
| prefixed_column_path

func_application ::=
func_name '(' ')'
| func_name '(' expr_list ')'
@@ -2101,11 +2107,6 @@ interval_qualifier ::=
| 'HOUR' 'TO' interval_second
| 'MINUTE' 'TO' interval_second

prefixed_column_path ::=
db_object_name_component '.' unrestricted_name
| db_object_name_component '.' unrestricted_name '.' unrestricted_name
| db_object_name_component '.' unrestricted_name '.' unrestricted_name '.' unrestricted_name

func_name ::=
type_function_name
| prefixed_column_path
@@ -333,5 +333,5 @@ const (
// CommentType is type for system.comments
DatabaseCommentType = 0
TableCommentType = 1
// ColumnCommentType = 2
ColumnCommentType = 2
)
@@ -20,6 +20,7 @@ import (
gojson "encoding/json"
"fmt"

"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/sql/coltypes"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
@@ -411,6 +412,13 @@ func (n *alterTableNode) startExec(params runParams) error {
descriptorChanged = true
}

if err != nil {
return err
}
if err := params.p.removeColumnComment(params.ctx, n.tableDesc.ID, col.ID); err != nil {
return err
}

found := false
for i := range n.tableDesc.Columns {
if n.tableDesc.Columns[i].ID == col.ID {
@@ -873,3 +881,18 @@ func injectTableStats(

return stats.GossipTableStatAdded(params.extendedEvalCtx.ExecCfg.Gossip, desc.ID)
}

func (p *planner) removeColumnComment(
ctx context.Context, tableID sqlbase.ID, columnID sqlbase.ColumnID,
) error {
_, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.Exec(
ctx,
"delete-column-comment",
p.txn,
"DELETE FROM system.comments WHERE type=$1 AND object_id=$2 AND sub_id=$3",
keys.ColumnCommentType,
tableID,
columnID)

return err
}
@@ -0,0 +1,106 @@
// Copyright 2018 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

package sql

import (
"context"

"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

type commentOnColumnNode struct {
n *tree.CommentOnColumn
tableDesc *MutableTableDescriptor
}

// CommentOnColumn add comment on a column.
// Privileges: CREATE on table.
func (p *planner) CommentOnColumn(ctx context.Context, n *tree.CommentOnColumn) (planNode, error) {
tableName, err := tree.NormalizeTableName(&n.ColumnItem.TableName)
if err != nil {
return nil, err
}

tableDesc, err := p.ResolveMutableTableDescriptor(ctx, &tableName, true, requireTableDesc)
if err != nil {
return nil, err
}

if err := p.CheckPrivilege(ctx, tableDesc, privilege.CREATE); err != nil {
return nil, err
}

return &commentOnColumnNode{n: n, tableDesc: tableDesc}, nil
}

func (n *commentOnColumnNode) startExec(params runParams) error {
col, _, err := n.tableDesc.FindColumnByName(n.n.ColumnItem.ColumnName)
if err != nil {
return err
}

if n.n.Comment != nil {
_, err := params.p.extendedEvalCtx.ExecCfg.InternalExecutor.Exec(
params.ctx,
"set-column-comment",
params.p.Txn(),
"UPSERT INTO system.comments VALUES ($1, $2, $3, $4)",
keys.ColumnCommentType,
n.tableDesc.ID,
col.ID,
*n.n.Comment)
if err != nil {
return err
}
} else {
_, err := params.p.extendedEvalCtx.ExecCfg.InternalExecutor.Exec(
params.ctx,
"delete-column-comment",
params.p.Txn(),
"DELETE FROM system.comments WHERE type=$1 AND object_id=$2 AND sub_id=$3",
keys.ColumnCommentType,
n.tableDesc.ID,
col.ID)
if err != nil {
return err
}
}

return MakeEventLogger(params.extendedEvalCtx.ExecCfg).InsertEventRecord(
params.ctx,
params.p.txn,
EventLogCommentOnColumn,
int32(n.tableDesc.ID),
int32(params.extendedEvalCtx.NodeID),
struct {
TableName string
ColumnName string
Statement string
User string
Comment *string
}{
n.tableDesc.Name,
string(n.n.ColumnItem.ColumnName),
n.n.String(),
params.SessionData().User,
n.n.Comment},
)
}

func (n *commentOnColumnNode) Next(runParams) (bool, error) { return false, nil }
func (n *commentOnColumnNode) Values() tree.Datums { return tree.Datums{} }
func (n *commentOnColumnNode) Close(context.Context) {}

0 comments on commit 52fa9cb

Please sign in to comment.
You can’t perform that action at this time.