// TypeCheck implements the Expr interface. func (expr *IndirectionExpr) TypeCheck(ctx *SemaContext, desired Type) (TypedExpr, error) { for i, part := range expr.Indirection { switch t := part.(type) { case *ArraySubscript: if t.Slice { return nil, util.UnimplementedWithIssueErrorf(2115, "ARRAY slicing in %s", expr) } if i > 0 { return nil, util.UnimplementedWithIssueErrorf(2115, "multidimensional ARRAY %s", expr) } beginExpr, err := typeCheckAndRequire(ctx, t.Begin, TypeInt, "ARRAY subscript") if err != nil { return nil, err } t.Begin = beginExpr default: return nil, errors.Errorf("syntax not yet supported: %s", expr.Indirection) } } subExpr, err := expr.Expr.TypeCheck(ctx, tArray{desired}) if err != nil { return nil, err } typ := subExpr.ResolvedType() arrType, ok := typ.(tArray) if !ok { return nil, errors.Errorf("cannot subscript type %s because it is not an array", typ) } expr.Expr = subExpr expr.typ = arrType.Typ return expr, nil }
func (p *planner) processColumns( tableDesc *sqlbase.TableDescriptor, node parser.UnresolvedNames, ) ([]sqlbase.ColumnDescriptor, error) { if node == nil { // VisibleColumns is used here to prevent INSERT INTO <table> VALUES (...) // (as opposed to INSERT INTO <table> (...) VALUES (...)) from writing // hidden columns. At present, the only hidden column is the implicit rowid // primary key column. return tableDesc.VisibleColumns(), nil } cols := make([]sqlbase.ColumnDescriptor, len(node)) colIDSet := make(map[sqlbase.ColumnID]struct{}, len(node)) for i, n := range node { c, err := n.NormalizeUnqualifiedColumnItem() if err != nil { return nil, err } if len(c.Selector) > 0 { return nil, util.UnimplementedWithIssueErrorf(8318, "compound types not supported yet: %q", n) } col, err := tableDesc.FindActiveColumnByName(c.ColumnName) if err != nil { return nil, err } if _, ok := colIDSet[col.ID]; ok { return nil, fmt.Errorf("multiple assignments to the same column %q", n) } colIDSet[col.ID] = struct{}{} cols[i] = col } return cols, nil }
// execStmtInOpenTxn executes one statement in the context // of the planner's transaction (which is assumed to exist). // It handles statements that affect the transaction state (BEGIN, COMMIT) // and delegates everything else to `execStmt`. // It binds placeholders. // // The current transaction might be committed/rolled back when this returns. // It might also have transitioned to the aborted or RestartWait state. // // Args: // implicitTxn: set if the current transaction was implicitly // created by the system (i.e. the client sent the statement outside of // a transaction). // COMMIT/ROLLBACK statements are rejected if set. Also, the transaction // might be auto-committed in this function. // firstInTxn: set for the first statement in a transaction. Used // so that nested BEGIN statements are caught. // stmtTimestamp: Used as the statement_timestamp(). // // Returns: // - a Result // - an error, if any. In case of error, the result returned also reflects this error. func (e *Executor) execStmtInOpenTxn( stmt parser.Statement, planMaker *planner, implicitTxn bool, firstInTxn bool, txnState *txnState, ) (Result, error) { if txnState.State != Open { panic("execStmtInOpenTxn called outside of an open txn") } if planMaker.txn == nil { panic("execStmtInOpenTxn called with a txn not set on the planner") } planMaker.evalCtx.SetTxnTimestamp(txnState.sqlTimestamp) planMaker.evalCtx.SetStmtTimestamp(e.cfg.Clock.PhysicalTime()) session := planMaker.session log.Eventf(session.context, "%s", stmt) // TODO(cdo): Figure out how to not double count on retries. e.updateStmtCounts(stmt) switch s := stmt.(type) { case *parser.BeginTransaction: if !firstInTxn { txnState.updateStateAndCleanupOnErr(errTransactionInProgress, e) return Result{Err: errTransactionInProgress}, errTransactionInProgress } case *parser.CommitTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } // CommitTransaction is executed fully here; there's no planNode for it // and the planner is not involved at all. res, err := commitSQLTransaction(txnState, planMaker, commit, e) return res, err case *parser.ReleaseSavepoint: if implicitTxn { return e.noTransactionHelper(txnState) } if err := parser.ValidateRestartCheckpoint(s.Savepoint); err != nil { return Result{Err: err}, err } // ReleaseSavepoint is executed fully here; there's no planNode for it // and the planner is not involved at all. res, err := commitSQLTransaction(txnState, planMaker, release, e) return res, err case *parser.RollbackTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } // RollbackTransaction is executed fully here; there's no planNode for it // and the planner is not involved at all. // Notice that we don't return any errors on rollback. return rollbackSQLTransaction(txnState, planMaker), nil case *parser.SetTransaction: if implicitTxn { return e.noTransactionHelper(txnState) } case *parser.Savepoint: if implicitTxn { return e.noTransactionHelper(txnState) } if err := parser.ValidateRestartCheckpoint(s.Name); err != nil { return Result{Err: err}, err } // We want to disallow SAVEPOINTs to be issued after a transaction has // started running, but such enforcement is problematic in the // presence of transaction retries (since the transaction proto is // necessarily reused). To work around this, we keep track of the // transaction's retrying state and special-case SAVEPOINT when it is // set. // // TODO(andrei): the check for retrying is a hack - we erroneously // allow SAVEPOINT to be issued at any time during a retry, not just // in the beginning. We should figure out how to track whether we // started using the transaction during a retry. if txnState.txn.Proto.IsInitialized() && !txnState.retrying { err := fmt.Errorf("SAVEPOINT %s needs to be the first statement in a transaction", parser.RestartSavepointName) txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err } // Note that Savepoint doesn't have a corresponding plan node. // This here is all the execution there is. txnState.retryIntent = true return Result{}, nil case *parser.RollbackToSavepoint: err := parser.ValidateRestartCheckpoint(s.Savepoint) if err == nil { // Can't restart if we didn't get an error first, which would've put the // txn in a different state. err = errNotRetriable } txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err case *parser.Prepare: err := util.UnimplementedWithIssueErrorf(7568, "Prepared statements are supported only via the Postgres wire protocol") txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err case *parser.Execute: err := util.UnimplementedWithIssueErrorf(7568, "Executing prepared statements is supported only via the Postgres wire protocol") txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err case *parser.Deallocate: if s.Name == "" { planMaker.session.PreparedStatements.DeleteAll() } else { if found := planMaker.session.PreparedStatements.Delete(string(s.Name)); !found { err := fmt.Errorf("prepared statement %s does not exist", s.Name) txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err } } return Result{PGTag: s.StatementTag()}, nil } autoCommit := implicitTxn && !e.cfg.TestingKnobs.DisableAutoCommit result, err := e.execStmt(stmt, planMaker, autoCommit) if err != nil { if result.Rows != nil { result.Rows.Close() result.Rows = nil } if traceSQL { log.ErrEventf(txnState.txn.Context, "ERROR: %v", err) } log.ErrEventf(session.context, "ERROR: %v", err) txnState.updateStateAndCleanupOnErr(err, e) return Result{Err: err}, err } tResult := &traceResult{tag: result.PGTag, count: -1} switch result.Type { case parser.RowsAffected: tResult.count = result.RowsAffected case parser.Rows: tResult.count = result.Rows.Len() } if traceSQL { log.Eventf(txnState.txn.Context, "%s done", tResult) } log.Eventf(session.context, "%s done", tResult) return result, nil }
// findColumn looks up the column specified by a ColumnItem. The // function returns the index of the source in the multiSourceInfo // array and the column index for the column array of that // source. Returns invalid indices and an error if the source is not // found or the name is ambiguous. func (sources multiSourceInfo) findColumn(c *parser.ColumnItem) (srcIdx int, colIdx int, err error) { if len(c.Selector) > 0 { return invalidSrcIdx, invalidColIdx, util.UnimplementedWithIssueErrorf(8318, "compound types not supported yet: %q", c) } colName := c.ColumnName.Normalize() var tableName parser.TableName if c.TableName.Table() != "" { tableName = c.TableName.NormalizedTableName() tn, err := sources.checkDatabaseName(tableName) if err != nil { return invalidSrcIdx, invalidColIdx, err } tableName = tn // Propagate the discovered database name back to the original VarName. // (to clarify the output of e.g. EXPLAIN) c.TableName.DatabaseName = tableName.DatabaseName } colIdx = invalidColIdx for iSrc, src := range sources { findColHelper := func(src *dataSourceInfo, iSrc, srcIdx, colIdx int, idx int) (int, int, error) { col := src.sourceColumns[idx] if parser.ReNormalizeName(col.Name) == colName { if colIdx != invalidColIdx { return invalidSrcIdx, invalidColIdx, fmt.Errorf("column reference %q is ambiguous", c) } srcIdx = iSrc colIdx = idx } return srcIdx, colIdx, nil } if tableName.Table() == "" { for idx := 0; idx < len(src.sourceColumns); idx++ { srcIdx, colIdx, err = findColHelper(src, iSrc, srcIdx, colIdx, idx) if err != nil { return srcIdx, colIdx, err } } } else { colRange, ok := src.sourceAliases[tableName] if !ok { // The data source "src" has no column for table tableName. // Try again with the net one. continue } for _, idx := range colRange { srcIdx, colIdx, err = findColHelper(src, iSrc, srcIdx, colIdx, idx) if err != nil { return srcIdx, colIdx, err } } } } if colIdx == invalidColIdx { return invalidSrcIdx, invalidColIdx, fmt.Errorf("column name %q not found", c) } return srcIdx, colIdx, nil }