github.com/mithrandie/csvq@v1.18.1/lib/query/processor.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os/exec"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/mithrandie/csvq/lib/excmd"
    12  	"github.com/mithrandie/csvq/lib/option"
    13  	"github.com/mithrandie/csvq/lib/parser"
    14  	"github.com/mithrandie/csvq/lib/value"
    15  
    16  	"github.com/mithrandie/ternary"
    17  )
    18  
    19  type StatementFlow int
    20  
    21  const (
    22  	Terminate StatementFlow = iota
    23  	TerminateWithError
    24  	Exit
    25  	Break
    26  	Continue
    27  	Return
    28  )
    29  
    30  const StoringResultsContextKey = "sqr"
    31  const StatementReplaceValuesContextKey = "rv"
    32  
    33  func ContextForStoringResults(ctx context.Context) context.Context {
    34  	return context.WithValue(ctx, StoringResultsContextKey, true)
    35  }
    36  
    37  func ContextForPreparedStatement(ctx context.Context, values *ReplaceValues) context.Context {
    38  	return context.WithValue(ctx, StatementReplaceValuesContextKey, values)
    39  }
    40  
    41  type Processor struct {
    42  	Tx             *Transaction
    43  	ReferenceScope *ReferenceScope
    44  
    45  	storeResults bool
    46  
    47  	returnVal        value.Primary
    48  	measurementStart time.Time
    49  }
    50  
    51  func NewProcessor(tx *Transaction) *Processor {
    52  	return NewProcessorWithScope(tx, NewReferenceScope(tx))
    53  }
    54  
    55  func NewProcessorWithScope(tx *Transaction, scope *ReferenceScope) *Processor {
    56  	return &Processor{
    57  		Tx:             tx,
    58  		ReferenceScope: scope,
    59  	}
    60  }
    61  
    62  func (proc *Processor) NewChildProcessor() *Processor {
    63  	return &Processor{
    64  		Tx:             proc.Tx,
    65  		ReferenceScope: proc.ReferenceScope.CreateChild(),
    66  	}
    67  }
    68  
    69  func (proc *Processor) Close() {
    70  	proc.ReferenceScope.CloseCurrentBlock()
    71  }
    72  
    73  func (proc *Processor) Execute(ctx context.Context, statements []parser.Statement) (StatementFlow, error) {
    74  	if v := ctx.Value(StoringResultsContextKey); v != nil {
    75  		if b, ok := v.(bool); ok && b {
    76  			proc.storeResults = true
    77  		}
    78  	}
    79  
    80  	proc.Tx.SelectedViews = nil
    81  	proc.Tx.AffectedRows = 0
    82  
    83  	flow, err := proc.execute(ctx, statements)
    84  	if err == nil && flow == Terminate && proc.Tx.AutoCommit {
    85  		err = proc.AutoCommit(ctx)
    86  	}
    87  	return flow, err
    88  }
    89  
    90  func (proc *Processor) execute(ctx context.Context, statements []parser.Statement) (flow StatementFlow, err error) {
    91  	defer func() {
    92  		if err == nil {
    93  			if panicReport := recover(); panicReport != nil {
    94  				flow = TerminateWithError
    95  				err = NewFatalError(panicReport)
    96  			}
    97  		}
    98  	}()
    99  
   100  	flow = Terminate
   101  
   102  	for _, stmt := range statements {
   103  		flow, err = proc.ExecuteStatement(ctx, stmt)
   104  		if err != nil {
   105  			return
   106  		}
   107  		if flow != Terminate {
   108  			break
   109  		}
   110  	}
   111  	return
   112  }
   113  
   114  func (proc *Processor) executeChild(ctx context.Context, statements []parser.Statement) (StatementFlow, error) {
   115  	child := proc.NewChildProcessor()
   116  	flow, err := child.execute(ctx, statements)
   117  	if child.returnVal != nil {
   118  		proc.returnVal = child.returnVal
   119  	}
   120  	child.Close()
   121  	return flow, err
   122  }
   123  
   124  func (proc *Processor) ExecuteStatement(ctx context.Context, stmt parser.Statement) (StatementFlow, error) {
   125  	if ctx.Err() != nil {
   126  		return TerminateWithError, ConvertContextError(ctx.Err())
   127  	}
   128  
   129  	flow := Terminate
   130  
   131  	var printstr string
   132  	var err error
   133  
   134  	switch stmt.(type) {
   135  	case parser.SetFlag:
   136  		err = SetFlag(ctx, proc.ReferenceScope, stmt.(parser.SetFlag))
   137  	case parser.AddFlagElement:
   138  		err = AddFlagElement(ctx, proc.ReferenceScope, stmt.(parser.AddFlagElement))
   139  	case parser.RemoveFlagElement:
   140  		err = RemoveFlagElement(ctx, proc.ReferenceScope, stmt.(parser.RemoveFlagElement))
   141  	case parser.ShowFlag:
   142  		if printstr, err = ShowFlag(proc.Tx, stmt.(parser.ShowFlag)); err == nil {
   143  			proc.Log(printstr, false)
   144  		}
   145  	case parser.VariableDeclaration:
   146  		err = proc.ReferenceScope.DeclareVariable(ctx, stmt.(parser.VariableDeclaration))
   147  	case parser.VariableSubstitution:
   148  		_, err = proc.ReferenceScope.SubstituteVariable(ctx, stmt.(parser.VariableSubstitution))
   149  	case parser.SetEnvVar:
   150  		err = SetEnvVar(ctx, proc.ReferenceScope, stmt.(parser.SetEnvVar))
   151  	case parser.UnsetEnvVar:
   152  		err = UnsetEnvVar(stmt.(parser.UnsetEnvVar))
   153  	case parser.DisposeVariable:
   154  		err = proc.ReferenceScope.DisposeVariable(stmt.(parser.DisposeVariable).Variable)
   155  	case parser.CursorDeclaration:
   156  		err = proc.ReferenceScope.DeclareCursor(stmt.(parser.CursorDeclaration))
   157  	case parser.OpenCursor:
   158  		openCur := stmt.(parser.OpenCursor)
   159  		err = proc.ReferenceScope.OpenCursor(ctx, openCur.Cursor, openCur.Values)
   160  	case parser.CloseCursor:
   161  		err = proc.ReferenceScope.CloseCursor(stmt.(parser.CloseCursor).Cursor)
   162  	case parser.DisposeCursor:
   163  		err = proc.ReferenceScope.DisposeCursor(stmt.(parser.DisposeCursor).Cursor)
   164  	case parser.FetchCursor:
   165  		fetch := stmt.(parser.FetchCursor)
   166  		_, err = FetchCursor(ctx, proc.ReferenceScope, fetch.Cursor, fetch.Position, fetch.Variables)
   167  	case parser.ViewDeclaration:
   168  		err = DeclareView(ctx, proc.ReferenceScope, stmt.(parser.ViewDeclaration))
   169  	case parser.DisposeView:
   170  		err = proc.ReferenceScope.DisposeTemporaryTable(stmt.(parser.DisposeView).View)
   171  	case parser.FunctionDeclaration:
   172  		err = proc.ReferenceScope.DeclareFunction(stmt.(parser.FunctionDeclaration))
   173  	case parser.DisposeFunction:
   174  		err = proc.ReferenceScope.DisposeFunction(stmt.(parser.DisposeFunction).Name)
   175  	case parser.AggregateDeclaration:
   176  		err = proc.ReferenceScope.DeclareAggregateFunction(stmt.(parser.AggregateDeclaration))
   177  	case parser.StatementPreparation:
   178  		err = proc.Tx.PreparedStatements.Prepare(proc.ReferenceScope.Tx.Flags, stmt.(parser.StatementPreparation))
   179  	case parser.ExecuteStatement:
   180  		execStmt := stmt.(parser.ExecuteStatement)
   181  		prepared, e := proc.Tx.PreparedStatements.Get(execStmt.Name)
   182  		if e != nil {
   183  			err = e
   184  		} else {
   185  			flow, err = proc.execute(ContextForPreparedStatement(ctx, NewReplaceValues(execStmt.Values)), prepared.Statements)
   186  		}
   187  	case parser.DisposeStatement:
   188  		err = proc.Tx.PreparedStatements.Dispose(stmt.(parser.DisposeStatement))
   189  	case parser.SelectQuery:
   190  		if selectEntity, ok := stmt.(parser.SelectQuery).SelectEntity.(parser.SelectEntity); ok && selectEntity.IntoClause != nil {
   191  			_, err = Select(ctx, proc.ReferenceScope, stmt.(parser.SelectQuery))
   192  		} else {
   193  			if proc.Tx.Flags.Stats {
   194  				proc.measurementStart = time.Now()
   195  			}
   196  
   197  			view, e := Select(ctx, proc.ReferenceScope, stmt.(parser.SelectQuery))
   198  			if e == nil {
   199  				var warnmsg string
   200  
   201  				proc.Tx.Session.mtx.Lock()
   202  
   203  				if proc.storeResults {
   204  					proc.Tx.SelectedViews = append(proc.Tx.SelectedViews, view)
   205  				}
   206  
   207  				if _, ok := proc.Tx.Session.Stdout().(*Discard); !ok || proc.Tx.Session.OutFile() != nil {
   208  					exportOptions := proc.Tx.Flags.ExportOptions.Copy()
   209  
   210  					var writer io.Writer
   211  					if proc.Tx.Session.OutFile() != nil {
   212  						writer = proc.Tx.Session.OutFile()
   213  					} else {
   214  						writer = proc.Tx.Session.Stdout()
   215  					}
   216  					warn, e := EncodeView(ctx, writer, view, exportOptions, proc.Tx.Palette)
   217  
   218  					if e != nil {
   219  						if e == EmptyResultSetError {
   220  							warnmsg = warn
   221  						} else if e == DataEmpty {
   222  							// Do Nothing
   223  						} else {
   224  							err = e
   225  						}
   226  					} else if !proc.Tx.Flags.ExportOptions.StripEndingLineBreak &&
   227  						!(proc.Tx.Session.OutFile() != nil && exportOptions.Format == option.FIXED && exportOptions.SingleLine) {
   228  						_, err = writer.Write([]byte(proc.Tx.Flags.ExportOptions.LineBreak.Value()))
   229  					}
   230  				}
   231  
   232  				proc.Tx.Session.mtx.Unlock()
   233  
   234  				if 0 < len(warnmsg) {
   235  					proc.LogWarn(warnmsg, proc.Tx.Flags.Quiet)
   236  				}
   237  			} else {
   238  				err = e
   239  			}
   240  
   241  			if proc.Tx.Flags.Stats {
   242  				proc.showExecutionTime(ctx)
   243  			}
   244  		}
   245  	case parser.InsertQuery:
   246  		if proc.Tx.Flags.Stats {
   247  			proc.measurementStart = time.Now()
   248  		}
   249  
   250  		fileInfo, cnt, e := Insert(ctx, proc.ReferenceScope, stmt.(parser.InsertQuery))
   251  		if e == nil {
   252  			if 0 < cnt {
   253  				proc.Tx.UncommittedViews.SetForUpdatedView(fileInfo)
   254  			}
   255  			proc.Log(fmt.Sprintf("%s inserted on %q.", FormatCount(cnt, "record"), fileInfo.Path), proc.Tx.Flags.Quiet)
   256  			if proc.storeResults {
   257  				proc.Tx.AffectedRows = cnt
   258  			}
   259  		} else {
   260  			err = e
   261  		}
   262  
   263  		if proc.Tx.Flags.Stats {
   264  			proc.showExecutionTime(ctx)
   265  		}
   266  	case parser.UpdateQuery:
   267  		if proc.Tx.Flags.Stats {
   268  			proc.measurementStart = time.Now()
   269  		}
   270  
   271  		infos, cnts, e := Update(ctx, proc.ReferenceScope, stmt.(parser.UpdateQuery))
   272  		if e == nil {
   273  			cntTotal := 0
   274  			for i, info := range infos {
   275  				if 0 < cnts[i] {
   276  					proc.Tx.UncommittedViews.SetForUpdatedView(info)
   277  					cntTotal += cnts[i]
   278  				}
   279  				proc.Log(fmt.Sprintf("%s updated on %q.", FormatCount(cnts[i], "record"), info.Path), proc.Tx.Flags.Quiet)
   280  			}
   281  			if proc.storeResults {
   282  				proc.Tx.AffectedRows = cntTotal
   283  			}
   284  		} else {
   285  			err = e
   286  		}
   287  
   288  		if proc.Tx.Flags.Stats {
   289  			proc.showExecutionTime(ctx)
   290  		}
   291  	case parser.ReplaceQuery:
   292  		if proc.Tx.Flags.Stats {
   293  			proc.measurementStart = time.Now()
   294  		}
   295  
   296  		fileInfo, cnt, e := Replace(ctx, proc.ReferenceScope, stmt.(parser.ReplaceQuery))
   297  		if e == nil {
   298  			if 0 < cnt {
   299  				proc.Tx.UncommittedViews.SetForUpdatedView(fileInfo)
   300  			}
   301  			proc.Log(fmt.Sprintf("%s replaced on %q.", FormatCount(cnt, "record"), fileInfo.Path), proc.Tx.Flags.Quiet)
   302  			if proc.storeResults {
   303  				proc.Tx.AffectedRows = cnt
   304  			}
   305  		} else {
   306  			err = e
   307  		}
   308  
   309  		if proc.Tx.Flags.Stats {
   310  			proc.showExecutionTime(ctx)
   311  		}
   312  	case parser.DeleteQuery:
   313  		if proc.Tx.Flags.Stats {
   314  			proc.measurementStart = time.Now()
   315  		}
   316  
   317  		infos, cnts, e := Delete(ctx, proc.ReferenceScope, stmt.(parser.DeleteQuery))
   318  		if e == nil {
   319  			cntTotal := 0
   320  			for i, info := range infos {
   321  				if 0 < cnts[i] {
   322  					proc.Tx.UncommittedViews.SetForUpdatedView(info)
   323  					cntTotal += cnts[i]
   324  				}
   325  				proc.Log(fmt.Sprintf("%s deleted on %q.", FormatCount(cnts[i], "record"), info.Path), proc.Tx.Flags.Quiet)
   326  			}
   327  			if proc.storeResults {
   328  				proc.Tx.AffectedRows = cntTotal
   329  			}
   330  		} else {
   331  			err = e
   332  		}
   333  
   334  		if proc.Tx.Flags.Stats {
   335  			proc.showExecutionTime(ctx)
   336  		}
   337  	case parser.CreateTable:
   338  		createTableStatement := stmt.(parser.CreateTable)
   339  		info, e := CreateTable(ctx, proc.ReferenceScope, createTableStatement)
   340  		if e == nil {
   341  			proc.Tx.UncommittedViews.SetForCreatedView(info)
   342  			proc.Log(fmt.Sprintf("file %q is created.", info.Path), proc.Tx.Flags.Quiet)
   343  		} else if _, ok := e.(*FileAlreadyExistError); ok && createTableStatement.IfNotExists {
   344  			e := func() error {
   345  				filePath, e := CreateFilePath(createTableStatement.Table, proc.ReferenceScope.Tx.Flags.Repository)
   346  				if e != nil {
   347  					return NewIOError(createTableStatement.Table, err.Error())
   348  				}
   349  
   350  				tableIndentifier := parser.Identifier{
   351  					BaseExpr: createTableStatement.GetBaseExpr(),
   352  					Literal:  filePath,
   353  				}
   354  
   355  				queryScope := proc.ReferenceScope.CreateNode()
   356  				defer queryScope.CloseCurrentNode()
   357  
   358  				view, e := LoadViewFromTableIdentifier(ctx, queryScope, tableIndentifier, false, false)
   359  				if e != nil {
   360  					return e
   361  				}
   362  
   363  				proc.Log(fmt.Sprintf("file %q already exists.", filePath), proc.Tx.Flags.Quiet)
   364  
   365  				if createTableStatement.Fields != nil {
   366  					columns := view.Header.TableColumnNames()
   367  
   368  					if len(columns) != len(createTableStatement.Fields) {
   369  						return NewFieldLengthNotMatchError(createTableStatement.Fields[0])
   370  					}
   371  
   372  					for _, f := range createTableStatement.Fields {
   373  						if !InStrSliceWithCaseInsensitive(f.(parser.Identifier).Literal, columns) {
   374  							return NewFieldNotExistError(f)
   375  						}
   376  					}
   377  				}
   378  
   379  				return nil
   380  			}()
   381  
   382  			if e != nil {
   383  				err = e
   384  			}
   385  		} else {
   386  			err = e
   387  		}
   388  	case parser.AddColumns:
   389  		info, cnt, e := AddColumns(ctx, proc.ReferenceScope, stmt.(parser.AddColumns))
   390  		if e == nil {
   391  			proc.Tx.UncommittedViews.SetForUpdatedView(info)
   392  			proc.Log(fmt.Sprintf("%s added on %q.", FormatCount(cnt, "field"), info.Path), proc.Tx.Flags.Quiet)
   393  		} else {
   394  			err = e
   395  		}
   396  	case parser.DropColumns:
   397  		info, cnt, e := DropColumns(ctx, proc.ReferenceScope, stmt.(parser.DropColumns))
   398  		if e == nil {
   399  			proc.Tx.UncommittedViews.SetForUpdatedView(info)
   400  			proc.Log(fmt.Sprintf("%s dropped on %q.", FormatCount(cnt, "field"), info.Path), proc.Tx.Flags.Quiet)
   401  		} else {
   402  			err = e
   403  		}
   404  	case parser.RenameColumn:
   405  		info, e := RenameColumn(ctx, proc.ReferenceScope, stmt.(parser.RenameColumn))
   406  		if e == nil {
   407  			proc.Tx.UncommittedViews.SetForUpdatedView(info)
   408  			proc.Log(fmt.Sprintf("%s renamed on %q.", FormatCount(1, "field"), info.Path), proc.Tx.Flags.Quiet)
   409  		} else {
   410  			err = e
   411  		}
   412  	case parser.SetTableAttribute:
   413  		expr := stmt.(parser.SetTableAttribute)
   414  		info, log, e := SetTableAttribute(ctx, proc.ReferenceScope, expr)
   415  		if e == nil {
   416  			proc.Tx.UncommittedViews.SetForUpdatedView(info)
   417  			proc.Log(log, proc.Tx.Flags.Quiet)
   418  		} else {
   419  			if unchanged, ok := e.(*TableAttributeUnchangedError); ok {
   420  				proc.Log(fmt.Sprintf("Table attributes of %s remain unchanged.", unchanged.Path), proc.Tx.Flags.Quiet)
   421  			} else {
   422  				err = e
   423  			}
   424  		}
   425  	case parser.TransactionControl:
   426  		switch stmt.(parser.TransactionControl).Token {
   427  		case parser.COMMIT:
   428  			err = proc.Commit(ctx, stmt.(parser.Expression))
   429  		case parser.ROLLBACK:
   430  			err = proc.Rollback(stmt.(parser.Expression))
   431  		}
   432  	case parser.FlowControl:
   433  		switch stmt.(parser.FlowControl).Token {
   434  		case parser.CONTINUE:
   435  			flow = Continue
   436  		case parser.BREAK:
   437  			flow = Break
   438  		}
   439  	case parser.Exit:
   440  		ex := stmt.(parser.Exit)
   441  		code := 0
   442  		if ex.Code != nil {
   443  			code = int(ex.Code.(*value.Integer).Raw())
   444  		}
   445  		if 0 < code {
   446  			flow = TerminateWithError
   447  			err = NewForcedExit(code)
   448  		} else {
   449  			flow = Exit
   450  		}
   451  	case parser.Return:
   452  		var ret value.Primary
   453  		if ret, err = Evaluate(ctx, proc.ReferenceScope, stmt.(parser.Return).Value); err == nil {
   454  			proc.returnVal = ret
   455  			flow = Return
   456  		}
   457  	case parser.If:
   458  		flow, err = proc.IfStmt(ctx, stmt.(parser.If))
   459  	case parser.Case:
   460  		flow, err = proc.Case(ctx, stmt.(parser.Case))
   461  	case parser.While:
   462  		flow, err = proc.While(ctx, stmt.(parser.While))
   463  	case parser.WhileInCursor:
   464  		flow, err = proc.WhileInCursor(ctx, stmt.(parser.WhileInCursor))
   465  	case parser.Echo:
   466  		if printstr, err = Echo(ctx, proc.ReferenceScope, stmt.(parser.Echo)); err == nil {
   467  			proc.Log(printstr, false)
   468  		}
   469  	case parser.Print:
   470  		if printstr, err = Print(ctx, proc.ReferenceScope, stmt.(parser.Print)); err == nil {
   471  			proc.Log(printstr, false)
   472  		}
   473  	case parser.Printf:
   474  		if printstr, err = Printf(ctx, proc.ReferenceScope, stmt.(parser.Printf)); err == nil {
   475  			proc.Log(printstr, false)
   476  		}
   477  	case parser.Source:
   478  		var externalStatements []parser.Statement
   479  		if externalStatements, err = Source(ctx, proc.ReferenceScope, stmt.(parser.Source)); err == nil {
   480  			flow, err = proc.execute(ctx, externalStatements)
   481  		}
   482  	case parser.Execute:
   483  		var externalStatements []parser.Statement
   484  		if externalStatements, err = ParseExecuteStatements(ctx, proc.ReferenceScope, stmt.(parser.Execute)); err == nil {
   485  			flow, err = proc.execute(ctx, externalStatements)
   486  		}
   487  	case parser.Chdir:
   488  		err = Chdir(ctx, proc.ReferenceScope, stmt.(parser.Chdir))
   489  	case parser.Pwd:
   490  		var dirpath string
   491  		dirpath, err = Pwd(stmt.(parser.Pwd))
   492  		if err == nil {
   493  			proc.Log(dirpath, false)
   494  		}
   495  	case parser.Reload:
   496  		err = Reload(ctx, proc.Tx, stmt.(parser.Reload))
   497  	case parser.ShowObjects:
   498  		if printstr, err = ShowObjects(proc.ReferenceScope, stmt.(parser.ShowObjects)); err == nil {
   499  			proc.Log(printstr, false)
   500  		}
   501  	case parser.ShowFields:
   502  		if printstr, err = ShowFields(ctx, proc.ReferenceScope, stmt.(parser.ShowFields)); err == nil {
   503  			proc.Log(printstr, false)
   504  		}
   505  	case parser.Syntax:
   506  		if printstr, err = Syntax(ctx, proc.ReferenceScope, stmt.(parser.Syntax)); err == nil {
   507  			proc.Log(printstr, false)
   508  		}
   509  	case parser.Trigger:
   510  		trigger := stmt.(parser.Trigger)
   511  		switch strings.ToUpper(trigger.Event.Literal) {
   512  		case "ERROR":
   513  			var message string
   514  			if trigger.Message != nil {
   515  				if pt, ok := trigger.Message.(parser.PrimitiveType); ok && trigger.Code == nil && pt.IsInteger() {
   516  					trigger.Code = pt.Value
   517  				} else {
   518  					var p value.Primary
   519  					if p, err = Evaluate(ctx, proc.ReferenceScope, trigger.Message); err == nil {
   520  						if s := value.ToString(p); !value.IsNull(s) {
   521  							message = s.(*value.String).Raw()
   522  						}
   523  					}
   524  				}
   525  			}
   526  			if err == nil {
   527  				err = NewUserTriggeredError(trigger, message)
   528  			}
   529  		default:
   530  			err = NewInvalidEventNameError(trigger.Event)
   531  		}
   532  	case parser.ExternalCommand:
   533  		err = proc.ExecExternalCommand(ctx, stmt.(parser.ExternalCommand))
   534  	default:
   535  		if expr, ok := stmt.(parser.QueryExpression); ok {
   536  			_, err = Evaluate(ctx, proc.ReferenceScope, expr)
   537  		}
   538  	}
   539  
   540  	if err != nil {
   541  		flow = TerminateWithError
   542  	}
   543  	return flow, err
   544  }
   545  
   546  func (proc *Processor) IfStmt(ctx context.Context, stmt parser.If) (StatementFlow, error) {
   547  	stmts := make([]parser.ElseIf, 0, len(stmt.ElseIf)+1)
   548  	stmts = append(stmts, parser.ElseIf{
   549  		Condition:  stmt.Condition,
   550  		Statements: stmt.Statements,
   551  	})
   552  	for _, v := range stmt.ElseIf {
   553  		stmts = append(stmts, v)
   554  	}
   555  
   556  	for _, v := range stmts {
   557  		p, err := Evaluate(ctx, proc.ReferenceScope, v.Condition)
   558  		if err != nil {
   559  			return TerminateWithError, err
   560  		}
   561  		if p.Ternary() == ternary.TRUE {
   562  			return proc.executeChild(ctx, v.Statements)
   563  		}
   564  	}
   565  
   566  	if stmt.Else.Statements != nil {
   567  		return proc.executeChild(ctx, stmt.Else.Statements)
   568  	}
   569  	return Terminate, nil
   570  }
   571  
   572  func (proc *Processor) Case(ctx context.Context, stmt parser.Case) (StatementFlow, error) {
   573  	var val value.Primary
   574  	var err error
   575  	if stmt.Value != nil {
   576  		val, err = Evaluate(ctx, proc.ReferenceScope, stmt.Value)
   577  		if err != nil {
   578  			return TerminateWithError, err
   579  		}
   580  	}
   581  
   582  	for _, when := range stmt.When {
   583  		var t ternary.Value
   584  
   585  		cond, err := Evaluate(ctx, proc.ReferenceScope, when.Condition)
   586  		if err != nil {
   587  			return TerminateWithError, err
   588  		}
   589  
   590  		if val == nil {
   591  			t = cond.Ternary()
   592  		} else {
   593  			t = value.Equal(val, cond, proc.Tx.Flags.DatetimeFormat, proc.Tx.Flags.GetTimeLocation())
   594  		}
   595  
   596  		if t == ternary.TRUE {
   597  			return proc.executeChild(ctx, when.Statements)
   598  		}
   599  	}
   600  
   601  	if stmt.Else.Statements == nil {
   602  		return Terminate, nil
   603  	}
   604  	return proc.executeChild(ctx, stmt.Else.Statements)
   605  }
   606  
   607  func (proc *Processor) While(ctx context.Context, stmt parser.While) (StatementFlow, error) {
   608  	childProc := proc.NewChildProcessor()
   609  	defer childProc.Close()
   610  
   611  	for {
   612  		childProc.ReferenceScope.ClearCurrentBlock()
   613  		p, err := Evaluate(ctx, childProc.ReferenceScope, stmt.Condition)
   614  		if err != nil {
   615  			return TerminateWithError, err
   616  		}
   617  		if p.Ternary() != ternary.TRUE {
   618  			break
   619  		}
   620  
   621  		f, err := childProc.execute(ctx, stmt.Statements)
   622  		if err != nil {
   623  			return TerminateWithError, err
   624  		}
   625  
   626  		switch f {
   627  		case Break:
   628  			return Terminate, nil
   629  		case Exit:
   630  			return Exit, nil
   631  		case Return:
   632  			proc.returnVal = childProc.returnVal
   633  			return Return, nil
   634  		}
   635  	}
   636  	return Terminate, nil
   637  }
   638  
   639  func (proc *Processor) WhileInCursor(ctx context.Context, stmt parser.WhileInCursor) (StatementFlow, error) {
   640  	fetchPosition := parser.FetchPosition{
   641  		Position: parser.Token{Token: parser.NEXT},
   642  	}
   643  
   644  	childProc := proc.NewChildProcessor()
   645  	defer childProc.Close()
   646  
   647  	for {
   648  		childProc.ReferenceScope.ClearCurrentBlock()
   649  		if stmt.WithDeclaration {
   650  			assigns := make([]parser.VariableAssignment, len(stmt.Variables))
   651  			for i, v := range stmt.Variables {
   652  				assigns[i] = parser.VariableAssignment{Variable: v}
   653  			}
   654  			decl := parser.VariableDeclaration{Assignments: assigns}
   655  			if err := childProc.ReferenceScope.DeclareVariable(ctx, decl); err != nil {
   656  				return TerminateWithError, err
   657  			}
   658  		}
   659  
   660  		success, err := FetchCursor(ctx, childProc.ReferenceScope, stmt.Cursor, fetchPosition, stmt.Variables)
   661  		if err != nil {
   662  			return TerminateWithError, err
   663  		}
   664  		if !success {
   665  			break
   666  		}
   667  
   668  		f, err := childProc.execute(ctx, stmt.Statements)
   669  		if err != nil {
   670  			return TerminateWithError, err
   671  		}
   672  
   673  		switch f {
   674  		case Break:
   675  			return Terminate, nil
   676  		case Exit:
   677  			return Exit, nil
   678  		case Return:
   679  			proc.returnVal = childProc.returnVal
   680  			return Return, nil
   681  		}
   682  	}
   683  
   684  	return Terminate, nil
   685  }
   686  
   687  func (proc *Processor) ExecExternalCommand(ctx context.Context, stmt parser.ExternalCommand) error {
   688  	splitter := new(excmd.ArgsSplitter).Init(stmt.Command)
   689  	var argStrs = make([]string, 0, 8)
   690  	for splitter.Scan() {
   691  		argStrs = append(argStrs, splitter.Text())
   692  	}
   693  	err := splitter.Err()
   694  	if err != nil {
   695  		return NewExternalCommandError(stmt, err.Error())
   696  	}
   697  
   698  	args := make([]string, 0, len(argStrs))
   699  	for _, argStr := range argStrs {
   700  		arg, err := EvaluateEmbeddedString(ctx, proc.ReferenceScope, argStr)
   701  		if err != nil {
   702  			if appErr, ok := err.(Error); ok {
   703  				err = NewExternalCommandError(stmt, appErr.Message())
   704  			} else {
   705  				err = NewExternalCommandError(stmt, err.Error())
   706  			}
   707  			return err
   708  		}
   709  		args = append(args, arg)
   710  	}
   711  
   712  	if len(args) < 1 {
   713  		return nil
   714  	}
   715  
   716  	c := exec.Command(args[0], args[1:]...)
   717  	c.Stdin = proc.Tx.Session.Stdin()
   718  	c.Stdout = proc.Tx.Session.Stdout()
   719  	c.Stderr = proc.Tx.Session.Stderr()
   720  
   721  	err = c.Run()
   722  	if err != nil {
   723  		err = NewExternalCommandError(stmt, err.Error())
   724  	}
   725  	return err
   726  }
   727  
   728  func (proc *Processor) showExecutionTime(ctx context.Context) {
   729  	if ctx.Err() != nil {
   730  		return
   731  	}
   732  
   733  	exectime := option.FormatNumber(time.Since(proc.measurementStart).Seconds(), 6, ".", ",", "")
   734  	stats := fmt.Sprintf(proc.Tx.Palette.Render(option.LableEffect, "Query Execution Time: ")+"%s seconds", exectime)
   735  	proc.Log(stats, false)
   736  }
   737  
   738  func (proc *Processor) Log(log string, quiet bool) {
   739  	proc.Tx.Log(log, quiet)
   740  }
   741  
   742  func (proc *Processor) LogNotice(log string, quiet bool) {
   743  	proc.Tx.LogNotice(log, quiet)
   744  }
   745  
   746  func (proc *Processor) LogWarn(log string, quiet bool) {
   747  	proc.Tx.LogWarn(log, quiet)
   748  }
   749  
   750  func (proc *Processor) LogError(log string) {
   751  	proc.Tx.LogError(log)
   752  }
   753  
   754  func (proc *Processor) AutoCommit(ctx context.Context) error {
   755  	return proc.Commit(ctx, nil)
   756  }
   757  
   758  func (proc *Processor) Commit(ctx context.Context, expr parser.Expression) error {
   759  	return proc.Tx.Commit(ctx, proc.ReferenceScope, expr)
   760  }
   761  
   762  func (proc *Processor) AutoRollback() error {
   763  	return proc.Rollback(nil)
   764  }
   765  
   766  func (proc *Processor) Rollback(expr parser.Expression) error {
   767  	return proc.Tx.Rollback(proc.ReferenceScope, expr)
   768  }
   769  
   770  func (proc *Processor) ReleaseResources() error {
   771  	return proc.Tx.ReleaseResources()
   772  }
   773  
   774  func (proc *Processor) ReleaseResourcesWithErrors() error {
   775  	return proc.Tx.ReleaseResourcesWithErrors()
   776  }