Ejemplo n.º 1
0
// SanitizeVarFreeExpr verifies a default expression is valid, has the
// correct type and contains no variable expressions.
func SanitizeVarFreeExpr(expr parser.Expr, expectedType parser.Type, context string) error {
	if parser.ContainsVars(expr) {
		return exprContainsVarsError(context, expr)
	}
	typedExpr, err := parser.TypeCheck(expr, nil, expectedType)
	if err != nil {
		return err
	}
	if defaultType := typedExpr.ResolvedType(); !expectedType.Equal(defaultType) {
		return incompatibleExprTypeError(context, expectedType, defaultType)
	}
	return nil
}
Ejemplo n.º 2
0
func (p *planner) SetTimeZone(n *parser.SetTimeZone) (planNode, error) {
	typedValue, err := parser.TypeCheck(n.Value, nil, parser.TypeInt)
	if err != nil {
		return nil, err
	}
	d, err := typedValue.Eval(&p.evalCtx)
	if err != nil {
		return nil, err
	}

	var loc *time.Location
	var offset int64
	switch v := d.(type) {
	case *parser.DString:
		location := string(*v)
		loc, err = timeutil.LoadLocation(location)
		if err != nil {
			return nil, fmt.Errorf("cannot find time zone %q: %v", location, err)
		}

	case *parser.DInterval:
		offset, _, _, err = v.Duration.Div(time.Second.Nanoseconds()).Encode()
		if err != nil {
			return nil, err
		}

	case *parser.DInt:
		offset = int64(*v) * 60 * 60

	case *parser.DFloat:
		offset = int64(float64(*v) * 60.0 * 60.0)

	case *parser.DDecimal:
		sixty := inf.NewDec(60, 0)
		sixty.Mul(sixty, sixty).Mul(sixty, &v.Dec)
		sixty.Round(sixty, 0, inf.RoundDown)
		var ok bool
		if offset, ok = sixty.Unscaled(); !ok {
			return nil, fmt.Errorf("time zone value %s would overflow an int64", sixty)
		}

	default:
		return nil, fmt.Errorf("bad time zone value: %v", n.Value)
	}
	if loc == nil {
		loc = time.FixedZone(d.String(), int(offset))
	}
	p.session.Location = loc
	return &emptyNode{}, nil
}
Ejemplo n.º 3
0
func makeDefaultExprs(
	cols []sqlbase.ColumnDescriptor, parse *parser.Parser, evalCtx *parser.EvalContext,
) ([]parser.TypedExpr, error) {
	// Check to see if any of the columns have DEFAULT expressions. If there
	// are no DEFAULT expressions, we don't bother with constructing the
	// defaults map as the defaults are all NULL.
	haveDefaults := false
	for _, col := range cols {
		if col.DefaultExpr != nil {
			haveDefaults = true
			break
		}
	}
	if !haveDefaults {
		return nil, nil
	}

	// Build the default expressions map from the parsed SELECT statement.
	defaultExprs := make([]parser.TypedExpr, 0, len(cols))
	exprStrings := make([]string, 0, len(cols))
	for _, col := range cols {
		if col.DefaultExpr != nil {
			exprStrings = append(exprStrings, *col.DefaultExpr)
		}
	}
	exprs, err := parser.ParseExprsTraditional(exprStrings)
	if err != nil {
		return nil, err
	}

	defExprIdx := 0
	for _, col := range cols {
		if col.DefaultExpr == nil {
			defaultExprs = append(defaultExprs, parser.DNull)
			continue
		}
		expr := exprs[defExprIdx]
		typedExpr, err := parser.TypeCheck(expr, nil, col.Type.ToDatumType())
		if err != nil {
			return nil, err
		}
		if typedExpr, err = parse.NormalizeExpr(evalCtx, typedExpr); err != nil {
			return nil, err
		}
		defaultExprs = append(defaultExprs, typedExpr)
		defExprIdx++
	}
	return defaultExprs, nil
}
Ejemplo n.º 4
0
// analyzeExpr performs semantic analysis of an axpression, including:
// - replacing sub-queries by a sql.subquery node;
// - resolving names (optional);
// - type checking (with optional type enforcement);
// - normalization.
// The parameters sources and IndexedVars, if both are non-nil, indicate
// name resolution should be performed. The IndexedVars map will be filled
// as a result.
func (p *planner) analyzeExpr(
	raw parser.Expr,
	sources multiSourceInfo,
	ivarHelper parser.IndexedVarHelper,
	expectedType parser.Type,
	requireType bool,
	typingContext string,
) (parser.TypedExpr, error) {
	// Replace the sub-queries.
	// In all contexts that analyze a single expression, a single value
	// is expected. Tell this to replaceSubqueries.  (See UPDATE for a
	// counter-example; cases where a subquery is an operand of a
	// comparison are handled specially in the subqueryVisitor already.)
	replaced, err := p.replaceSubqueries(raw, 1 /* one value expected */)
	if err != nil {
		return nil, err
	}

	// Perform optional name resolution.
	var resolved parser.Expr
	if sources == nil {
		resolved = replaced
	} else {
		resolved, err = p.resolveNames(replaced, sources, ivarHelper)
		if err != nil {
			return nil, err
		}
	}

	// Type check.
	var typedExpr parser.TypedExpr
	if requireType {
		typedExpr, err = parser.TypeCheckAndRequire(resolved, &p.semaCtx,
			expectedType, typingContext)
	} else {
		typedExpr, err = parser.TypeCheck(resolved, &p.semaCtx, expectedType)
	}
	if err != nil {
		return nil, err
	}

	// Normalize.
	return p.parser.NormalizeExpr(&p.evalCtx, typedExpr)
}
Ejemplo n.º 5
0
func parseAndNormalizeExpr(t *testing.T, sql string, sel *selectNode) parser.TypedExpr {
	expr, err := parser.ParseExprTraditional(sql)
	if err != nil {
		t.Fatalf("%s: %v", sql, err)
	}

	// Perform name resolution because {analyze,simplify}Expr want
	// expressions containing IndexedVars.
	if expr, err = sel.resolveNames(expr); err != nil {
		t.Fatalf("%s: %v", sql, err)
	}
	typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference)
	if err != nil {
		t.Fatalf("%s: %v", sql, err)
	}
	ctx := &parser.EvalContext{}
	if typedExpr, err = ctx.NormalizeExpr(typedExpr); err != nil {
		t.Fatalf("%s: %v", sql, err)
	}
	return typedExpr
}
Ejemplo n.º 6
0
// processExpression parses the string expression inside an Expression,
// interpreting $0, $1, etc as indexed variables.
func processExpression(exprSpec Expression, h *parser.IndexedVarHelper) (parser.TypedExpr, error) {
	if exprSpec.Expr == "" {
		return nil, nil
	}
	expr, err := parser.ParseExprTraditional(exprSpec.Expr)
	if err != nil {
		return nil, err
	}

	// Convert Placeholders to IndexedVars
	v := valArgsConvert{h: h, err: nil}
	expr, _ = parser.WalkExpr(&v, expr)
	if v.err != nil {
		return nil, v.err
	}

	// Convert to a fully typed expression.
	typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference)
	if err != nil {
		return nil, err
	}

	return typedExpr, nil
}
Ejemplo n.º 7
0
// processExpression parses the string expression inside an Expression,
// and associates ordinal references (@1, @2, etc) with the given helper.
func processExpression(exprSpec Expression, h *parser.IndexedVarHelper) (parser.TypedExpr, error) {
	if exprSpec.Expr == "" {
		return nil, nil
	}
	expr, err := parser.ParseExprTraditional(exprSpec.Expr)
	if err != nil {
		return nil, err
	}

	// Bind IndexedVars to our eh.vars.
	v := ivarBinder{h: h, err: nil}
	parser.WalkExprConst(&v, expr)
	if v.err != nil {
		return nil, v.err
	}

	// Convert to a fully typed expression.
	typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference)
	if err != nil {
		return nil, err
	}

	return typedExpr, nil
}
Ejemplo n.º 8
0
// Update updates columns for a selection of rows from a table.
// Privileges: UPDATE and SELECT on table. We currently always use a select statement.
//   Notes: postgres requires UPDATE. Requires SELECT with WHERE clause with table.
//          mysql requires UPDATE. Also requires SELECT with WHERE clause with table.
// TODO(guanqun): need to support CHECK in UPDATE
func (p *planner) Update(
	n *parser.Update, desiredTypes []parser.Type, autoCommit bool,
) (planNode, error) {
	tracing.AnnotateTrace()

	tn, err := p.getAliasedTableName(n.Table)
	if err != nil {
		return nil, err
	}

	en, err := p.makeEditNode(tn, autoCommit, privilege.UPDATE)
	if err != nil {
		return nil, err
	}

	exprs := make([]*parser.UpdateExpr, len(n.Exprs))
	for i, expr := range n.Exprs {
		// Replace the sub-query nodes.
		newExpr, err := p.replaceSubqueries(expr.Expr, len(expr.Names))
		if err != nil {
			return nil, err
		}
		exprs[i] = &parser.UpdateExpr{Tuple: expr.Tuple, Expr: newExpr, Names: expr.Names}
	}

	// Determine which columns we're inserting into.
	names, err := p.namesForExprs(exprs)
	if err != nil {
		return nil, err
	}

	updateCols, err := p.processColumns(en.tableDesc, names)
	if err != nil {
		return nil, err
	}

	defaultExprs, err := makeDefaultExprs(updateCols, &p.parser, &p.evalCtx)
	if err != nil {
		return nil, err
	}

	var requestedCols []sqlbase.ColumnDescriptor
	if len(n.Returning) > 0 || len(en.tableDesc.Checks) > 0 {
		// TODO(dan): This could be made tighter, just the rows needed for RETURNING
		// exprs.
		requestedCols = en.tableDesc.Columns
	}

	fkTables := tablesNeededForFKs(*en.tableDesc, CheckUpdates)
	if err := p.fillFKTableMap(fkTables); err != nil {
		return nil, err
	}
	ru, err := makeRowUpdater(p.txn, en.tableDesc, fkTables, updateCols, requestedCols, rowUpdaterDefault)
	if err != nil {
		return nil, err
	}
	tw := tableUpdater{ru: ru, autoCommit: autoCommit}

	tracing.AnnotateTrace()

	// Generate the list of select targets. We need to select all of the columns
	// plus we select all of the update expressions in case those expressions
	// reference columns (e.g. "UPDATE t SET v = v + 1"). Note that we flatten
	// expressions for tuple assignments just as we flattened the column names
	// above. So "UPDATE t SET (a, b) = (1, 2)" translates into select targets of
	// "*, 1, 2", not "*, (1, 2)".
	targets := sqlbase.ColumnsSelectors(ru.fetchCols)
	i := 0
	// Remember the index where the targets for exprs start.
	exprTargetIdx := len(targets)
	desiredTypesFromSelect := make([]parser.Type, len(targets), len(targets)+len(exprs))
	for i := range targets {
		desiredTypesFromSelect[i] = parser.TypeAny
	}
	for _, expr := range exprs {
		if expr.Tuple {
			switch t := expr.Expr.(type) {
			case (*parser.Tuple):
				for _, e := range t.Exprs {
					typ := updateCols[i].Type.ToDatumType()
					e := fillDefault(e, typ, i, defaultExprs)
					targets = append(targets, parser.SelectExpr{Expr: e})
					desiredTypesFromSelect = append(desiredTypesFromSelect, typ)
					i++
				}
			default:
				return nil, fmt.Errorf("cannot use this expression to assign multiple columns: %s", expr.Expr)
			}
		} else {
			typ := updateCols[i].Type.ToDatumType()
			e := fillDefault(expr.Expr, typ, i, defaultExprs)
			targets = append(targets, parser.SelectExpr{Expr: e})
			desiredTypesFromSelect = append(desiredTypesFromSelect, typ)
			i++
		}
	}

	rows, err := p.SelectClause(&parser.SelectClause{
		Exprs: targets,
		From:  &parser.From{Tables: []parser.TableExpr{n.Table}},
		Where: n.Where,
	}, nil, nil, desiredTypesFromSelect, publicAndNonPublicColumns)
	if err != nil {
		return nil, err
	}

	// Placeholders have their types populated in the above Select if they are part
	// of an expression ("SET a = 2 + $1") in the type check step where those
	// types are inferred. For the simpler case ("SET a = $1"), populate them
	// using checkColumnType. This step also verifies that the expression
	// types match the column types.
	sel := rows.(*selectTopNode).source.(*selectNode)
	for i, target := range sel.render[exprTargetIdx:] {
		// DefaultVal doesn't implement TypeCheck
		if _, ok := target.(parser.DefaultVal); ok {
			continue
		}
		// TODO(nvanbenschoten) isn't this TypeCheck redundant with the call to SelectClause?
		typedTarget, err := parser.TypeCheck(target, &p.semaCtx, updateCols[i].Type.ToDatumType())
		if err != nil {
			return nil, err
		}
		err = sqlbase.CheckColumnType(updateCols[i], typedTarget.ResolvedType(), p.semaCtx.Placeholders)
		if err != nil {
			return nil, err
		}
	}

	updateColsIdx := make(map[sqlbase.ColumnID]int, len(ru.updateCols))
	for i, col := range ru.updateCols {
		updateColsIdx[col.ID] = i
	}

	un := &updateNode{
		n:             n,
		editNodeBase:  en,
		updateCols:    ru.updateCols,
		updateColsIdx: updateColsIdx,
		tw:            tw,
	}
	if err := un.checkHelper.init(p, tn, en.tableDesc); err != nil {
		return nil, err
	}
	if err := un.run.initEditNode(&un.editNodeBase, rows, n.Returning, desiredTypes); err != nil {
		return nil, err
	}
	return un, nil
}
Ejemplo n.º 9
0
// Set sets session variables.
// Privileges: None.
//   Notes: postgres/mysql do not require privileges for session variables (some exceptions).
func (p *planner) Set(n *parser.Set) (planNode, error) {
	if n.Name == nil {
		// A client has sent the reserved internal syntax SET ROW ...
		// Reject it.
		return nil, errors.New("invalid statement: SET ROW")
	}

	// By using VarName.String() here any variables that are keywords will
	// be double quoted.
	name := strings.ToUpper(n.Name.String())
	typedValues := make([]parser.TypedExpr, len(n.Values))
	for i, expr := range n.Values {
		typedValue, err := parser.TypeCheck(expr, nil, parser.TypeString)
		if err != nil {
			return nil, err
		}
		typedValues[i] = typedValue
	}
	switch name {
	case `DATABASE`:
		dbName, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		if len(dbName) != 0 {
			// Verify database descriptor exists.
			if _, err := p.mustGetDatabaseDesc(dbName); err != nil {
				return nil, err
			}
		}
		p.session.Database = dbName
		p.evalCtx.Database = dbName

	case `SYNTAX`:
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch parser.Name(s).Normalize() {
		case parser.ReNormalizeName(parser.Modern.String()):
			p.session.Syntax = int32(parser.Modern)
		case parser.ReNormalizeName(parser.Traditional.String()):
			p.session.Syntax = int32(parser.Traditional)
		default:
			return nil, fmt.Errorf("%s: \"%s\" is not in (%q, %q)", name, s, parser.Modern, parser.Traditional)
		}

	case `EXTRA_FLOAT_DIGITS`:
		// These settings are sent by the JDBC driver but we silently ignore them.

	case `APPLICATION_NAME`:
		// These settings are sent by the clients to improve query logging on the server,
		// but we silently ignore them.

	case `DEFAULT_TRANSACTION_ISOLATION`:
		// It's unfortunate that clients want us to support both SET
		// SESSION CHARACTERISTICS AS TRANSACTION ..., which takes the
		// isolation level as keywords/identifiers (e.g. JDBC), and SET
		// DEFAULT_TRANSACTION_ISOLATION TO '...', which takes an
		// expression (e.g. psycopg2). But that's how it is.  Just ensure
		// this code keeps in sync with SetDefaultIsolation() below.
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch strings.ToUpper(s) {
		case `READ UNCOMMITTED`, `READ COMMITTED`, `SNAPSHOT`:
			p.session.DefaultIsolationLevel = enginepb.SNAPSHOT
		case `REPEATABLE READ`, `SERIALIZABLE`:
			p.session.DefaultIsolationLevel = enginepb.SERIALIZABLE
		default:
			return nil, fmt.Errorf("%s: unknown isolation level: %q", name, s)
		}

	case `DIST_SQL`:
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch parser.Name(s).Normalize() {
		case parser.ReNormalizeName("sync"):
			p.session.DistSQLMode = distSQLSync
		case parser.ReNormalizeName("async"):
			p.session.DistSQLMode = distSQLAsync
		default:
			return nil, fmt.Errorf("%s: \"%s\" not supported", name, s)
		}

	default:
		return nil, fmt.Errorf("unknown variable: %q", name)
	}
	return &emptyNode{}, nil
}
Ejemplo n.º 10
0
// Set sets session variables.
// Privileges: None.
//   Notes: postgres/mysql do not require privileges for session variables (some exceptions).
func (p *planner) Set(n *parser.Set) (planNode, error) {
	if n.Name == nil {
		// A client has sent the reserved internal syntax SET ROW ...
		// Reject it.
		return nil, errors.New("invalid statement: SET ROW")
	}

	// By using VarName.String() here any variables that are keywords will
	// be double quoted.
	name := strings.ToUpper(n.Name.String())
	typedValues := make([]parser.TypedExpr, len(n.Values))
	for i, expr := range n.Values {
		typedValue, err := parser.TypeCheck(expr, nil, parser.TypeString)
		if err != nil {
			return nil, err
		}
		typedValues[i] = typedValue
	}
	switch name {
	case `DATABASE`:
		dbName, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		if len(dbName) != 0 {
			// Verify database descriptor exists.
			if _, err := p.mustGetDatabaseDesc(dbName); err != nil {
				return nil, err
			}
		}
		p.session.Database = dbName
		p.evalCtx.Database = dbName

	case `SYNTAX`:
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch parser.Name(s).Normalize() {
		case parser.ReNormalizeName(parser.Modern.String()):
			p.session.Syntax = int32(parser.Modern)
		case parser.ReNormalizeName(parser.Traditional.String()):
			p.session.Syntax = int32(parser.Traditional)
		default:
			return nil, fmt.Errorf("%s: \"%s\" is not in (%q, %q)", name, s, parser.Modern, parser.Traditional)
		}

	case `DEFAULT_TRANSACTION_ISOLATION`:
		// It's unfortunate that clients want us to support both SET
		// SESSION CHARACTERISTICS AS TRANSACTION ..., which takes the
		// isolation level as keywords/identifiers (e.g. JDBC), and SET
		// DEFAULT_TRANSACTION_ISOLATION TO '...', which takes an
		// expression (e.g. psycopg2). But that's how it is.  Just ensure
		// this code keeps in sync with SetDefaultIsolation() below.
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch strings.ToUpper(s) {
		case `READ UNCOMMITTED`, `READ COMMITTED`, `SNAPSHOT`:
			p.session.DefaultIsolationLevel = enginepb.SNAPSHOT
		case `REPEATABLE READ`, `SERIALIZABLE`:
			p.session.DefaultIsolationLevel = enginepb.SERIALIZABLE
		default:
			return nil, fmt.Errorf("%s: unknown isolation level: %q", name, s)
		}

	case `DIST_SQL`:
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		switch parser.Name(s).Normalize() {
		case parser.ReNormalizeName("off"):
			p.session.DistSQLMode = distSQLOff
		case parser.ReNormalizeName("on"):
			p.session.DistSQLMode = distSQLOn
		case parser.ReNormalizeName("always"):
			p.session.DistSQLMode = distSQLAlways
		default:
			return nil, fmt.Errorf("%s: \"%s\" not supported", name, s)
		}

	// These settings are sent by various client drivers. We don't support
	// changing them, so we either silently ignore them or throw an error given
	// a setting that we do not respect.
	case `EXTRA_FLOAT_DIGITS`:
	// See https://www.postgresql.org/docs/9.6/static/runtime-config-client.html
	case `APPLICATION_NAME`:
	// Set by clients to improve query logging.
	// See https://www.postgresql.org/docs/9.6/static/runtime-config-logging.html#GUC-APPLICATION-NAME
	case `CLIENT_ENCODING`:
		// See https://www.postgresql.org/docs/9.6/static/multibyte.html
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		if strings.ToUpper(s) != "UTF8" {
			return nil, fmt.Errorf("non-UTF8 encoding %s not supported", s)
		}
	case `SEARCH_PATH`:
	// Controls the schema search order. We don't really support this as we
	// don't have first-class support for schemas.
	// TODO(jordan) can we hook this up to EvalContext.SearchPath without
	// breaking things?
	// See https://www.postgresql.org/docs/9.6/static/runtime-config-client.html
	case `STANDARD_CONFORMING_STRINGS`:
		// If true, escape backslash literals in strings. We do this by default,
		// and we do not support the opposite behavior.
		// See https://www.postgresql.org/docs/9.1/static/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS
		s, err := p.getStringVal(name, typedValues)
		if err != nil {
			return nil, err
		}
		if parser.Name(s).Normalize() != parser.ReNormalizeName("on") {
			return nil, fmt.Errorf("%s: \"%s\" not supported", name, s)
		}
	case `CLIENT_MIN_MESSAGES`:
	// Controls returned message verbosity. We don't support this.
	// See https://www.postgresql.org/docs/9.6/static/runtime-config-compatible.html

	default:
		return nil, fmt.Errorf("unknown variable: %q", name)
	}
	return &emptyNode{}, nil
}