Esempio n. 1
0
// RenameTable renames a table. Old path and new path have the form
// "<namespace>.<table>".
func (db *DB) RenameTable(oldPath, newPath string) error {
	// TODO(pmattis): Should we allow both the old and new name to exist
	// simultaneously for a period of time? The thought is to allow an
	// application to access the table via either name while the application is
	// being upgraded. Alternatively, instead of a rename table operation perhaps
	// there should be a link table operation which adds a "hard link" to the
	// table. Similar to a file, a table would not be removed until all of the
	// hard links are removed.

	oldNSID, oldName, err := db.lookupTable(oldPath)
	if err != nil {
		return err
	}
	newNSID, newName, err := db.lookupTable(newPath)
	if err != nil {
		return err
	}
	if newName == "" {
		return fmt.Errorf("empty table name: %s", newPath)
	}

	return db.Txn(func(txn *Txn) error {
		oldNameKey := keys.MakeNameMetadataKey(oldNSID, oldName)
		gr, err := txn.Get(oldNameKey)
		if err != nil {
			return err
		}
		if !gr.Exists() {
			return fmt.Errorf("unable to find table \"%s\"", oldPath)
		}
		descKey := gr.ValueBytes()
		desc := structured.TableDescriptor{}
		if err := txn.GetProto(descKey, &desc); err != nil {
			return err
		}
		desc.Name = strings.ToLower(newPath)
		if err := structured.ValidateTableDesc(desc); err != nil {
			return err
		}
		newNameKey := keys.MakeNameMetadataKey(newNSID, newName)
		b := &Batch{}
		b.Put(descKey, &desc)
		// If the new name already exists the conditional put will fail causing the
		// transaction to fail.
		b.CPut(newNameKey, descKey, nil)
		b.Del(oldNameKey)
		return txn.Commit(b)
	})
}
Esempio n. 2
0
// CreateTable creates a table from the specified schema. Table creation will
// fail if the table name is already in use. The table name is required to have
// the form "<namespace>.<table>".
func (db *DB) CreateTable(desc *structured.TableDescriptor) error {
	desc.Name = strings.ToLower(desc.Name)
	if err := desc.AllocateIDs(); err != nil {
		return err
	}

	nsID, name, err := db.lookupTable(desc.Name)
	if err != nil {
		return err
	}
	if name == "" {
		return fmt.Errorf("empty table name: %s", desc.Name)
	}
	nameKey := keys.MakeNameMetadataKey(nsID, name)

	// This isn't strictly necessary as the conditional put below will fail if
	// the key already exists, but it seems good to avoid the table ID allocation
	// in most cases when the table already exists.
	if gr, err := db.Get(nameKey); err != nil {
		return err
	} else if gr.Exists() {
		return fmt.Errorf("table \"%s\" already exists", desc.Name)
	}

	ir, err := db.Inc(keys.DescIDGenerator, 1)
	if err != nil {
		return err
	}
	desc.ID = uint32(ir.ValueInt() - 1)

	// TODO(pmattis): Be cognizant of error messages when this is ported to the
	// server. The error currently returned below is likely going to be difficult
	// to interpret.
	return db.Txn(func(txn *Txn) error {
		descKey := keys.MakeDescMetadataKey(desc.ID)
		b := &Batch{}
		b.CPut(nameKey, descKey, nil)
		b.Put(descKey, desc)
		return txn.Commit(b)
	})
}
Esempio n. 3
0
func makeTableDesc(p *parser.CreateTable) (structured.TableDescriptor, error) {
	desc := structured.TableDescriptor{}
	desc.Name = p.Table.String()

	for _, def := range p.Defs {
		switch d := def.(type) {
		case *parser.ColumnTableDef:
			col := structured.ColumnDescriptor{
				Name:     d.Name,
				Nullable: (d.Nullable != parser.NotNull),
			}
			switch t := d.Type.(type) {
			case *parser.BitType:
				col.Type.Kind = structured.ColumnType_BIT
				col.Type.Width = int32(t.N)
			case *parser.IntType:
				col.Type.Kind = structured.ColumnType_INT
				col.Type.Width = int32(t.N)
			case *parser.FloatType:
				col.Type.Kind = structured.ColumnType_FLOAT
				col.Type.Precision = int32(t.Prec)
			case *parser.DecimalType:
				col.Type.Kind = structured.ColumnType_DECIMAL
				col.Type.Width = int32(t.Scale)
				col.Type.Precision = int32(t.Prec)
			case *parser.DateType:
				col.Type.Kind = structured.ColumnType_DATE
			case *parser.TimeType:
				col.Type.Kind = structured.ColumnType_TIME
			case *parser.TimestampType:
				col.Type.Kind = structured.ColumnType_TIMESTAMP
			case *parser.CharType:
				col.Type.Kind = structured.ColumnType_CHAR
				col.Type.Width = int32(t.N)
			case *parser.TextType:
				col.Type.Kind = structured.ColumnType_TEXT
			case *parser.BlobType:
				col.Type.Kind = structured.ColumnType_BLOB
			}
			desc.Columns = append(desc.Columns, col)

			// Create any associated index.
			if d.PrimaryKey || d.Unique {
				index := structured.IndexDescriptor{
					Unique:      true,
					ColumnNames: []string{d.Name},
				}
				if d.PrimaryKey {
					index.Name = "primary"
				}
				desc.Indexes = append(desc.Indexes, index)
			}
		case *parser.IndexTableDef:
			index := structured.IndexDescriptor{
				Name:        d.Name,
				Unique:      d.Unique,
				ColumnNames: d.Columns,
			}
			desc.Indexes = append(desc.Indexes, index)
		default:
			return desc, fmt.Errorf("unsupported table def: %T", def)
		}
	}
	return desc, nil
}
Esempio n. 4
0
func makeTableDesc(p *parser.CreateTable) (structured.TableDescriptor, error) {
	desc := structured.TableDescriptor{}
	desc.Name = p.Table.Table()
	for _, def := range p.Defs {
		switch d := def.(type) {
		case *parser.ColumnTableDef:
			col := structured.ColumnDescriptor{
				Name:     string(d.Name),
				Nullable: (d.Nullable != parser.NotNull),
			}
			switch t := d.Type.(type) {
			case *parser.BitType:
				col.Type.Kind = structured.ColumnType_BIT
				col.Type.Width = int32(t.N)
			case *parser.BoolType:
				col.Type.Kind = structured.ColumnType_BOOL
			case *parser.IntType:
				col.Type.Kind = structured.ColumnType_INT
				col.Type.Width = int32(t.N)
			case *parser.FloatType:
				col.Type.Kind = structured.ColumnType_FLOAT
				col.Type.Precision = int32(t.Prec)
			case *parser.DecimalType:
				col.Type.Kind = structured.ColumnType_DECIMAL
				col.Type.Width = int32(t.Scale)
				col.Type.Precision = int32(t.Prec)
			case *parser.DateType:
				col.Type.Kind = structured.ColumnType_DATE
			case *parser.TimeType:
				col.Type.Kind = structured.ColumnType_TIME
			case *parser.TimestampType:
				col.Type.Kind = structured.ColumnType_TIMESTAMP
			case *parser.CharType:
				col.Type.Kind = structured.ColumnType_CHAR
				col.Type.Width = int32(t.N)
			case *parser.TextType:
				col.Type.Kind = structured.ColumnType_TEXT
			case *parser.BlobType:
				col.Type.Kind = structured.ColumnType_BLOB
			default:
				panic(fmt.Sprintf("unexpected type %T", t))
			}
			desc.Columns = append(desc.Columns, col)

			// Create any associated index.
			if d.PrimaryKey || d.Unique {
				index := structured.IndexDescriptor{
					Unique:      true,
					ColumnNames: []string{string(d.Name)},
				}
				if d.PrimaryKey {
					index.Name = structured.PrimaryKeyIndexName
					desc.PrimaryIndex = index
				} else {
					desc.Indexes = append(desc.Indexes, index)
				}
			}
		case *parser.IndexTableDef:
			index := structured.IndexDescriptor{
				Name:        string(d.Name),
				Unique:      d.Unique,
				ColumnNames: d.Columns,
			}
			if d.PrimaryKey {
				// Only override the index name if it hasn't been set by the user.
				if index.Name == "" {
					index.Name = structured.PrimaryKeyIndexName
				}
				desc.PrimaryIndex = index
			} else {
				desc.Indexes = append(desc.Indexes, index)
			}
		default:
			return desc, fmt.Errorf("unsupported table def: %T", def)
		}
	}
	return desc, nil
}
Esempio n. 5
0
// SchemaFromModel allows the easy construction of a TableDescriptor from a Go
// struct. Columns are created for each exported field in the struct. The "db"
// struct tag is used to control the mapping of field name to column name and
// to indicate exported fields which should be skipped.
//
//   type User struct {
//     ID      int
//     Name    string `db:"old_name"`
//     Ignored int    `db:"-"`
//   }
//
// Indexes are specified using the "roach" struct tag declaration.
//
//   type User struct {
//     ID   int    `roach:"primary key"`
//     Name string `db:"old_name" roach:"index"`
//   }
//
// The following "roach" options are supported:
//
//   "primary key [(columns...)]" - creates a unique index on <columns> and
//   marks it as the primary key for the table. If <columns> is not specified
//   it defaults to the name of the column the option is associated with.
//
//   "index" [(columns...)]" - creates an index on <columns>.
//
//   "unique index" [(columns...)]" - creates a unique index on <columns>.
func SchemaFromModel(obj interface{}) (structured.TableDescriptor, error) {
	desc := structured.TableDescriptor{}
	m, err := getDBFields(deref(reflect.TypeOf(obj)))
	if err != nil {
		return desc, err
	}

	desc.Name = strings.ToLower(reflect.TypeOf(obj).Name())

	// Create the columns for the table.
	for name, sf := range m {
		colType := structured.ColumnType{}

		// TODO(pmattis): The mapping from Go-type Kind to column-type Kind is
		// likely not complete or correct, but this is probably going away pretty
		// soon with the move to SQL.
		switch sf.Type.Kind() {
		case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
			reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
			reflect.Uint64, reflect.Uintptr:
			colType.Kind = structured.ColumnType_INT

		case reflect.Float32, reflect.Float64:
			colType.Kind = structured.ColumnType_FLOAT

		case reflect.String:
			colType.Kind = structured.ColumnType_TEXT
		}

		col := structured.ColumnDescriptor{
			Name: name,
			Type: colType,
		}
		desc.Columns = append(desc.Columns, col)
	}

	// Create the indexes for the table.
	for name, f := range m {
		tag := f.Tag.Get("roach")
		if tag == "" {
			continue
		}
		for _, opt := range strings.Split(tag, ";") {
			match := schemaOptRE.FindStringSubmatch(opt)
			if match == nil {
				return desc, fmt.Errorf("invalid schema option: %s", opt)
			}
			cmd := match[1]
			var params []string
			if len(match[2]) > 0 {
				params = strings.Split(match[2], ",")
			} else {
				params = []string{name}
			}
			var index structured.IndexDescriptor
			switch strings.ToLower(cmd) {
			case "primary key":
				index.Name = structured.PrimaryKeyIndexName
				index.Unique = true
			case "unique index":
				index.Name = strings.Join(params, ":")
				index.Unique = true
			case "index":
				index.Name = strings.Join(params, ":")
			}
			index.ColumnNames = params
			desc.Indexes = append(desc.Indexes, index)
		}
	}

	// Normalize the column and index order.
	sort.Sort(columnsByName(desc.Columns))
	sort.Sort(indexesByName(desc.Indexes))
	return desc, nil
}