From bc75d263c85622adc6b7ff744b725ad58b9fe5db Mon Sep 17 00:00:00 2001 From: Nathan Gabrielson Date: Wed, 23 Jul 2025 12:54:07 -0700 Subject: [PATCH] Many changes --- go/cmd/dolt/commands/sql.go | 73 ++-- .../sqle/dtables/query_catalog_table.go | 388 +----------------- integration-tests/bats/query-catalog.bats | 131 +++--- integration-tests/bats/sql-server.bats | 25 ++ 4 files changed, 116 insertions(+), 501 deletions(-) diff --git a/go/cmd/dolt/commands/sql.go b/go/cmd/dolt/commands/sql.go index 7ac69269c5..fc2e942ee6 100644 --- a/go/cmd/dolt/commands/sql.go +++ b/go/cmd/dolt/commands/sql.go @@ -15,9 +15,12 @@ package commands import ( + "bytes" "context" "fmt" "github.com/dolthub/dolt/go/store/val" + "github.com/gocraft/dbr/v2" + "github.com/gocraft/dbr/v2/dialect" "io" "os" "os/signal" @@ -157,7 +160,7 @@ func (cmd SqlCmd) EventType() eventsapi.ClientEventType { // that parameter is not provided there is additional error handling within this command to make sure that this was in // fact run from within a dolt data repository directory. func (cmd SqlCmd) RequiresRepo() bool { - return true + return false } // Exec executes the command @@ -220,13 +223,13 @@ func (cmd SqlCmd) Exec(ctx context.Context, commandStr string, args []string, dE if query, queryOK := apr.GetValue(QueryFlag); queryOK { if apr.Contains(saveFlag) { - return execSaveQuery(sqlCtx, dEnv, queryist, apr, query, format, usage) + return SaveQuery(sqlCtx, queryist, apr, query, format, usage) } return queryMode(sqlCtx, queryist, apr, query, format, usage) } else if savedQueryName, exOk := apr.GetValue(executeFlag); exOk { - return executeSavedQuery(sqlCtx, queryist, dEnv, savedQueryName, format, usage) + return executeSavedQuery(sqlCtx, queryist, savedQueryName, format, usage) } else if apr.Contains(listSavedFlag) { - return listSavedQueries(sqlCtx, queryist, dEnv, format, usage) + return listSavedQueries(sqlCtx, queryist, format, usage) } else { // Run in either batch mode for piped input, or shell mode for interactive isTty := false @@ -336,33 +339,16 @@ func (cmd SqlCmd) handleLegacyArguments(ap *argparser.ArgParser, commandStr stri } -func listSavedQueries(ctx *sql.Context, qryist cli.Queryist, dEnv *env.DoltEnv, format engine.PrintResultFormat, usage cli.UsagePrinter) int { - if !dEnv.Valid() { - return sqlHandleVErrAndExitCode(qryist, errhand.BuildDError("error: --%s must be used in a dolt database directory.", listSavedFlag).Build(), usage) - } - - workingRoot, err := dEnv.WorkingRoot(ctx) - if err != nil { - return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) - } - - hasQC, err := workingRoot.HasTable(ctx, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}) - - if err != nil { - verr := errhand.BuildDError("error: Failed to read from repository.").AddCause(err).Build() - return sqlHandleVErrAndExitCode(qryist, verr, usage) - } - - if !hasQC { - return 0 - } - +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) } -func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, _ *env.DoltEnv, savedQueryName string, format engine.PrintResultFormat, usage cli.UsagePrinter) int { - searchQuery := fmt.Sprintf("SELECT query FROM dolt_query_catalog where name = '%s'", savedQueryName) +func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, savedQueryName string, format engine.PrintResultFormat, usage cli.UsagePrinter) int { + var buffer bytes.Buffer + buffer.WriteString("SELECT query FROM dolt_query_catalog where name = ?") + searchQuery, err := dbr.InterpolateForDialect(buffer.String(), []interface{}{savedQueryName}, dialect.MySQL) + rows, err := GetRowsForSql(qryist, ctx, searchQuery) if err != nil { return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) @@ -371,12 +357,19 @@ func executeSavedQuery(ctx *sql.Context, qryist cli.Queryist, _ *env.DoltEnv, sa return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) } - query, err := rows[0][0].(*val.TextStorage).Unwrap(ctx) - if err != nil { - return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) + var query string + if ts, ok := rows[0][0].(*val.TextStorage); ok { + query, err = ts.Unwrap(ctx) + if err != nil { + return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) + } + } else { + if s, ok := rows[0][0].(string); ok { + query = s + } } - cli.PrintErrf("Executing saved query: '%s': \n%s\n", savedQueryName, query) + cli.PrintErrf("Executing saved query '%s': \n%s\n", savedQueryName, query) return sqlHandleVErrAndExitCode(qryist, execSingleQuery(ctx, qryist, query, format), usage) } @@ -400,7 +393,7 @@ func queryMode( return 0 } -func execSaveQuery(ctx *sql.Context, _ *env.DoltEnv, 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) int { saveName := apr.GetValueOrDefault(saveFlag, "") verr := execSingleQuery(ctx, qryist, query, format) @@ -409,17 +402,27 @@ func execSaveQuery(ctx *sql.Context, _ *env.DoltEnv, qryist cli.Queryist, apr *a } order := int32(1) + var ok bool rows, err := GetRowsForSql(qryist, ctx, "SELECT MAX(display_order) FROM dolt_query_catalog") if err != nil { return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) } if len(rows) > 0 && rows[0][0] != nil { - order = rows[0][0].(int32) + 1 + if order, ok = rows[0][0].(int32); !ok { + err = fmt.Errorf("could not get display_order from dolt_query_catalog") + return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) + } + order++ } saveMessage := apr.GetValueOrDefault(messageFlag, "") - insertQuery := fmt.Sprintf("INSERT INTO dolt_query_catalog VALUES ('%s', %d, '%s', '%s', '%s') "+ - "ON DUPLICATE KEY UPDATE query = '%s', description = '%s'", saveName, order, saveName, query, saveMessage, query, saveMessage) + var buffer bytes.Buffer + buffer.WriteString("INSERT INTO dolt_query_catalog VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE query = ?, description = ?") + params := []interface{}{saveName, order, saveName, query, saveMessage, query, saveMessage} + insertQuery, err := dbr.InterpolateForDialect(buffer.String(), params, dialect.MySQL) + if err != nil { + return sqlHandleVErrAndExitCode(qryist, errhand.VerboseErrorFromError(err), usage) + } _, err = GetRowsForSql(qryist, ctx, insertQuery) diff --git a/go/libraries/doltcore/sqle/dtables/query_catalog_table.go b/go/libraries/doltcore/sqle/dtables/query_catalog_table.go index e22a8ab6b4..332f357922 100644 --- a/go/libraries/doltcore/sqle/dtables/query_catalog_table.go +++ b/go/libraries/doltcore/sqle/dtables/query_catalog_table.go @@ -92,13 +92,13 @@ func NewEmptyQueryCatalogTable(_ *sql.Context) sql.Table { return &QueryCatalogTable{} } -func (qt *QueryCatalogTable) Replacer(ctx *sql.Context) sql.RowReplacer { +func (qt *QueryCatalogTable) Replacer(_ *sql.Context) sql.RowReplacer { return newQueryCatalogWriter(qt) } // Updater returns a RowUpdater for this table. The RowUpdater will have Update called once for each row to be // updated, followed by a call to Close() when all rows have been processed. -func (qt *QueryCatalogTable) Updater(ctx *sql.Context) sql.RowUpdater { +func (qt *QueryCatalogTable) Updater(_ *sql.Context) sql.RowUpdater { return newQueryCatalogWriter(qt) } @@ -116,12 +116,12 @@ func (qt *QueryCatalogTable) Deleter(*sql.Context) sql.RowDeleter { // IndexedAccess implements IndexAddressableTable, but QueryCatalogTables has no indexes. // Thus, this should never be called. -func (qt *QueryCatalogTable) IndexedAccess(ctx *sql.Context, lookup sql.IndexLookup) sql.IndexedTable { +func (qt *QueryCatalogTable) IndexedAccess(_ *sql.Context, _ sql.IndexLookup) sql.IndexedTable { panic("Unreachable") } // GetIndexes implements IndexAddressableTable, but QueryCatalogTables has no indexes. -func (qt *QueryCatalogTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) { +func (qt *QueryCatalogTable) GetIndexes(_ *sql.Context) ([]sql.Index, error) { return nil, nil } @@ -284,383 +284,3 @@ func (qw *queryCatalogWriter) Close(ctx *sql.Context) error { } return nil } - -/*var queryCatalogCols = schema.NewColCollection( - // QueryCatalogIdCol is the name of the primary key column of the query catalog table - schema.NewColumn(doltdb.QueryCatalogIdCol, schema.QueryCatalogIdTag, types.StringKind, true, schema.NotNullConstraint{}), - // QueryCatalogOrderCol is the column containing the order of the queries in the catalog - schema.NewColumn(doltdb.QueryCatalogOrderCol, schema.QueryCatalogOrderTag, types.UintKind, false, schema.NotNullConstraint{}), - // QueryCatalogNameCol is the name of the column containing the name of a query in the catalog - // TODO: parser won't handle a reserved word here, but it should. Only an issue for create table statements. - schema.NewColumn(doltdb.QueryCatalogNameCol, schema.QueryCatalogNameTag, types.StringKind, false), - // QueryCatalogQueryCol is the name of the column containing the query of a catalog entry - schema.NewColumn(doltdb.QueryCatalogQueryCol, schema.QueryCatalogQueryTag, types.StringKind, false), - // QueryCatalogDescriptionCol is the name of the column containing the description of a query in the catalog - schema.NewColumn(doltdb.QueryCatalogDescriptionCol, schema.QueryCatalogDescriptionTag, types.StringKind, false), -) - -var ErrQueryNotFound = errors.NewKind("Query '%s' not found") - -type SavedQuery struct { - ID string - Name string - Query string - Description string - Order uint64 -} - -func savedQueryFromKVProlly(id string, value val.Tuple) (SavedQuery, error) { - orderVal, ok := catalogVd.GetUint64(0, value) - if !ok { - orderVal = 0 - } - nameVal, ok := catalogVd.GetString(1, value) - if !ok { - nameVal = "" - } - queryVal, ok := catalogVd.GetString(2, value) - if !ok { - nameVal = "" - } - descVal, ok := catalogVd.GetString(3, value) - if !ok { - descVal = "" - } - - return SavedQuery{ - ID: id, - Name: nameVal, - Query: queryVal, - Description: descVal, - Order: orderVal, - }, nil -} - -func savedQueryFromKVNoms(id string, valTuple types.Tuple) (SavedQuery, error) { - tv, err := row.ParseTaggedValues(valTuple) - - if err != nil { - return SavedQuery{}, err - } - - nameVal := tv.GetWithDefault(schema.QueryCatalogNameTag, types.String("")) - queryVal := tv.GetWithDefault(schema.QueryCatalogQueryTag, types.String("")) - descVal := tv.GetWithDefault(schema.QueryCatalogDescriptionTag, types.String("")) - orderVal := tv.GetWithDefault(schema.QueryCatalogOrderTag, types.Uint(0)) - - return SavedQuery{ - ID: id, - Name: string(nameVal.(types.String)), - Query: string(queryVal.(types.String)), - Description: string(descVal.(types.String)), - Order: uint64(orderVal.(types.Uint)), - }, nil -} - -func (sq SavedQuery) asRow(nbf *types.NomsBinFormat) (row.Row, error) { - taggedVals := make(row.TaggedValues) - taggedVals[schema.QueryCatalogIdTag] = types.String(sq.ID) - taggedVals[schema.QueryCatalogOrderTag] = types.Uint(sq.Order) - taggedVals[schema.QueryCatalogNameTag] = types.String(sq.Name) - taggedVals[schema.QueryCatalogQueryTag] = types.String(sq.Query) - taggedVals[schema.QueryCatalogDescriptionTag] = types.String(sq.Description) - - return row.New(nbf, DoltQueryCatalogSchema, taggedVals) -} - -var DoltQueryCatalogSchema = schema.MustSchemaFromCols(queryCatalogCols) - -// system tables do not contain addressable columns, and do not require nodestore access. -var catalogKd = DoltQueryCatalogSchema.GetKeyDescriptor(nil) -var catalogVd = DoltQueryCatalogSchema.GetValueDescriptor(nil) - -// Creates the query catalog table if it doesn't exist. -func createQueryCatalogIfNotExists(ctx context.Context, root doltdb.RootValue) (doltdb.RootValue, error) { - _, ok, err := root.GetTable(ctx, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}) - if err != nil { - return nil, err - } - - if !ok { - return doltdb.CreateEmptyTable(ctx, root, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}, DoltQueryCatalogSchema) - } - - return root, nil -} - -// NewQueryCatalogEntryWithRandID saves a new entry in the query catalog table and returns the new root value. An ID will be -// chosen automatically. -func NewQueryCatalogEntryWithRandID(ctx context.Context, root doltdb.RootValue, name, query, description string) (SavedQuery, doltdb.RootValue, error) { - uid, err := uuid.NewRandom() - if err != nil { - return SavedQuery{}, nil, err - } - - // Use the last 12 hex digits of the uuid for the ID. - uidStr := uid.String() - id := uidStr[len(uidStr)-12:] - - return newQueryCatalogEntry(ctx, root, id, name, query, description) -} - -// NewQueryCatalogEntryWithNameAsID saves an entry in the query catalog table and returns the new root value. If an -// entry with the given name is already present, it will be overwritten. -func NewQueryCatalogEntryWithNameAsID(ctx context.Context, root doltdb.RootValue, name, query, description string) (SavedQuery, doltdb.RootValue, error) { - return newQueryCatalogEntry(ctx, root, name, name, query, description) -} - -func newQueryCatalogEntry(ctx context.Context, root doltdb.RootValue, id, name, query, description string) (SavedQuery, doltdb.RootValue, error) { - root, err := createQueryCatalogIfNotExists(ctx, root) - if err != nil { - return SavedQuery{}, nil, err - } - - tbl, _, err := root.GetTable(ctx, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}) - if err != nil { - return SavedQuery{}, nil, err - } - - var sq SavedQuery - var newTable *doltdb.Table - if types.IsFormat_DOLT(tbl.Format()) { - sq, newTable, err = newQueryCatalogEntryProlly(ctx, tbl, id, name, query, description) - } else { - sq, newTable, err = newQueryCatalogEntryNoms(ctx, tbl, id, name, query, description) - } - if err != nil { - return SavedQuery{}, nil, err - } - - root, err = root.PutTable(ctx, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}, newTable) - - if err != nil { - return SavedQuery{}, nil, err - } - - return sq, root, err -} - -func newQueryCatalogEntryNoms(ctx context.Context, tbl *doltdb.Table, id, name, query, description string) (SavedQuery, *doltdb.Table, error) { - data, err := tbl.GetNomsRowData(ctx) - if err != nil { - return SavedQuery{}, nil, err - } - - order := getMaxQueryOrderNoms(data, ctx) + 1 - existingSQ, err := retrieveFromQueryCatalogNoms(ctx, tbl, id) - - if err != nil { - if !ErrQueryNotFound.Is(err) { - return SavedQuery{}, nil, err - } - } else { - order = existingSQ.Order - } - - sq := SavedQuery{ - ID: id, - Name: name, - Query: query, - Description: description, - Order: order, - } - - r, err := sq.asRow(tbl.Format()) - if err != nil { - return SavedQuery{}, nil, err - } - - me := data.Edit() - me.Set(r.NomsMapKey(DoltQueryCatalogSchema), r.NomsMapValue(DoltQueryCatalogSchema)) - - updatedTable, err := me.Map(ctx) - if err != nil { - return SavedQuery{}, nil, err - } - - newTable, err := tbl.UpdateNomsRows(ctx, updatedTable) - if err != nil { - return SavedQuery{}, nil, err - } - - return sq, newTable, nil -} - -func newQueryCatalogEntryProlly(ctx context.Context, tbl *doltdb.Table, id, name, query, description string) (SavedQuery, *doltdb.Table, error) { - idx, err := tbl.GetRowData(ctx) - if err != nil { - return SavedQuery{}, nil, err - } - m, err := durable.ProllyMapFromIndex(idx) - if err != nil { - return SavedQuery{}, nil, err - } - - existingSQ, err := retrieveFromQueryCatalogProlly(ctx, tbl, id) - if err != nil && !ErrQueryNotFound.Is(err) { - return SavedQuery{}, nil, err - } - - var order uint64 - if ErrQueryNotFound.Is(err) { - order, err = getMaxQueryOrderProlly(ctx, m) - if err != nil { - return SavedQuery{}, nil, err - } - order++ - } else { - order = existingSQ.Order - } - - kb := val.NewTupleBuilder(catalogKd, m.NodeStore()) - vb := val.NewTupleBuilder(catalogVd, m.NodeStore()) - kb.PutString(0, id) - k, err := kb.Build(m.Pool()) - if err != nil { - return SavedQuery{}, nil, err - } - - vb.PutUint64(0, order) - vb.PutString(1, name) - vb.PutString(2, query) - vb.PutString(3, description) - v, err := vb.Build(m.Pool()) - if err != nil { - return SavedQuery{}, nil, err - } - - mut := m.Mutate() - err = mut.Put(ctx, k, v) - if err != nil { - return SavedQuery{}, nil, err - } - m, err = mut.Map(ctx) - if err != nil { - return SavedQuery{}, nil, err - } - idx = durable.IndexFromProllyMap(m) - - tbl, err = tbl.UpdateRows(ctx, idx) - if err != nil { - return SavedQuery{}, nil, err - } - - return SavedQuery{ - ID: id, - Name: name, - Query: query, - Description: description, - Order: order, - }, tbl, nil -} - -func RetrieveFromQueryCatalog(ctx context.Context, root doltdb.RootValue, id string) (SavedQuery, error) { - tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: doltdb.DoltQueryCatalogTableName}) - - if err != nil { - return SavedQuery{}, err - } else if !ok { - return SavedQuery{}, doltdb.ErrTableNotFound - } - - if types.IsFormat_DOLT(tbl.Format()) { - return retrieveFromQueryCatalogProlly(ctx, tbl, id) - } - - return retrieveFromQueryCatalogNoms(ctx, tbl, id) -} - -func retrieveFromQueryCatalogProlly(ctx context.Context, tbl *doltdb.Table, id string) (SavedQuery, error) { - idx, err := tbl.GetRowData(ctx) - if err != nil { - return SavedQuery{}, err - } - - m, err := durable.ProllyMapFromIndex(idx) - if err != nil { - return SavedQuery{}, err - } - - kb := val.NewTupleBuilder(catalogKd, m.NodeStore()) - kb.PutString(0, id) - k, err := kb.Build(m.Pool()) - if err != nil { - return SavedQuery{}, err - } - var value val.Tuple - _ = m.Get(ctx, k, func(_, v val.Tuple) error { - value = v - return nil - }) - if value == nil { - return SavedQuery{}, ErrQueryNotFound.New(id) - } - - return savedQueryFromKVProlly(id, value) -} - -func retrieveFromQueryCatalogNoms(ctx context.Context, tbl *doltdb.Table, id string) (SavedQuery, error) { - m, err := tbl.GetNomsRowData(ctx) - - if err != nil { - return SavedQuery{}, err - } - - k, err := types.NewTuple(tbl.Format(), types.Uint(schema.QueryCatalogIdTag), types.String(id)) - - if err != nil { - return SavedQuery{}, err - } - - val, ok, err := m.MaybeGet(ctx, k) - - if err != nil { - return SavedQuery{}, err - } else if !ok { - return SavedQuery{}, ErrQueryNotFound.New(id) - } - - return savedQueryFromKVNoms(id, val.(types.Tuple)) -} - -// Returns the largest order entry in the catalog -func getMaxQueryOrderNoms(data types.Map, ctx context.Context) uint64 { - maxOrder := uint64(0) - data.IterAll(ctx, func(key, value types.Value) error { - r, _ := row.FromNoms(DoltQueryCatalogSchema, key.(types.Tuple), value.(types.Tuple)) - orderVal, ok := r.GetColVal(schema.QueryCatalogOrderTag) - if ok { - order := uint64(orderVal.(types.Uint)) - if order > maxOrder { - maxOrder = order - } - } - return nil - }) - return maxOrder -} - -func getMaxQueryOrderProlly(ctx context.Context, data prolly.Map) (uint64, error) { - itr, err := data.IterAll(ctx) - if err != nil { - return 0, err - } - - maxOrder := uint64(0) - for { - _, v, err := itr.Next(ctx) - if err != nil && err != io.EOF { - return 0, err - } - if err == io.EOF { - return maxOrder, nil - } - order, ok := catalogVd.GetUint64(0, v) - if ok { - if order > maxOrder { - maxOrder = order - } - } - } -} -*/ diff --git a/integration-tests/bats/query-catalog.bats b/integration-tests/bats/query-catalog.bats index 1f49857aac..04e87f32cc 100644 --- a/integration-tests/bats/query-catalog.bats +++ b/integration-tests/bats/query-catalog.bats @@ -60,32 +60,28 @@ teardown() { [[ "$output" =~ "my message" ]] || false } -@test "query-catalog: empty directory" { - mkdir empty && cd empty - - run dolt sql -q "show databases" --save name - [ "$status" -ne 0 ] - [[ ! "$output" =~ panic ]] || false - [[ "$output" =~ "The current directory is not a valid dolt repository." ]] || false - - run dolt sql --list-saved - [ "$status" -ne 0 ] - [[ ! "$output" =~ panic ]] || false - [[ "$output" =~ "The current directory is not a valid dolt repository." ]] || false - - run dolt sql --execute name - [ "$status" -ne 0 ] - [[ ! "$output" =~ panic ]] || false - [[ "$output" =~ "The current directory is not a valid dolt repository." ]] || false -} - -@test "query-catalog: executed saved" { +@test "can list saved queries" { Q1="select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1" Q2="select pk from one_pk order by pk" dolt sql -q "$Q1" -s name1 dolt sql -q "$Q2" -s name2 - # save Q1 and verify output + EXPECTED=$(cat <<'EOF' +id,display_order,name,query,description +name1,1,name1,"select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1","" +name2,2,name2,select pk from one_pk order by pk,"" +EOF +) + + run dolt sql --list-saved -r csv + [ "$status" -eq 0 ] + [[ "$output" =~ "$EXPECTED" ]] || false +} + +@test "can execute saved queries" { + Q1="select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1" + dolt sql -q "$Q1" -s name1 + EXPECTED=$(cat <<'EOF' pk,pk1,pk2 0,0,0 @@ -95,67 +91,38 @@ pk,pk1,pk2 EOF ) - run dolt sql -r csv -x name1 - echo "$output" - [ "$status" -eq 0 ] - [[ "$output" =~ "$EXPECTED" ]] || false - - # save Q2 and verify output - EXPECTED=$(cat <<'EOF' -pk -0 -1 -2 -3 -EOF -) - - run dolt sql -r csv -x name2 - [ "$status" -eq 0 ] - [[ "$output" =~ "$EXPECTED" ]] || false - - # execute list-saved and verify output. I have no idea why the - # query on the second line isn't quoted, assuming it's a bash - # interpretation thing. Has quotes when run by hand. - EXPECTED=$(cat <<'EOF' -id,display_order,name,query,description -name1,1,name1,"select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1","" -name2,2,name2,select pk from one_pk order by pk,"" -EOF -) - - run dolt sql --list-saved -r csv - echo "$output" - [ "$status" -eq 0 ] - [[ "$output" =~ "$EXPECTED" ]] || false - - # update an existing query, and verify query catalog is updated - Q1_UPDATED="select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 and pk < 3 order by 1 desc" - dolt sql -q "$Q1_UPDATED" -s name1 - - # execute list-saved and verify output - EXPECTED=$(cat <<'EOF' -id,display_order,name,query,description -name1,1,name1,"select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 and pk < 3 order by 1 desc","" -name2,2,name2,select pk from one_pk order by pk,"" -EOF -) - - run dolt sql --list-saved -r csv - echo "$output" - [ "$status" -eq 0 ] - [[ "$output" =~ "$EXPECTED" ]] || false - - EXPECTED=$(cat <<'EOF' -pk,pk1,pk2 -2,1,0 -1,0,1 -0,0,0 -EOF -) - - # Execute updated saved query and verify once output - run dolt sql -r csv -x name1 + run dolt sql -x name1 -r csv [ "$status" -eq 0 ] [[ "$output" =~ "$EXPECTED" ]] || false } + +@test "can update saved query with --save" { + Q1="select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1" + Q2="select pk from one_pk order by pk" + dolt sql -q "$Q1" -s name1 + + EXPECTED=$(cat <<'EOF' +id,display_order,name,query,description +name1,1,name1,"select pk, pk1, pk2 from one_pk,two_pk where one_pk.c1=two_pk.c1 order by 1","" +EOF +) + + run dolt sql --list-saved -r csv + [ "$status" -eq 0 ] + [[ "$output" =~ "$EXPECTED" ]] || false + + dolt sql -q "$Q2" -s name1 + + # execute list-saved and verify output. I have no idea why the + # query isn't quoted, but I assume it's a bash + # interpretation thing. Has quotes when run by hand. + EXPECTED=$(cat <<'EOF' +id,display_order,name,query,description +name1,1,name1,select pk from one_pk order by pk,"" +EOF +) + + run dolt sql --list-saved -r csv + [ "$status" -eq 0 ] + [[ "$output" =~ "$EXPECTED" ]] || false +} \ No newline at end of file diff --git a/integration-tests/bats/sql-server.bats b/integration-tests/bats/sql-server.bats index 7f733daf1d..345df8c631 100644 --- a/integration-tests/bats/sql-server.bats +++ b/integration-tests/bats/sql-server.bats @@ -2197,3 +2197,28 @@ EOF run grep -F "Dropping persisted '__dolt_local_user__@localhost' because this account name is reserved for Dolt" server_log.txt [ $status -eq 0 ] } + +@test "sql-server: can create and use saved queries with --host and --use-db" { + cd repo1 + dolt sql -q "create table test (i int)" + start_sql_server_with_args --host 0.0.0.0 + + cd ../repo2 + + run dolt --host 0.0.0.0 --no-tls --port $PORT --use-db repo1 sql -q "show tables" --save "show" + [ "$status" -eq 0 ] + [[ "$output" =~ "test" ]] || false + + run dolt --host 0.0.0.0 --no-tls --port $PORT --use-db repo1 sql -l -r csv + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" =~ "show,1,show,show tables,\"\"" ]] || false + + run dolt --host 0.0.0.0 --no-tls --port $PORT --use-db repo1 sql -x "show" + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" =~ "test" ]] || false + + stop_sql_server 1 && sleep 0.5 + rm -rf $BATS_TMPDIR/sql-server-test$$ +} \ No newline at end of file