func analyzeInsert(ins *sqlparser.Insert, getTable TableGetter) (plan *ExecPlan, err error) { plan = &ExecPlan{ PlanId: PLAN_PASS_DML, FullQuery: GenerateFullQuery(ins), } tableName := sqlparser.GetTableName(ins.Table) if tableName == "" { plan.Reason = REASON_TABLE return plan, nil } tableInfo, err := plan.setTableInfo(tableName, getTable) if err != nil { return nil, err } if len(tableInfo.Indexes) == 0 || tableInfo.Indexes[0].Name != "PRIMARY" { log.Warningf("no primary key for table %s", tableName) plan.Reason = REASON_TABLE_NOINDEX return plan, nil } pkColumnNumbers := getInsertPKColumns(ins.Columns, tableInfo) if ins.OnDup != nil { // Upserts are not safe for statement based replication: // http://bugs.mysql.com/bug.php?id=58637 plan.Reason = REASON_UPSERT return plan, nil } if sel, ok := ins.Rows.(sqlparser.SelectStatement); ok { plan.PlanId = PLAN_INSERT_SUBQUERY plan.OuterQuery = GenerateInsertOuterQuery(ins) plan.Subquery = GenerateSelectLimitQuery(sel) if len(ins.Columns) != 0 { plan.ColumnNumbers, err = analyzeSelectExprs(sqlparser.SelectExprs(ins.Columns), tableInfo) if err != nil { return nil, err } } else { // StarExpr node will expand into all columns n := sqlparser.SelectExprs{&sqlparser.StarExpr{}} plan.ColumnNumbers, err = analyzeSelectExprs(n, tableInfo) if err != nil { return nil, err } } plan.SubqueryPKColumns = pkColumnNumbers return plan, nil } // If it's not a sqlparser.SelectStatement, it's Values. rowList := ins.Rows.(sqlparser.Values) pkValues, err := getInsertPKValues(pkColumnNumbers, rowList, tableInfo) if err != nil { return nil, err } if pkValues != nil { plan.PlanId = PLAN_INSERT_PK plan.OuterQuery = plan.FullQuery plan.PKValues = pkValues } return plan, nil }
func analyzeInsert(ins *sqlparser.Insert, getTable TableGetter) (plan *ExecPlan, err error) { plan = &ExecPlan{ PlanID: PlanPassDML, FullQuery: GenerateFullQuery(ins), } tableName := sqlparser.GetTableName(ins.Table) if tableName == "" { plan.Reason = ReasonTable return plan, nil } tableInfo, err := plan.setTableInfo(tableName, getTable) if err != nil { return nil, err } if len(tableInfo.Indexes) == 0 || tableInfo.Indexes[0].Name != "PRIMARY" { log.Warningf("no primary key for table %s", tableName) plan.Reason = ReasonTableNoIndex return plan, nil } pkColumnNumbers := getInsertPKColumns(ins.Columns, tableInfo) if sel, ok := ins.Rows.(sqlparser.SelectStatement); ok { if ins.OnDup != nil { // Upserts not allowed for subqueries. // http://bugs.mysql.com/bug.php?id=58637 plan.Reason = ReasonUpsert return plan, nil } plan.PlanID = PlanInsertSubquery plan.OuterQuery = GenerateInsertOuterQuery(ins) plan.Subquery = GenerateSelectLimitQuery(sel) if len(ins.Columns) != 0 { plan.ColumnNumbers, err = analyzeSelectExprs(sqlparser.SelectExprs(ins.Columns), tableInfo) if err != nil { return nil, err } } else { // StarExpr node will expand into all columns n := sqlparser.SelectExprs{&sqlparser.StarExpr{}} plan.ColumnNumbers, err = analyzeSelectExprs(n, tableInfo) if err != nil { return nil, err } } plan.SubqueryPKColumns = pkColumnNumbers return plan, nil } // If it's not a sqlparser.SelectStatement, it's Values. rowList := ins.Rows.(sqlparser.Values) pkValues, err := getInsertPKValues(pkColumnNumbers, rowList, tableInfo) if err != nil { return nil, err } if pkValues == nil { plan.Reason = ReasonComplexExpr return plan, nil } plan.PKValues = pkValues if ins.OnDup == nil { plan.PlanID = PlanInsertPK plan.OuterQuery = sqlparser.GenerateParsedQuery(ins) return plan, nil } if len(rowList) > 1 { // Upsert supported only for single row inserts. plan.Reason = ReasonUpsert return plan, nil } plan.SecondaryPKValues, err = analyzeUpdateExpressions(sqlparser.UpdateExprs(ins.OnDup), tableInfo.Indexes[0]) if err != nil { if err == ErrTooComplex { plan.Reason = ReasonPKChange return plan, nil } return nil, err } plan.PlanID = PlanUpsertPK newins := *ins newins.Ignore = "" newins.OnDup = nil plan.OuterQuery = sqlparser.GenerateParsedQuery(&newins) upd := &sqlparser.Update{ Comments: ins.Comments, Table: ins.Table, Exprs: sqlparser.UpdateExprs(ins.OnDup), } plan.UpsertQuery = GenerateUpdateOuterQuery(upd) return plan, nil }
// Wireup performs the wire-up tasks. func (rb *route) Wireup(bldr builder, jt *jointab) error { // Resolve values stored in the builder. var err error switch vals := rb.ERoute.Values.(type) { case *sqlparser.ComparisonExpr: // A comparison expression is stored only if it was an IN clause. // We have to convert it to use a list argutment and resolve values. rb.ERoute.Values, err = rb.procureValues(bldr, jt, vals.Right) if err != nil { return err } vals.Right = sqlparser.ListArg("::" + engine.ListVarName) default: rb.ERoute.Values, err = rb.procureValues(bldr, jt, vals) if err != nil { return err } } // Fix up the AST. _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { switch node := node.(type) { case *sqlparser.Select: if len(node.SelectExprs) == 0 { node.SelectExprs = sqlparser.SelectExprs([]sqlparser.SelectExpr{ &sqlparser.NonStarExpr{ Expr: sqlparser.NumVal([]byte{'1'}), }, }) } case *sqlparser.ComparisonExpr: if node.Operator == sqlparser.EqualStr { if exprIsValue(node.Left, rb) && !exprIsValue(node.Right, rb) { node.Left, node.Right = node.Right, node.Left } } } return true, nil }, &rb.Select) // Generate query while simultaneously resolving values. varFormatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { switch node := node.(type) { case *sqlparser.ColName: if !rb.isLocal(node) { joinVar := jt.Procure(bldr, node, rb.Order()) rb.ERoute.JoinVars[joinVar] = struct{}{} buf.Myprintf("%a", ":"+joinVar) return } case *sqlparser.TableName: node.Name.Format(buf) return } node.Format(buf) } buf := sqlparser.NewTrackedBuffer(varFormatter) varFormatter(buf, &rb.Select) rb.ERoute.Query = buf.ParsedQuery().Query rb.ERoute.FieldQuery = rb.generateFieldQuery(&rb.Select, jt) return nil }