コード例 #1
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Inserts a row to the bottom of the table.
func (table *Table) Insert(row map[string]string) int {
	// Seek to EOF
	_, err := table.DataFile.Seek(0, 2)
	if err == nil {
		// For the columns in their order
		for _, column := range table.ColumnsInOrder {
			value, exists := row[column.Name]
			if !exists {
				value = ""
			}
			// Keep writing the column value.
			status := table.Write(column, value)
			if status != st.OK {
				return status
			}
		}
		// Write a new-line character.
		_, err = table.DataFile.WriteString("\n")
		if err != nil {
			logg.Err("table", "Insert", err.Error())
			return st.CannotWriteTableDataFile
		}
	} else {
		logg.Err("table", "Insert", err.Error())
		return st.CannotSeekTableDataFile
	}
	return st.OK
}
コード例 #2
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Adds a new column.
func (table *Table) Add(name string, length int) int {
	_, exists := table.Columns[name]
	if exists {
		return st.ColumnAlreadyExists
	}
	if len(name) > constant.MaxColumnNameLength {
		return st.ColumnNameTooLong
	}
	if length <= 0 {
		return st.InvalidColumnLength
	}
	var numberOfRows int
	numberOfRows, status := table.NumberOfRows()
	if status == st.OK && numberOfRows > 0 {
		// Rebuild data file if there are already rows in the table.
		// (To leave space for the new column)
		status = table.RebuildDataFile(name, length)
		table.pushNewColumn(name, length)
	} else {
		newColumn := table.pushNewColumn(name, length)
		// Write definition of the new column into definition file.
		_, err := table.DefFile.Seek(0, 2)
		if err != nil {
			logg.Err("table", "Add", err.Error())
			return st.CannotSeekTableDefFile
		}
		_, err = table.DefFile.WriteString(column.ColumnToDef(newColumn))
		if err != nil {
			logg.Err("table", "Add", err.Error())
			return st.CannotWriteTableDefFile
		}
	}
	table.RowLength += length
	return st.OK
}
コード例 #3
0
ファイル: file.go プロジェクト: shyrobbiani/database
// Creates a file and writes the content into it.
func CreateAndWrite(filename, content string) int {
	file, err := os.Create(filename)
	defer file.Close()
	if err != nil {
		logg.Err("util", "CreateAndWrite", err)
		return st.CannotCreateFile
	}
	_, err = file.WriteString(content)
	if err != nil {
		logg.Err("util", "CreateAndWrite", err)
		return st.CannotCreateFile
	}
	return st.OK
}
コード例 #4
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Opens file handles.
func (table *Table) OpenFiles() int {
	var err error
	table.DefFile, err = os.OpenFile(table.DefFilePath, os.O_RDWR, constant.DataFilePerm)
	if err == nil {
		table.DataFile, err = os.OpenFile(table.DataFilePath, os.O_RDWR, constant.DataFilePerm)
		if err != nil {
			logg.Err("table", "OpenFiles", err.Error())
			return st.CannotOpenTableDataFile
		}
	} else {
		logg.Err("table", "OpenFiles", err.Error())
		return st.CannotOpenTableDefFile
	}
	return st.OK
}
コード例 #5
0
// Renames table files.
func Rename(path string, oldName string, newName string) int {
	for _, ext := range constant.TableFiles() {
		err := os.Rename(path+oldName+ext, path+newName+ext)
		if err != nil {
			logg.Err("tablefilemanager", "Rename", err)
			return st.CannotRenameTableFile
		}
	}
	for _, dir := range constant.TableDirs() {
		err := os.Rename(path+oldName+dir, path+newName+dir)
		if err != nil {
			logg.Err("tablefilemanager", "Rename", err)
			return st.CannotRenameTableDir
		}
	}
	return st.OK
}
コード例 #6
0
// Deletes table files
func Delete(path string, name string) int {
	for _, ext := range constant.TableFiles() {
		err := os.Remove(path + name + ext)
		if err != nil {
			logg.Err("tablefilemanager", "Delete", err)
			return st.CannotRemoveTableFile
		}
	}
	for _, dir := range constant.TableDirs() {
		err := os.RemoveAll(path + name + dir)
		if err != nil {
			logg.Err("tablefilemanager", "Delete", err)
			return st.CannotRemoveTableDir
		}
	}
	return st.OK
}
コード例 #7
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Returns the number of rows in this table.
func (table *Table) NumberOfRows() (int, int) {
	// var numberOfRows int
	// var dataFileInfo *os.FileInfo
	dataFileInfo, err := table.DataFile.Stat()
	if err != nil {
		logg.Err("table", "NumberOfRows", err.Error())
		return 0, st.CannotStatTableDataFile
	}
	numberOfRows := int(dataFileInfo.Size()) / table.RowLength
	return numberOfRows, st.OK
}
コード例 #8
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Seeks to a row (e.g. row number 10).
func (table *Table) Seek(rowNumber int) int {
	var numberOfRows int
	numberOfRows, status := table.NumberOfRows()
	if status == st.OK && rowNumber < numberOfRows {
		_, err := table.DataFile.Seek(int64(rowNumber*table.RowLength), 0)
		if err != nil {
			logg.Err("table", "Seek", err.Error())
			return st.CannotSeekTableDataFile
		}
	}
	return st.OK
}
コード例 #9
0
ファイル: column.go プロジェクト: shyrobbiani/database
// Constructs a Column from a column's text definition.
func ColumnFromDef(offset int, definition string) (*Column, int) {
	var column *Column
	// Extract length and name from the definition.
	lengthName := strings.Split(definition, ":")
	length, err := strconv.Atoi(lengthName[1])
	if err != nil {
		logg.Err("Column", "ColumnFromDef", "Definition malformed: "+definition)
		return nil, st.InvalidColumnDefinition
	}
	column = &Column{Offset: offset, Length: length, Name: lengthName[0]}
	return column, st.OK
}
コード例 #10
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Opens a table.
func Open(path, name string) (*Table, int) {
	var table *Table
	table = new(Table)
	table.Path = path
	table.Name = name
	status := table.Init()
	if status != st.OK {
		logg.Err("table", "Open", "Failed to open"+path+name+" Err: "+string(status))
		return nil, status
	}
	return table, st.OK
}
コード例 #11
0
// Creates table files.
func Create(path string, name string) int {
	if len(name) > constant.MaxTableNameLength {
		return st.TableNameTooLong
	}
	// Create table files with extension names.
	for _, ext := range constant.TableFiles() {
		_, err := os.Create(path + name + ext)
		if err != nil {
			logg.Err("tablefilemanager", "Create", err)
			return st.CannotCreateTableFile
		}
	}
	// Create table directories with name suffixes.
	for _, dir := range constant.TableDirs() {
		err := os.Mkdir(path+name+dir, constant.TableDirPerm)
		if err != nil {
			logg.Err("tablefilemanager", "Create", err)
			return st.CannotCreateTableDir
		}
	}
	return st.OK
}
コード例 #12
0
ファイル: database.go プロジェクト: shyrobbiani/database
// Opens a path as database.
func Open(path string) (*Database, int) {
	var db *Database
	db = new(Database)
	db.Tables = make(map[string]*table.Table)
	// Open and read content of the path (as a directory).
	directory, err := os.Open(path)
	if err != nil {
		db = nil
		logg.Err("database", "Open", err.Error())
		return db, st.CannotOpenDatabaseDirectory
	}
	defer directory.Close()
	fi, err := directory.Readdir(0)
	if err != nil {
		db = nil
		logg.Err("database", "Open", err.Error())
		return db, st.CannotReadDatabaseDirectory
	}
	for _, fileInfo := range fi {
		// Extract extension of file name.
		if !fileInfo.IsDir() {
			name, ext := util.FilenameParts(fileInfo.Name())
			// If extension is .data, open the file as a Table.
			if ext == "data" {
				_, exists := db.Tables[name]
				if !exists {
					var status int
					// Open the table and put it into tables map.
					db.Tables[name], status = table.Open(path, name)
					if status != st.OK {
						return nil, status
					}
				}
			}
		}
	}
	db.Path = path
	return db, db.PrepareForTriggers(false)
}
コード例 #13
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Flushes table's files
func (table *Table) Flush() int {
	err := table.DefFile.Sync()
	if err == nil {
		err = table.DataFile.Sync()
		if err != nil {
			logg.Err("table", "Flush", err.Error())
			return st.CannotFlushTableDataFile
		}
	} else {
		return st.CannotFlushTableDefFile
	}
	return st.OK
}
コード例 #14
0
ファイル: file.go プロジェクト: shyrobbiani/database
// Removes a line's occurances from a file.
func RemoveLine(filename, line string) int {
	// Open and read the file.
	file, err := os.Open(filename)
	if err != nil {
		logg.Err("util", "RemoveLine", err)
		return st.CannotReadFile
	}
	fi, err := file.Stat()
	if err != nil {
		logg.Err("util", "RemoveLine", err)
		return st.CannotReadFile
	}
	buffer := make([]byte, fi.Size())
	_, err = file.Read(buffer)
	if err != nil {
		logg.Err("util", "RemoveLine", err)
		return st.CannotReadFile
	}
	file.Close()
	// Re-open the file and overwrite it.
	file, err = os.OpenFile(filename, os.O_WRONLY+os.O_TRUNC, 0666)
	defer file.Close()
	if err != nil {
		logg.Err("util", "RemoveLine", err)
		return st.CannotReadFile
	}
	lines := strings.Split(string(buffer), "\n")
	for _, content := range lines {
		if strings.TrimSpace(content) != strings.TrimSpace(line) {
			_, err = file.WriteString(content + "\n")
			if err != nil {
				logg.Err("util", "RemoveLine", err)
				return st.CannotWriteFile
			}
		}
	}
	return st.OK
}
コード例 #15
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Seeks to a row and column (e.g. row number 10 column "NAME").
func (table *Table) SeekColumn(rowNumber int, columnName string) int {
	status := table.Seek(rowNumber)
	if status == st.OK {
		column, exists := table.Columns[columnName]
		if exists {
			_, err := table.DataFile.Seek(int64(column.Offset), 1)
			if err != nil {
				logg.Err("table", "SeekColumn", err.Error())
				return st.CannotSeekTableDataFile
			}
		}
	}
	return st.OK
}
コード例 #16
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Reads a row and return a map representation (name1:value1, name2:value2...)
func (table *Table) Read(rowNumber int) (map[string]string, int) {
	row := make(map[string]string)
	status := table.Seek(rowNumber)
	if status == st.OK {
		rowInBytes := make([]byte, table.RowLength)
		_, err := table.DataFile.Read(rowInBytes)
		if err == nil {
			// For the columns in their order
			for _, column := range table.ColumnsInOrder {
				// column1:value2, column2:value2...
				row[column.Name] = strings.TrimSpace(string(rowInBytes[column.Offset : column.Offset+column.Length]))
			}
		} else {
			logg.Err("table", "Read", err.Error())
			return nil, st.CannotReadTableDataFile
		}
	}
	return row, st.OK
}
コード例 #17
0
ファイル: table.go プロジェクト: shyrobbiani/database
// Load the table (column definitions, etc.).
func (table *Table) Init() int {
	// This function may be called multiple times, thus clear previous state.
	table.RowLength = 0
	table.Columns = make(map[string]*column.Column)
	table.ColumnsInOrder = make([]*column.Column, 0)
	table.DefFilePath = table.Path + table.Name + ".def"
	table.DataFilePath = table.Path + table.Name + ".data"
	status := table.OpenFiles()
	if status != st.OK {
		return status
	}
	defFileInfo, err := table.DefFile.Stat()
	if err != nil {
		logg.Err("table", "Init", err.Error())
		return st.CannotStatTableDefFile
	}
	// Read definition file into memeory.
	content := make([]byte, defFileInfo.Size())
	table.DefFile.Read(content)
	// Each line contains one column definition.
	lines := strings.Split(string(content), "\n")
	for _, line := range lines {
		if line != "" {
			var aColumn *column.Column
			// Convert the definition into a Column.
			aColumn, status = column.ColumnFromDef(table.RowLength, line)
			if status != st.OK {
				return status
			}
			table.Columns[aColumn.Name] = aColumn
			table.ColumnsInOrder = append(table.ColumnsInOrder[:], aColumn)
			table.RowLength += aColumn.Length
		}
	}
	table.RowLength++
	return st.OK
}
コード例 #18
0
ファイル: locking.go プロジェクト: shyrobbiani/database
// Returns existing shared and exclusive locks of a table.
func LocksOf(t *table.Table) (*Locks, int) {
	// Read files in .shared directory.
	sharedLocksPath := t.Path + t.Name + ".shared"
	sharedLocksDir, err := os.Open(sharedLocksPath)
	if err != nil {
		return nil, st.CannotReadSharedLocksDir
	}
	defer sharedLocksDir.Close()
	fi, err := sharedLocksDir.Readdir(0)
	if err != nil {
		logg.Err("transaction", "LocksOf", err)
		return nil, st.CannotReadSharedLocksDir
	}
	locks := new(Locks)
	locks.Shared = make([]int64, 0)
	for _, fileInfo := range fi {
		// File name represents a transaction ID (also a timestamp).
		theID, err := strconv.ParseInt(fileInfo.Name(), 10, 64)
		// tt := time.Now().Nanosecond() + constant.LockTimeout
		if err != nil || theID > int64(time.Now().Nanosecond()+constant.LockTimeout) {
			// Remove expired shared lock.
			err = os.Remove(sharedLocksPath + "/" + fileInfo.Name())
			logg.Warn("transaction", "LocksOf", "Expired shared lock ID "+
				fileInfo.Name()+" file "+sharedLocksPath+"/"+fileInfo.Name()+" is removed")
			if err != nil {
				logg.Err("transaction", "LocksOf", err)
				return nil, st.CannotUnlockSharedLock
			}
		} else {
			locks.Shared = append(locks.Shared[:], theID)
		}
	}
	// Read the content of exclusive lock.
	exclusiveLockPath := t.Path + t.Name + ".exclusive"
	exclusiveFile, err := os.Open(exclusiveLockPath)
	if err != nil {
		return locks, st.OK
	}
	fi2, err := exclusiveFile.Stat()
	if err != nil {
		logg.Err("transaction", "LocksOf", err)
		return nil, st.CannotReadExclusiveLocksFile
	}
	// The file content is a transaction ID
	buffer := make([]byte, fi2.Size())
	_, err = exclusiveFile.Read(buffer)
	if err != nil {
		logg.Err("transaction", "LocksOf", err)
		return nil, st.CannotReadExclusiveLocksFile
	}
	theID, err := strconv.ParseInt(string(buffer), 10, 64)

	if err != nil || theID > int64(time.Now().Nanosecond()+constant.LockTimeout) {
		// Remove expired exclusive lock.
		err = os.Remove(exclusiveLockPath)
		logg.Debug("transaction", "LocksOf", err)
		logg.Warn("transaction", "LocksOf", "Expired exclusive lock ID "+
			string(buffer)+" file "+exclusiveLockPath+" is removed")
		if err != nil {
			logg.Err("transaction", "LocksOf", err)
			return nil, st.CannotUnlockExclusiveLock
		}
	} else {
		locks.Exclusive = theID
	}
	return locks, st.OK
}