func buildDeletePlan(del *sqlparser.Delete, schema *Schema) *Plan { plan := &Plan{ ID: NoPlan, Rewritten: generateQuery(del), } tablename := sqlparser.GetTableName(del.Table) plan.Table, plan.Reason = schema.FindTable(tablename) if plan.Reason != "" { return plan } if !plan.Table.Keyspace.Sharded { plan.ID = DeleteUnsharded return plan } getWhereRouting(del.Where, plan, true) switch plan.ID { case SelectEqual: plan.ID = DeleteEqual case SelectIN, SelectScatter: plan.ID = NoPlan plan.Reason = "too complex" default: panic("unexpected") } return plan }
func buildUpdatePlan(upd *sqlparser.Update, schema *Schema) *Plan { plan := &Plan{ ID: NoPlan, Rewritten: generateQuery(upd), } tablename := sqlparser.GetTableName(upd.Table) plan.Table, plan.Reason = schema.FindTable(tablename) if plan.Reason != "" { return plan } if !plan.Table.Keyspace.Sharded { plan.ID = UpdateUnsharded return plan } getWhereRouting(upd.Where, plan, true) switch plan.ID { case SelectEqual: plan.ID = UpdateEqual case SelectIN, SelectScatter: plan.ID = NoPlan plan.Reason = "too complex" return plan default: panic("unexpected") } if isIndexChanging(upd.Exprs, plan.Table.ColVindexes) { plan.ID = NoPlan plan.Reason = "index is changing" } return plan }
// Ensure that the input query is a Select statement that contains no Join, // GroupBy, OrderBy, Limit or Distinct operations. Also ensure that the // source table is present in the schema and has at least one primary key. func (qs *QuerySplitter) validateQuery() error { statement, err := sqlparser.Parse(qs.query.Sql) if err != nil { return err } var ok bool qs.sel, ok = statement.(*sqlparser.Select) if !ok { return fmt.Errorf("not a select statement") } if qs.sel.Distinct != "" || qs.sel.GroupBy != nil || qs.sel.Having != nil || len(qs.sel.From) != 1 || qs.sel.OrderBy != nil || qs.sel.Limit != nil || qs.sel.Lock != "" { return fmt.Errorf("unsupported query") } node, ok := qs.sel.From[0].(*sqlparser.AliasedTableExpr) if !ok { return fmt.Errorf("unsupported query") } qs.tableName = sqlparser.GetTableName(node.Expr) if qs.tableName == "" { return fmt.Errorf("not a simple table expression") } tableInfo, ok := qs.schemaInfo.tables[qs.tableName] if !ok { return fmt.Errorf("can't find table in schema") } if len(tableInfo.PKColumns) == 0 { return fmt.Errorf("no primary keys") } qs.pkCol = tableInfo.GetPKColumn(0).Name return nil }
func buildInsertPlan(ins *sqlparser.Insert, schema *Schema) *Plan { plan := &Plan{ ID: NoPlan, Rewritten: generateQuery(ins), } tablename := sqlparser.GetTableName(ins.Table) plan.Table, plan.Reason = schema.FindTable(tablename) if plan.Reason != "" { return plan } if !plan.Table.Keyspace.Sharded { plan.ID = InsertUnsharded return plan } if len(ins.Columns) == 0 { plan.Reason = "no column list" return plan } var values sqlparser.Values switch rows := ins.Rows.(type) { case *sqlparser.Select, *sqlparser.Union: plan.Reason = "subqueries not allowed" return plan case sqlparser.Values: values = rows default: panic("unexpected") } if len(values) != 1 { plan.Reason = "multi-row inserts not supported" return plan } switch values[0].(type) { case *sqlparser.Subquery: plan.Reason = "subqueries not allowed" return plan } row := values[0].(sqlparser.ValTuple) if len(ins.Columns) != len(row) { plan.Reason = "column list doesn't match values" return plan } colVindexes := schema.Tables[tablename].ColVindexes plan.ID = InsertSharded plan.Values = make([]interface{}, 0, len(colVindexes)) for _, index := range colVindexes { if err := buildIndexPlan(ins, tablename, index, plan); err != nil { plan.ID = NoPlan plan.Reason = err.Error() return plan } } plan.Rewritten = generateQuery(ins) return plan }
// TODO(sougou): Copied from tabletserver. Reuse. func analyzeFrom(tableExprs sqlparser.TableExprs) (tablename string, hasHints bool) { if len(tableExprs) > 1 { return "", false } node, ok := tableExprs[0].(*sqlparser.AliasedTableExpr) if !ok { return "", false } return sqlparser.GetTableName(node.Expr), node.Hints != nil }
func analyzeUpdate(upd *sqlparser.Update, getTable TableGetter) (plan *ExecPlan, err error) { plan = &ExecPlan{ PlanId: PLAN_PASS_DML, FullQuery: GenerateFullQuery(upd), } tableName := sqlparser.GetTableName(upd.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 } plan.SecondaryPKValues, err = analyzeUpdateExpressions(upd.Exprs, tableInfo.Indexes[0]) if err != nil { if err == TooComplex { plan.Reason = REASON_PK_CHANGE return plan, nil } return nil, err } plan.OuterQuery = GenerateUpdateOuterQuery(upd) if conditions := analyzeWhere(upd.Where); conditions != nil { pkValues, err := getPKValues(conditions, tableInfo.Indexes[0]) if err != nil { return nil, err } if pkValues != nil { plan.PlanId = PLAN_DML_PK plan.PKValues = pkValues return plan, nil } } plan.PlanId = PLAN_DML_SUBQUERY plan.Subquery = GenerateUpdateSubquery(upd, tableInfo) return plan, nil }
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 }