//----------------------------------------------- // Execution func (qe *QueryEngine) execInsertPK(logStats *sqlQueryStats, conn PoolConnection, plan *CompiledPlan) (qr *eproto.QueryResult) { log.Info("Execute insert pk sql %s", plan.Query) tableName := plan.TableName tableInfo := qe.schemaInfo.tables[plan.TableName] rowColumns := plan.RowColumns var pkValue []byte var key []byte var columnName string keys := make([][]byte, 0, len(rowColumns)*len(tableInfo.Columns)) values := make([][]byte, 0, len(rowColumns)*len(tableInfo.Columns)) pkList := buildPkValueList(tableInfo, plan.PKValues, plan.BindVars) for i, columnsMap := range rowColumns { if len(tableInfo.PKColumns) > 1 { // multiple pk pkValue = buildCompositeValue(pkList[i]) } else { pkColumn := tableInfo.GetPKColumn(0) if pkColumn.IsAuto { if plan.PKValues != nil { panic(NewTabletErrorDB(FAIL, fmt.Errorf("field %s value is auto created", columnName))) } pkValue = []byte(strconv.FormatInt(pkColumn.GetNextId(), 64)) } else if pkColumn.IsUUID { uid, err := uuid.NewV4() if err != nil { panic(NewTabletError(FATAL, "Make uuuid error")) } pkValue = []byte(uid.String()) } else { pkValue = pkList[i][0].Raw() // single pk } } log.Info("Pk Value is %v", string(pkValue)) for _, columnDef := range tableInfo.Columns { columnName = columnDef.Name if columnDef.IsPk { value := columnsMap[columnName] key = buildTableRowColumnKey(tableName, columnName, pkValue) keys = append(keys, key) values = append(values, value.Raw()) // if column is auto increment, update the value if columnDef.IsAuto { keys = append(keys, buildTableColumnAutoKey(tableName, columnName)) values = append(values, pkValue) } } else if columnDef.IsAuto { if _, ok := columnsMap[columnName]; ok { panic(NewTabletErrorDB(FAIL, fmt.Errorf("field %s value is auto created", columnName))) } keys = append(keys, buildTableRowColumnKey(tableName, columnName, pkValue)) nextId := []byte(strconv.FormatInt(columnDef.GetNextId(), 64)) values = append(values, nextId) keys = append(keys, buildTableColumnAutoKey(tableName, columnName)) values = append(values, nextId) } else if columnDef.IsUUID { uid, err := uuid.NewV4() if err != nil { panic(NewTabletError(FATAL, "Make uuuid error")) } keys = append(keys, buildTableRowColumnKey(tableName, columnName, pkValue)) values = append(values, []byte(uid.String())) } else { value, ok := columnsMap[columnName] if !ok { if !columnDef.Nullable { panic(NewTabletErrorDB(FAIL, fmt.Errorf("column %s shouldn't be null", columnDef.Name))) } } if !value.IsNull() { key = buildTableRowColumnKey(tableName, columnName, pkValue) keys = append(keys, key) values = append(values, value.Raw()) } } } // secondary index for _, index := range tableInfo.Indexes { if index.Name == "PRIMARY" { continue } columnValues := make([]sqltypes.Value, len(index.Columns)) for i, columnName := range index.Columns { columnValues[i] = columnsMap[columnName] } indexKey := buildSecondaryIndexKey(tableName, index.Name, buildCompositeValue(columnValues)) log.Trace("idx key is %s", indexKey) keys = append(keys, indexKey) values = append(values, pkValue) } } atomic.AddInt64(&qe.activeConnection, 1) defer atomic.AddInt64(&qe.activeConnection, -1) err := conn.Puts(nil, keys, values) if err != nil { panic(NewTabletErrorDB(FAIL, err)) } qr = &eproto.QueryResult{RowsAffected: uint64(len(rowColumns))} return qr }
func (node *Node) execAnalyzeSelect(getTable TableGetter) (plan *ExecPlan) { // Default plan plan = &ExecPlan{PlanId: PLAN_PASS_SELECT, FieldQuery: node.GenerateFieldQuery(), FullQuery: node.GenerateSelectLimitQuery()} // There are bind variables in the SELECT list if plan.FieldQuery == nil { plan.Reason = REASON_SELECT_LIST return plan } if !node.execAnalyzeSelectStructure() { plan.Reason = REASON_SELECT return plan } // from tableName, hasHints := node.At(SELECT_FROM_OFFSET).execAnalyzeFrom() if tableName == "" { plan.Reason = REASON_TABLE return plan } tableInfo := plan.setTableInfo(tableName, getTable) log.Info("hashints:%#v", hasHints) // Don't improve the plan if the select is for update if node.At(SELECT_FOR_UPDATE_OFFSET).Type == FOR_UPDATE { plan.Reason = REASON_FOR_UPDATE return plan } // Select expressions selects := node.At(SELECT_EXPR_OFFSET).execAnalyzeSelectExpressions(tableInfo) if selects == nil { plan.Reason = REASON_SELECT_LIST return plan } plan.ColumnNumbers = selects log.Info("select expre:%#v", selects) // where conditions := node.At(SELECT_WHERE_OFFSET).execAnalyzeWhere() if conditions == nil { plan.PlanId = PLAN_SELECT_ALL plan.Reason = REASON_WHERE return plan } for _, node := range conditions { log.Trace("%v", node) } // order if node.At(SELECT_ORDER_OFFSET).Len() != 0 { plan.Reason = REASON_ORDER return plan } // This check should never fail because we only cache tables with primary keys. if len(tableInfo.Indexes) == 0 || tableInfo.Indexes[0].Name != "PRIMARY" { panic("unexpected") } // Attempt PK match only if there's no limit clause if node.At(SELECT_LIMIT_OFFSET).Len() == 0 { planId, pkValues := getSelectPKValues(conditions, tableInfo.Indexes[0]) switch planId { case PLAN_PK_EQUAL: plan.PlanId = PLAN_PK_EQUAL plan.OuterQuery = node.GenerateEqualOuterQuery(tableInfo) plan.PKValues = pkValues return plan case PLAN_PK_IN: plan.PlanId = PLAN_PK_IN plan.OuterQuery = node.GenerateInOuterQuery(tableInfo) plan.PKValues = pkValues return plan } } if len(tableInfo.Indexes[0].Columns) != 1 { plan.Reason = REASON_COMPOSITE_PK return plan } // TODO: Analyze hints to improve plan. if hasHints { plan.Reason = REASON_HAS_HINTS return plan } plan.IndexUsed = getIndexMatch(conditions, tableInfo.Indexes) if plan.IndexUsed == "" { panic(NewParserError("no index matches the sql")) } if plan.IndexUsed == "PRIMARY" { plan.Reason = REASON_PKINDEX if len(conditions) != 1 { plan.Conditions = conditions } return plan } // TODO: We can further optimize. Change this to pass-through if select list matches all columns in index. plan.PlanId = PLAN_SELECT_SUBQUERY plan.Conditions = conditions plan.OuterQuery = node.GenerateInOuterQuery(tableInfo) plan.Subquery = node.GenerateSelectSubquery(tableInfo, plan.IndexUsed) return plan }