Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0af2edb
impl client-side binary-as-hex
elianddb Jul 24, 2025
e782684
Add expect files for binary-as-hex tests
elianddb Jul 24, 2025
77b0e3a
Complete binary-as-hex implementation for local and server connections
elianddb Jul 25, 2025
a3e6bba
mv impl to print layer
elianddb Jul 25, 2025
123f24d
add non-filter tests and interactive test for local db
elianddb Jul 28, 2025
130ef21
rm extra imports
elianddb Jul 28, 2025
2393ca3
bypass column validation on binaryAsHex flag print
elianddb Jul 28, 2025
debe8e8
switch to concrete type
elianddb Jul 28, 2025
cf1ec51
[ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/upda…
elianddb Jul 28, 2025
14b4292
add review compile-time interface check, err handling, and test cleanup
elianddb Jul 30, 2025
ae9f979
Update sql.go rm extra comments
elianddb Jul 30, 2025
05068ba
add miss dependencies for win and comments
elianddb Jul 30, 2025
c1719f1
rm expect install check in case of malformed installations
elianddb Jul 30, 2025
5157153
add direct link to issue
elianddb Jul 30, 2025
ef549b9
Merge latest main branch into binary-as-hex feature branch
elianddb Jul 30, 2025
65e15a8
add miss || false
elianddb Jul 30, 2025
0ae19fd
add no_lambda tag
elianddb Jul 30, 2025
04d13dc
[ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/upda…
elianddb Jul 30, 2025
85b2f27
Merge remote-tracking branch 'origin/main' into elianddb/9554-fix-var…
elianddb Jul 30, 2025
8b8f0b2
Merge latest main: accept dependency updates
elianddb Jul 30, 2025
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
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/blame.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (cmd BlameCmd) Exec(ctx context.Context, commandStr string, args []string,
return 1
}

err = engine.PrettyPrintResults(sqlCtx, engine.FormatTabular, schema, ri, false, false, true)
err = engine.PrettyPrintResults(sqlCtx, engine.FormatTabular, schema, ri, false, false, true, false)
if err != nil {
iohelp.WriteLine(cli.CliOut, err.Error())
return 1
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/cvcmds/verify_constraints.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func printViolationsForTable(ctx *sql.Context, dbName, tblName string, tbl *dolt

limitItr := &sqlLimitIter{itr: sqlItr, limit: 50}

err = engine.PrettyPrintResults(ctx, engine.FormatTabular, sqlSch, limitItr, false, false, false)
err = engine.PrettyPrintResults(ctx, engine.FormatTabular, sqlSch, limitItr, false, false, false, false)
if err != nil {
return errhand.BuildDError("Error outputting rows").AddCause(err).Build()
}
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,5 +436,5 @@ func execDebugMode(ctx *sql.Context, qryist cli.Queryist, queryFile *os.File, co
}()
input := bufio.NewReader(transform.NewReader(queryFile, textunicode.BOMOverride(transform.Nop)))

return execBatchMode(ctx, qryist, input, continueOnErr, format)
return execBatchMode(ctx, qryist, input, continueOnErr, format, false)
}
64 changes: 59 additions & 5 deletions go/cmd/dolt/commands/engine/sql_print.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/vitess/go/sqltypes"
"github.com/fatih/color"

"github.com/dolthub/dolt/go/cmd/dolt/cli"
Expand Down Expand Up @@ -56,16 +57,16 @@ const (
)

// PrettyPrintResults prints the result of a query in the format provided
func PrettyPrintResults(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, pageResults, showWarnings, printOkResult bool) (rerr error) {
return prettyPrintResultsWithSummary(ctx, resultFormat, sqlSch, rowIter, PrintNoSummary, pageResults, showWarnings, printOkResult)
func PrettyPrintResults(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, pageResults, showWarnings, printOkResult, binaryAsHex bool) (rerr error) {
return prettyPrintResultsWithSummary(ctx, resultFormat, sqlSch, rowIter, PrintNoSummary, pageResults, showWarnings, printOkResult, binaryAsHex)
}

// PrettyPrintResultsExtended prints the result of a query in the format provided, including row count and timing info
func PrettyPrintResultsExtended(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, pageResults, showWarnings, printOkResult bool) (rerr error) {
return prettyPrintResultsWithSummary(ctx, resultFormat, sqlSch, rowIter, PrintRowCountAndTiming, pageResults, showWarnings, printOkResult)
func PrettyPrintResultsExtended(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, pageResults, showWarnings, printOkResult, binaryAsHex bool) (rerr error) {
return prettyPrintResultsWithSummary(ctx, resultFormat, sqlSch, rowIter, PrintRowCountAndTiming, pageResults, showWarnings, printOkResult, binaryAsHex)
}

func prettyPrintResultsWithSummary(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, summary PrintSummaryBehavior, pageResults, showWarnings, printOkResult bool) (rerr error) {
func prettyPrintResultsWithSummary(ctx *sql.Context, resultFormat PrintResultFormat, sqlSch sql.Schema, rowIter sql.RowIter, summary PrintSummaryBehavior, pageResults, showWarnings, printOkResult, binaryAsHex bool) (rerr error) {
defer func() {
closeErr := rowIter.Close(ctx)
if rerr == nil && closeErr != nil {
Expand Down Expand Up @@ -126,6 +127,11 @@ func prettyPrintResultsWithSummary(ctx *sql.Context, resultFormat PrintResultFor
}
}

// Wrap iterator with binary-to-hex transformation if needed
if binaryAsHex {
rowIter = newBinaryHexIterator(rowIter, sqlSch)
}

numRows, err = writeResultSet(ctx, rowIter, wr)
}

Expand Down Expand Up @@ -197,6 +203,54 @@ func printResultSetSummary(numRows int, numWarnings uint16, warningsList string,
return nil
}

// binaryHexIterator wraps a row iterator and transforms binary data to hex format
type binaryHexIterator struct {
inner sql.RowIter
schema sql.Schema
}

var _ sql.RowIter = (*binaryHexIterator)(nil)

// newBinaryHexIterator creates a new iterator that transforms binary data to hex format
func newBinaryHexIterator(inner sql.RowIter, schema sql.Schema) sql.RowIter {
return &binaryHexIterator{
inner: inner,
schema: schema,
}
}

// Next returns the next row with binary data transformed to hex format
func (iter *binaryHexIterator) Next(ctx *sql.Context) (sql.Row, error) {
rowData, err := iter.inner.Next(ctx)
if err != nil {
return nil, err
}

// TODO: Add support for BLOB types (TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB) and BIT type
for i, val := range rowData {
if val != nil && i < len(iter.schema) {
switch iter.schema[i].Type.Type() {
case sqltypes.Binary, sqltypes.VarBinary:
switch v := val.(type) {
case []byte: // hex fmt is explicitly upper case
rowData[i] = sqlutil.BinaryAsHexDisplayValue(fmt.Sprintf("0x%X", v))
case string: // handles results from sql-server; MySQL wire protocol returns strings
rowData[i] = sqlutil.BinaryAsHexDisplayValue(fmt.Sprintf("0x%X", []byte(v)))
default:
return nil, fmt.Errorf("unexpected type %T for binary column %s", val, iter.schema[i].Name)
}
}
}
}

return rowData, nil
}

// Close closes the wrapped iterator and releases any resources.
func (iter *binaryHexIterator) Close(ctx *sql.Context) error {
return iter.inner.Close(ctx)
}

// writeResultSet drains the iterator given, printing rows from it to the writer given. Returns the number of rows.
func writeResultSet(ctx *sql.Context, rowIter sql.RowIter, wr table.SqlRowWriter) (int, error) {
i := 0
Expand Down
2 changes: 1 addition & 1 deletion go/cmd/dolt/commands/schcmds/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (cmd TagsCmd) Exec(ctx context.Context, commandStr string, args []string, d
}

sqlCtx := sql.NewContext(ctx)
err = engine.PrettyPrintResults(sqlCtx, outputFmt, headerSchema, sql.RowsToRowIter(rows...), false, false, false)
err = engine.PrettyPrintResults(sqlCtx, outputFmt, headerSchema, sql.RowsToRowIter(rows...), false, false, false, false)

return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
54 changes: 35 additions & 19 deletions go/cmd/dolt/commands/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ const (
ProfileFlag = "profile"
timeFlag = "time"
outputFlag = "output"
binaryAsHexFlag = "binary-as-hex"
skipBinaryAsHexFlag = "skip-binary-as-hex"
// TODO: Consider simplifying to use MySQL's skip pattern with single flag definition
// MySQL handles both --binary-as-hex and --skip-binary-as-hex with one option definition
// and uses disabled_my_option to distinguish between enable/disable

welcomeMsg = `# Welcome to the DoltSQL shell.
# Statements must be terminated with ';'.
Expand Down Expand Up @@ -147,6 +152,9 @@ func (cmd SqlCmd) ArgParser() *argparser.ArgParser {
ap.SupportsFlag(BatchFlag, "b", "Use to enable more efficient batch processing for large SQL import scripts. This mode is no longer supported and this flag is a no-op. To speed up your SQL imports, use either LOAD DATA, or structure your SQL import script to insert many rows per statement.")
ap.SupportsFlag(continueFlag, "c", "Continue running queries on an error. Used for batch mode only.")
ap.SupportsString(fileInputFlag, "f", "input file", "Execute statements from the file given.")
ap.SupportsFlag(binaryAsHexFlag, "", "Print binary data as hex. Enabled by default for interactive terminals.")
// TODO: MySQL uses a skip- pattern for negating flags and doesn't show them in help
ap.SupportsFlag(skipBinaryAsHexFlag, "", "Disable binary data as hex output.")
return ap
}

Expand Down Expand Up @@ -213,6 +221,12 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}

// Determine binary-as-hex behavior from flags (default false for non-interactive modes)
binaryAsHex := apr.Contains(binaryAsHexFlag)
if binaryAsHex && apr.Contains(skipBinaryAsHexFlag) { // We stray from MYSQL here to make usage clear for users
return HandleVErrAndExitCode(errhand.BuildDError("cannot use both --%s and --%s", binaryAsHexFlag, skipBinaryAsHexFlag).Build(), usage)
}

queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx)
if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
Expand All @@ -223,17 +237,16 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE

if query, queryOK := apr.GetValue(QueryFlag); queryOK {
if apr.Contains(saveFlag) {
return SaveQuery(sqlCtx, queryist, apr, query, format, usage)
return SaveQuery(sqlCtx, queryist, apr, query, format, usage, binaryAsHex)
}
return queryMode(sqlCtx, queryist, apr, query, format, usage)
return queryMode(sqlCtx, queryist, apr, query, format, usage, binaryAsHex)
} else if savedQueryName, exOk := apr.GetValue(executeFlag); exOk {
return executeSavedQuery(sqlCtx, queryist, savedQueryName, format, usage)
return executeSavedQuery(sqlCtx, queryist, savedQueryName, format, usage, binaryAsHex)
} else if apr.Contains(listSavedFlag) {
return listSavedQueries(sqlCtx, queryist, format, usage)
} else {
// Run in either batch mode for piped input, or shell mode for interactive
isTty := false

fi, err := os.Stdin.Stat()
if err != nil {
if !osutil.IsWindows {
Expand Down Expand Up @@ -265,13 +278,15 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE
}

if isTty {
err := execShell(sqlCtx, queryist, format, cliCtx)
// In shell mode, default to hex format unless explicitly disabled
shellBinaryAsHex := !apr.Contains(skipBinaryAsHexFlag)
err := execShell(sqlCtx, queryist, format, cliCtx, shellBinaryAsHex)
if err != nil {
return sqlHandleVErrAndExitCode(queryist, errhand.VerboseErrorFromError(err), usage)
}
} else {
input = transform.NewReader(input, textunicode.BOMOverride(transform.Nop))
err := execBatchMode(sqlCtx, queryist, input, continueOnError, format)
err := execBatchMode(sqlCtx, queryist, input, continueOnError, format, binaryAsHex)
if err != nil {
return sqlHandleVErrAndExitCode(queryist, errhand.VerboseErrorFromError(err), usage)
}
Expand Down Expand Up @@ -341,10 +356,10 @@ func (cmd SqlCmd) handleLegacyArguments(ap *argparser.ArgParser, commandStr stri

func listSavedQueries(ctx *sql.Context, qryist cli.Queryist, format engine.PrintResultFormat, usage cli.UsagePrinter) int {
query := "SELECT * FROM " + doltdb.DoltQueryCatalogTableName
return sqlHandleVErrAndExitCode(qryist, execSingleQuery(ctx, qryist, query, format), usage)
return sqlHandleVErrAndExitCode(qryist, execSingleQuery(ctx, qryist, query, format, false), usage)
}

func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, savedQueryName string, format engine.PrintResultFormat, usage cli.UsagePrinter) int {
func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, savedQueryName string, format engine.PrintResultFormat, usage cli.UsagePrinter, binaryAsHex bool) int {
var buffer bytes.Buffer
buffer.WriteString("SELECT query FROM dolt_query_catalog where id = ?")
searchQuery, err := dbr.InterpolateForDialect(buffer.String(), []interface{}{savedQueryName}, dialect.MySQL)
Expand All @@ -370,7 +385,7 @@ func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, savedQueryName str
}

cli.PrintErrf("Executing saved query '%s':\n%s\n", savedQueryName, query)
return sqlHandleVErrAndExitCode(qryist, execSingleQuery(ctx, qryist, query, format), usage)
return sqlHandleVErrAndExitCode(qryist, execSingleQuery(ctx, qryist, query, format, binaryAsHex), usage)
}

func queryMode(
Expand All @@ -380,23 +395,23 @@ func queryMode(
query string,
format engine.PrintResultFormat,
usage cli.UsagePrinter,
binaryAsHex bool,
) int {

_, continueOnError := apr.GetValue(continueFlag)

input := strings.NewReader(query)
err := execBatchMode(ctx, qryist, input, continueOnError, format)
err := execBatchMode(ctx, qryist, input, continueOnError, format, binaryAsHex)
if err != nil {
return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage)
}

return 0
}

func SaveQuery(ctx *sql.Context, qryist cli.Queryist, apr *argparser.ArgParseResults, query string, format engine.PrintResultFormat, usage cli.UsagePrinter) int {
func SaveQuery(ctx *sql.Context, qryist cli.Queryist, apr *argparser.ArgParseResults, query string, format engine.PrintResultFormat, usage cli.UsagePrinter, binaryAsHex bool) int {
saveName := apr.GetValueOrDefault(saveFlag, "")

verr := execSingleQuery(ctx, qryist, query, format)
verr := execSingleQuery(ctx, qryist, query, format, binaryAsHex)
if verr != nil {
return sqlHandleVErrAndExitCode(qryist, verr, usage)
}
Expand Down Expand Up @@ -435,6 +450,7 @@ func execSingleQuery(
qryist cli.Queryist,
query string,
format engine.PrintResultFormat,
binaryAsHex bool,
) errhand.VerboseError {

sqlSch, rowIter, _, err := processQuery(sqlCtx, query, qryist)
Expand All @@ -443,7 +459,7 @@ func execSingleQuery(
}

if rowIter != nil {
err = engine.PrettyPrintResults(sqlCtx, format, sqlSch, rowIter, false, false, false)
err = engine.PrettyPrintResults(sqlCtx, format, sqlSch, rowIter, false, false, false, binaryAsHex)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
Expand Down Expand Up @@ -601,7 +617,7 @@ func validateSqlArgs(apr *argparser.ArgParseResults) error {
}

// execBatchMode runs all the queries in the input reader
func execBatchMode(ctx *sql.Context, qryist cli.Queryist, input io.Reader, continueOnErr bool, format engine.PrintResultFormat) error {
func execBatchMode(ctx *sql.Context, qryist cli.Queryist, input io.Reader, continueOnErr bool, format engine.PrintResultFormat, binaryAsHex bool) error {
scanner := NewStreamScanner(input)
var query string
for scanner.Scan() {
Expand Down Expand Up @@ -649,7 +665,7 @@ func execBatchMode(ctx *sql.Context, qryist cli.Queryist, input io.Reader, conti
fileReadProg.printNewLineIfNeeded()
}
}
err = engine.PrettyPrintResults(ctx, format, sqlSch, rowIter, false, false, false)
err = engine.PrettyPrintResults(ctx, format, sqlSch, rowIter, false, false, false, binaryAsHex)
if err != nil {
err = buildBatchSqlErr(scanner.state.statementStartLine, query, err)
if !continueOnErr {
Expand All @@ -675,7 +691,7 @@ func buildBatchSqlErr(stmtStartLine int, query string, err error) error {

// execShell starts a SQL shell. Returns when the user exits the shell. The Root of the sqlEngine may
// be updated by any queries which were processed.
func execShell(sqlCtx *sql.Context, qryist cli.Queryist, format engine.PrintResultFormat, cliCtx cli.CliContext) error {
func execShell(sqlCtx *sql.Context, qryist cli.Queryist, format engine.PrintResultFormat, cliCtx cli.CliContext, binaryAsHex bool) error {
_ = iohelp.WriteLine(cli.CliOut, welcomeMsg)
historyFile := filepath.Join(".sqlhistory") // history file written to working dir

Expand Down Expand Up @@ -832,9 +848,9 @@ func execShell(sqlCtx *sql.Context, qryist cli.Queryist, format engine.PrintResu
} else if rowIter != nil {
switch closureFormat {
case engine.FormatTabular, engine.FormatVertical:
err = engine.PrettyPrintResultsExtended(sqlCtx, closureFormat, sqlSch, rowIter, pagerEnabled, toggleWarnings, true)
err = engine.PrettyPrintResultsExtended(sqlCtx, closureFormat, sqlSch, rowIter, pagerEnabled, toggleWarnings, true, binaryAsHex)
default:
err = engine.PrettyPrintResults(sqlCtx, closureFormat, sqlSch, rowIter, pagerEnabled, toggleWarnings, true)
err = engine.PrettyPrintResults(sqlCtx, closureFormat, sqlSch, rowIter, pagerEnabled, toggleWarnings, true, binaryAsHex)
}
} else {
if _, isUseStmt := sqlStmt.(*sqlparser.Use); isUseStmt {
Expand Down
16 changes: 8 additions & 8 deletions go/cmd/dolt/commands/sqlserver/queryist_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"strings"

"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
"github.com/dolthub/vitess/go/vt/sqlparser"
"github.com/go-sql-driver/mysql"
"github.com/gocraft/dbr/v2"
Expand All @@ -32,6 +31,7 @@ import (
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/libraries/doltcore/servercfg"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
)
Expand Down Expand Up @@ -147,18 +147,18 @@ type MysqlRowWrapper struct {
var _ sql.RowIter = (*MysqlRowWrapper)(nil)

func NewMysqlRowWrapper(sqlRows *sql2.Rows) (*MysqlRowWrapper, error) {
colNames, err := sqlRows.Columns()
colTypes, err := sqlRows.ColumnTypes()
if err != nil {
return nil, err
}
schema := make(sql.Schema, len(colNames))
vRow := make([]*string, len(colNames))
iRow := make([]interface{}, len(colNames))
schema := make(sql.Schema, len(colTypes))
vRow := make([]*string, len(colTypes))
iRow := make([]interface{}, len(colTypes))
rows := make([]sql.Row, 0)
for i, colName := range colNames {
for i, colType := range colTypes {
schema[i] = &sql.Column{
Name: colName,
Type: types.LongText,
Name: colType.Name(),
Type: sqlutil.DatabaseTypeNameToSqlType(colType.DatabaseTypeName()),
Nullable: true,
}
iRow[i] = &vRow[i]
Expand Down
Loading
Loading