-
-
Notifications
You must be signed in to change notification settings - Fork 526
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
Create dolt_help system table #8739
base: main
Are you sure you want to change the base?
Changes from 4 commits
13c546b
f86a441
d0fca91
485cbb3
2009c87
b17e52d
0495fe3
8373c72
7d042f6
4355fae
4c0ab89
6e87334
920e7f0
f88f7b9
dbee26c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
// Copyright 2025 Dolthub, Inc. | ||
|
||
// 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 dtables | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"strings" | ||
|
||
"github.com/dolthub/dolt/go/cmd/dolt/cli" | ||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures" | ||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index" | ||
"github.com/dolthub/go-mysql-server/sql" | ||
sqlTypes "github.com/dolthub/go-mysql-server/sql/types" | ||
) | ||
|
||
type HelpTable struct { | ||
dbName string | ||
tableName string | ||
} | ||
|
||
var HelpTableTypes = []string{ | ||
"system_table", | ||
"procedure", | ||
"function", | ||
"variable", | ||
} | ||
|
||
// NewHelpTable creates a HelpTable | ||
func NewHelpTable(_ *sql.Context, dbName, tableName string) sql.Table { | ||
return &HelpTable{dbName: dbName, tableName: tableName} | ||
} | ||
|
||
// Name is a sql.Table interface function which returns the name of the table. | ||
func (ht *HelpTable) Name() string { | ||
return ht.tableName | ||
} | ||
|
||
// String is a sql.Table interface function which returns the name of the table. | ||
func (ht *HelpTable) String() string { | ||
return ht.tableName | ||
} | ||
|
||
// Schema is a sql.Table interface function that gets the sql.Schema of the help system table. | ||
func (ht *HelpTable) Schema() sql.Schema { | ||
return []*sql.Column{ | ||
{ | ||
Name: "target", | ||
Type: sqlTypes.TinyText, | ||
Source: ht.tableName, | ||
PrimaryKey: true, | ||
DatabaseSource: ht.dbName, | ||
}, | ||
{ | ||
Name: "type", | ||
Type: sqlTypes.MustCreateEnumType(HelpTableTypes, sql.Collation_Default), | ||
Source: ht.tableName, | ||
PrimaryKey: false, | ||
DatabaseSource: ht.dbName, | ||
}, | ||
{ | ||
zachmu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Name: "short_description", | ||
Type: sqlTypes.LongText, | ||
Source: ht.tableName, | ||
PrimaryKey: false, | ||
DatabaseSource: ht.dbName, | ||
}, | ||
{ | ||
Name: "long_description", | ||
Type: sqlTypes.LongText, | ||
Source: ht.tableName, | ||
PrimaryKey: false, | ||
DatabaseSource: ht.dbName, | ||
}, | ||
{ | ||
Name: "arguments", | ||
Type: sqlTypes.JSON, | ||
Source: ht.tableName, | ||
PrimaryKey: false, | ||
DatabaseSource: ht.dbName, | ||
}, | ||
} | ||
} | ||
|
||
// Collation implements the sql.Table interface. | ||
func (ht *HelpTable) Collation() sql.CollationID { | ||
return sql.Collation_Default | ||
} | ||
|
||
// Partitions is a sql.Table interface function that returns a partition | ||
// of the data. Currently the data is unpartitioned. | ||
func (ht *HelpTable) Partitions(*sql.Context) (sql.PartitionIter, error) { | ||
return index.SinglePartitionIterFromNomsMap(nil), nil | ||
} | ||
|
||
// PartitionRows is a sql.Table interface function that gets a row iterator for a partition. | ||
func (ht *HelpTable) PartitionRows(_ *sql.Context, _ sql.Partition) (sql.RowIter, error) { | ||
return NewHelpRowIter(), nil | ||
} | ||
|
||
type HelpRowIter struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best practice here is to use a pointer for the receiver type and not the fields |
||
idx *int | ||
rows *[]sql.Row | ||
} | ||
|
||
func NewHelpRowIter() HelpRowIter { | ||
idx := 0 | ||
var nilRows []sql.Row | ||
return HelpRowIter{idx: &idx, rows: &nilRows} | ||
} | ||
|
||
// DoltCommand is set in cmd/dolt/dolt.go to avoid circular dependency. | ||
var DoltCommand cli.SubCommandHandler | ||
|
||
func (itr HelpRowIter) Next(_ *sql.Context) (sql.Row, error) { | ||
if *itr.rows == nil { | ||
var err error | ||
*itr.rows, err = generateProcedureHelpRows(DoltCommand.Name(), DoltCommand.Subcommands) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
helpRows := *itr.rows | ||
|
||
if *itr.idx >= len(helpRows) { | ||
return nil, io.EOF | ||
} | ||
|
||
row := helpRows[*itr.idx] | ||
(*itr.idx)++ | ||
return row, nil | ||
} | ||
|
||
func (itr HelpRowIter) Close(_ *sql.Context) error { | ||
return nil | ||
} | ||
|
||
// generateProcedureHelpRows generates a sql row for each procedure that has an equivalent CLI command. | ||
func generateProcedureHelpRows(cmdStr string, subCommands []cli.Command) ([]sql.Row, error) { | ||
rows := []sql.Row{} | ||
|
||
for _, curr := range subCommands { | ||
if hidCmd, ok := curr.(cli.HiddenCommand); ok && hidCmd.Hidden() { | ||
continue | ||
} | ||
|
||
if subCmdHandler, ok := curr.(cli.SubCommandHandler); ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This loop can be simplified, because there are no procedures that correspond to sub commands. E.g. there is no |
||
if subCmdHandler.Unspecified != nil { | ||
newRows, err := generateProcedureHelpRows(cmdStr, []cli.Command{subCmdHandler.Unspecified}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
rows = append(rows, newRows...) | ||
} | ||
newRows, err := generateProcedureHelpRows(cmdStr+"_"+subCmdHandler.Name(), subCmdHandler.Subcommands) | ||
if err != nil { | ||
return nil, err | ||
} | ||
rows = append(rows, newRows...) | ||
} else { | ||
nameFormatted := fmt.Sprintf("%s_%s", cmdStr, strings.ReplaceAll(curr.Name(), "-", "_")) | ||
|
||
hasProcedure := false | ||
for _, procedure := range dprocedures.DoltProcedures { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would pull out a boolean |
||
if procedure.Name == nameFormatted { | ||
hasProcedure = true | ||
break | ||
} | ||
} | ||
|
||
docs := curr.Docs() | ||
|
||
if hasProcedure && docs != nil { | ||
argsMap := map[string]string{} | ||
for _, argHelp := range curr.Docs().ArgParser.ArgListHelp { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is missing all the options (flags) |
||
argsMap[argHelp[0]] = argHelp[1] | ||
} | ||
|
||
argsJson, err := json.Marshal(argsMap) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why, but something about this seems to be producing invalid json in a subtle and weird way. Check this out: db1/main> select json_pretty(arguments) from dolt_help where name = 'dolt_revert';
invalid data type for JSON data in argument 1 to function json_pretty; a JSON string or JSON type is required
db1/main> create table json_test (j json);
db1/main*> insert into json_test (select arguments from dolt_help where name = 'dolt_revert');
db1/main*> select json_pretty(j) from json_test;
+-----------------------------------------------------------------------------------------------------------------------------------+
| json_pretty(j)
|
+-----------------------------------------------------------------------------------------------------------------------------------+
| {
|
| "--author=\u003cauthor\u003e": "Specify an explicit author using the standard A U Thor \[email protected]\u003e format.", |
| "\u003crevision\u003e": "The commit revisions. If multiple revisions are given, they're applied in the order given."
|
| }
|
+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec) So whatever it is, it's getting handled by our round-trip into storage, but can't get processed by as returned by this table. This seems to affect all our json functions, so it needs to be fixed. Another example: db1/main*> select arguments->>"$.<revision>" from dolt_help where name = 'dolt_revert';
invalid data type for JSON data in argument 1 to function json_extract; a JSON string or JSON type is required
db1/main*> select j->>"$.<revision>" from json_test;
+--------------------------------------------------------------------------------------------+
| j->>"$.<revision>" |
+--------------------------------------------------------------------------------------------+
| The commit revisions. If multiple revisions are given, they're applied in the order given. |
+--------------------------------------------------------------------------------------------+
1 row in set (0.00 sec) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it has something to do with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Above examples work great now, but there's a related problem where ascii command characters (used for bolding on the terminal) are making it into the SQL output like this:
I think what we need here is the |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rows = append(rows, sql.NewRow( | ||
nameFormatted, | ||
"procedure", | ||
curr.Docs().ShortDesc, | ||
curr.Docs().LongDesc, | ||
argsJson, | ||
)) | ||
} | ||
} | ||
} | ||
|
||
return rows, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would call this
name
instead