github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/dprocedures/dolt_commit.go (about)

     1  // Copyright 2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dprocedures
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/dolthub/go-mysql-server/sql/types"
    23  
    24  	"github.com/dolthub/dolt/go/cmd/dolt/cli"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/branch_control"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/dconfig"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/env/actions"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
    29  	"github.com/dolthub/dolt/go/store/datas"
    30  )
    31  
    32  // doltCommit is the stored procedure version for the CLI command `dolt commit`.
    33  func doltCommit(ctx *sql.Context, args ...string) (sql.RowIter, error) {
    34  	commitHash, skipped, err := doDoltCommit(ctx, args)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	if skipped {
    39  		return nil, nil
    40  	}
    41  	return rowToIter(commitHash), nil
    42  }
    43  
    44  // doltCommitHashOut is the stored procedure version for the CLI function `commit`. The first parameter is the variable
    45  // to set the hash of.
    46  func doltCommitHashOut(ctx *sql.Context, outHash *string, args ...string) (sql.RowIter, error) {
    47  	commitHash, skipped, err := doDoltCommit(ctx, args)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	if skipped {
    52  		return nil, nil
    53  	}
    54  
    55  	*outHash = commitHash
    56  	return rowToIter(commitHash), nil
    57  }
    58  
    59  // doDoltCommit creates a dolt commit using the specified command line |args| provided. The response is the commit hash
    60  // of the new commit (or the empty string if the commit was skipped), a boolean that indicates if creating the commit
    61  // was skipped (e.g. due to --skip-empty), and an error describing any error encountered.
    62  func doDoltCommit(ctx *sql.Context, args []string) (string, bool, error) {
    63  	if err := branch_control.CheckAccess(ctx, branch_control.Permissions_Write); err != nil {
    64  		return "", false, err
    65  	}
    66  	// Get the information for the sql context.
    67  	dbName := ctx.GetCurrentDatabase()
    68  
    69  	apr, err := cli.CreateCommitArgParser().Parse(args)
    70  	if err != nil {
    71  		return "", false, err
    72  	}
    73  
    74  	if err := cli.VerifyCommitArgs(apr); err != nil {
    75  		return "", false, err
    76  	}
    77  
    78  	dSess := dsess.DSessFromSess(ctx.Session)
    79  	roots, ok := dSess.GetRoots(ctx, dbName)
    80  	if !ok {
    81  		return "", false, fmt.Errorf("Could not load database %s", dbName)
    82  	}
    83  
    84  	if apr.Contains(cli.UpperCaseAllFlag) {
    85  		roots, err = actions.StageAllTables(ctx, roots, true)
    86  		if err != nil {
    87  			return "", false, fmt.Errorf(err.Error())
    88  		}
    89  		roots, err = actions.StageDatabase(ctx, roots, true)
    90  		if err != nil {
    91  			return "", false, fmt.Errorf(err.Error())
    92  		}
    93  	} else if apr.Contains(cli.AllFlag) {
    94  		roots, err = actions.StageModifiedAndDeletedTables(ctx, roots)
    95  		if err != nil {
    96  			return "", false, fmt.Errorf(err.Error())
    97  		}
    98  	}
    99  
   100  	var name, email string
   101  	if authorStr, ok := apr.GetValue(cli.AuthorParam); ok {
   102  		name, email, err = cli.ParseAuthor(authorStr)
   103  		if err != nil {
   104  			return "", false, err
   105  		}
   106  	} else {
   107  		// In SQL mode, use the current SQL user as the commit author, instead of the `dolt config` configured values.
   108  		// We won't have an email address for the SQL user though, so instead use the MySQL user@address notation.
   109  		name = ctx.Client().User
   110  		email = fmt.Sprintf("%s@%s", ctx.Client().User, ctx.Client().Address)
   111  	}
   112  
   113  	amend := apr.Contains(cli.AmendFlag)
   114  
   115  	msg, msgOk := apr.GetValue(cli.MessageArg)
   116  	if !msgOk {
   117  		if amend {
   118  			commit, err := dSess.GetHeadCommit(ctx, dbName)
   119  			if err != nil {
   120  				return "", false, err
   121  			}
   122  			commitMeta, err := commit.GetCommitMeta(ctx)
   123  			if err != nil {
   124  				return "", false, err
   125  			}
   126  			msg = commitMeta.Description
   127  		} else {
   128  			return "", false, fmt.Errorf("Must provide commit message.")
   129  		}
   130  	}
   131  
   132  	t := ctx.QueryTime()
   133  	if commitTimeStr, ok := apr.GetValue(cli.DateParam); ok {
   134  		var err error
   135  		t, err = dconfig.ParseDate(commitTimeStr)
   136  
   137  		if err != nil {
   138  			return "", false, fmt.Errorf(err.Error())
   139  		}
   140  	} else if datas.CustomAuthorDate {
   141  		t = datas.AuthorDate()
   142  	}
   143  
   144  	if apr.Contains(cli.ForceFlag) {
   145  		err = ctx.SetSessionVariable(ctx, "dolt_force_transaction_commit", 1)
   146  		if err != nil {
   147  			return "", false, fmt.Errorf(err.Error())
   148  		}
   149  	}
   150  
   151  	pendingCommit, err := dSess.NewPendingCommit(ctx, dbName, roots, actions.CommitStagedProps{
   152  		Message:    msg,
   153  		Date:       t,
   154  		AllowEmpty: apr.Contains(cli.AllowEmptyFlag),
   155  		SkipEmpty:  apr.Contains(cli.SkipEmptyFlag),
   156  		Amend:      amend,
   157  		Force:      apr.Contains(cli.ForceFlag),
   158  		Name:       name,
   159  		Email:      email,
   160  	})
   161  	if err != nil {
   162  		return "", false, err
   163  	}
   164  
   165  	// Nothing to commit, and we didn't pass --allowEmpty
   166  	if pendingCommit == nil && apr.Contains(cli.SkipEmptyFlag) {
   167  		return "", true, nil
   168  	} else if pendingCommit == nil {
   169  		return "", false, errors.New("nothing to commit")
   170  	}
   171  
   172  	newCommit, err := dSess.DoltCommit(ctx, dbName, dSess.GetTransaction(), pendingCommit)
   173  	if err != nil {
   174  		return "", false, err
   175  	}
   176  
   177  	h, err := newCommit.HashOf()
   178  	if err != nil {
   179  		return "", false, err
   180  	}
   181  
   182  	return h.String(), false, nil
   183  }
   184  
   185  func getDoltArgs(ctx *sql.Context, row sql.Row, children []sql.Expression) ([]string, error) {
   186  	args := make([]string, len(children))
   187  	for i := range children {
   188  		childVal, err := children[i].Eval(ctx, row)
   189  
   190  		if err != nil {
   191  			return nil, err
   192  		}
   193  
   194  		text, _, err := types.Text.Convert(childVal)
   195  
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  
   200  		args[i] = text.(string)
   201  	}
   202  
   203  	return args, nil
   204  }