Example #1
0
//-----------------------------------------------
// 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
}
Example #2
0
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
}