mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-02 11:30:13 -05:00
Merge pull request #7975 from dolthub/bh/dolt_hashof_db
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
// Copyright 2024 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 dfunctions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
"github.com/dolthub/dolt/go/store/hash"
|
||||
)
|
||||
|
||||
const HashOfDatabaseFuncName = "dolt_hashof_db"
|
||||
|
||||
type HashOfDatabase struct {
|
||||
children []sql.Expression
|
||||
}
|
||||
|
||||
// NewHashOfDatabase creates a new HashOfDatabase expression.
|
||||
func NewHashOfDatabase(args ...sql.Expression) (sql.Expression, error) {
|
||||
if len(args) > 1 {
|
||||
return nil, sql.ErrInvalidArgumentNumber.New(HashOfDatabaseFuncName, "0 or 1", len(args))
|
||||
}
|
||||
|
||||
return &HashOfDatabase{
|
||||
children: args,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Children implements the Expression interface.
|
||||
func (t *HashOfDatabase) Children() []sql.Expression {
|
||||
return t.children
|
||||
}
|
||||
|
||||
// Eval implements the Expression interface.
|
||||
func (t *HashOfDatabase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
||||
var args []string
|
||||
for _, child := range t.children {
|
||||
args = append(args, child.String())
|
||||
}
|
||||
|
||||
dbName := ctx.GetCurrentDatabase()
|
||||
ds := dsess.DSessFromSess(ctx.Session)
|
||||
|
||||
roots, ok := ds.GetRoots(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, sql.ErrDatabaseNotFound.New(dbName)
|
||||
}
|
||||
|
||||
refStr := "WORKING"
|
||||
if len(args) != 0 {
|
||||
refStr = strings.TrimSpace(args[0])
|
||||
|
||||
// Remove quotes if they are present at the beginning and end of the string
|
||||
for _, quote := range []rune{'\'', '"', '`'} {
|
||||
if rune(refStr[0]) == quote && rune(refStr[len(refStr)-1]) == quote {
|
||||
refStr = refStr[1 : len(refStr)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var h hash.Hash
|
||||
var err error
|
||||
switch {
|
||||
case len(refStr) == 0 || strings.EqualFold(refStr, doltdb.Working):
|
||||
h, err = roots.Working.HashOf()
|
||||
|
||||
case strings.EqualFold(refStr, doltdb.Staged):
|
||||
h, err = roots.Staged.HashOf()
|
||||
|
||||
case strings.EqualFold(refStr, "HEAD"):
|
||||
h, err = roots.Head.HashOf()
|
||||
|
||||
default:
|
||||
dbData, ok := ds.GetDbData(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, sql.ErrDatabaseNotFound.New(dbName)
|
||||
}
|
||||
|
||||
ddb := dbData.Ddb
|
||||
r, err := ddb.GetRefByNameInsensitive(ctx, refStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting ref '%s' from database '%s': %w", refStr, dbName, err)
|
||||
}
|
||||
|
||||
cm, err := ddb.ResolveCommitRef(ctx, r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolving ref '%s' from database '%s': %w", refStr, dbName, err)
|
||||
}
|
||||
|
||||
root, err := cm.GetRootValue(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting root value for commit '%s' in database '%s': %w", refStr, dbName, err)
|
||||
}
|
||||
|
||||
h, err = root.HashOf()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting dolt_hashof_db('%s'): %w", refStr, err)
|
||||
}
|
||||
|
||||
return h.String(), nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (t *HashOfDatabase) String() string {
|
||||
args := make([]string, 0, len(t.children))
|
||||
for _, child := range t.children {
|
||||
args = append(args, child.String())
|
||||
}
|
||||
|
||||
argStr := strings.Join(args, ", ")
|
||||
return fmt.Sprintf("%s(%s)", HashOfDatabaseFuncName, argStr)
|
||||
}
|
||||
|
||||
// Description implements the FunctionExpression interface
|
||||
func (t *HashOfDatabase) Description() string {
|
||||
return "returns a hash of the contents of the database for the specified ref (The current branche's working set is used if no arguments are provided), typically used for detecting if a database has changed"
|
||||
}
|
||||
|
||||
// IsNullable implements the Expression interface.
|
||||
func (t *HashOfDatabase) IsNullable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolved implements the Expression interface.
|
||||
func (*HashOfDatabase) Resolved() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// WithChildren implements the Expression interface.
|
||||
func (t *HashOfDatabase) WithChildren(children ...sql.Expression) (sql.Expression, error) {
|
||||
return NewHashOfDatabase(children...)
|
||||
}
|
||||
|
||||
// Type implements the Expression interface.
|
||||
func (t *HashOfDatabase) Type() sql.Type {
|
||||
return types.Text
|
||||
}
|
||||
@@ -25,6 +25,7 @@ var DoltFunctions = []sql.Function{
|
||||
sql.Function2{Name: DoltMergeBaseFuncName, Fn: NewMergeBase},
|
||||
sql.Function2{Name: HasAncestorFuncName, Fn: NewHasAncestor},
|
||||
sql.Function1{Name: HashOfTableFuncName, Fn: NewHashOfTable},
|
||||
sql.FunctionN{Name: HashOfDatabaseFuncName, Fn: NewHashOfDatabase},
|
||||
}
|
||||
|
||||
// DolthubApiFunctions are the DoltFunctions that get exposed to Dolthub Api.
|
||||
|
||||
@@ -1667,10 +1667,12 @@ func TestConcurrentTransactions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDoltScripts(t *testing.T) {
|
||||
harness := newDoltHarness(t)
|
||||
defer harness.Close()
|
||||
for _, script := range DoltScripts {
|
||||
enginetest.TestScript(t, harness, script)
|
||||
go func() {
|
||||
harness := newDoltHarness(t)
|
||||
defer harness.Close()
|
||||
enginetest.TestScript(t, harness, script)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -735,6 +735,144 @@ var DoltScripts = []queries.ScriptTest{
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dolt_hashof_db tests",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE t1 (pk int primary key);",
|
||||
"CREATE TABLE t2 (pk int primary key);",
|
||||
"CREATE TABLE t3 (pk int primary key);",
|
||||
"call dolt_commit('-Am','table creation commit');",
|
||||
},
|
||||
Assertions: []queries.ScriptTestAssertion{
|
||||
{
|
||||
Query: "SHOW TABLES;",
|
||||
Expected: []sql.Row{
|
||||
{"t1"},
|
||||
{"t2"},
|
||||
{"t3"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SET @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('HEAD');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('STAGED');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('WORKING');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('main');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "CALL dolt_checkout('-b','new');",
|
||||
Expected: []sql.Row{{0, "Switched to branch 'new'"}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('new');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "INSERT INTO t1 VALUES (1);",
|
||||
Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('HEAD');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('STAGED');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('WORKING');",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('main');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('new');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
|
||||
{
|
||||
Query: "SET @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('STAGED');",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
{
|
||||
Query: "CALL dolt_add('t1');",
|
||||
Expected: []sql.Row{{int64(0)}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('STAGED');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('HEAD');",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('new');",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
{
|
||||
Query: "CALL dolt_commit('-m', 'added some rows to branch `new`');",
|
||||
SkipResultsCheck: true, // returned hash is not deterministic
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('HEAD');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('new');",
|
||||
Expected: []sql.Row{{true}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db('main');",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
|
||||
{
|
||||
Query: "INSERT INTO t2 VALUES (1);",
|
||||
Expected: []sql.Row{{types.OkResult{RowsAffected: 1}}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
|
||||
{
|
||||
Query: "SET @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{}},
|
||||
},
|
||||
{
|
||||
Query: "create procedure proc1() SELECT * FROM t3;",
|
||||
Expected: []sql.Row{{types.OkResult{}}},
|
||||
},
|
||||
{
|
||||
Query: "SELECT @hashofdb = dolt_hashof_db();",
|
||||
Expected: []sql.Row{{false}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// https://github.com/dolthub/dolt/issues/7384
|
||||
Name: "multiple unresolved foreign keys can be created on the same table",
|
||||
|
||||
Reference in New Issue
Block a user