예제 #1
0
파일: database.go 프로젝트: postfix/DBGo
// Creates a new table.
func (db *Database) Create(name string) (*table.Table, int) {
	var newTable *table.Table
	_, exists := db.Tables[name]
	if exists {
		return nil, st.TableAlreadyExists
	}
	if len(name) > constant.MaxTableNameLength {
		return nil, st.TableNameTooLong
	}
	// Create table files and directories.
	tablefilemanager.Create(db.Path, name)
	// Open the table
	var status int
	newTable, status = table.Open(db.Path, name)
	if status == st.OK {
		// Add default columns
		for columnName, length := range constant.DatabaseColumns() {
			status = newTable.Add(columnName, length)
			if status != st.OK {
				return nil, status
			}
		}
		db.Tables[name] = newTable
	}
	return newTable, st.OK
}
예제 #2
0
파일: result.go 프로젝트: postfix/DBGo
// Load all rows of a table into RA result.
func (r *Result) Load(t *table.Table) (*Result, int) {
	_, exists := r.Tables[t.Name]
	if exists {
		return r, st.TableAlreadyExists
	}
	// rowNumbers = list(range(t.NumberOfRows()))
	rowNumbers := make([]int, 0)
	numberOfRows, status := t.NumberOfRows()
	if status != st.OK {
		return r, status
	}
	for i := 0; i < numberOfRows; i++ {
		rowNumbers = append(rowNumbers[:], i)
	}
	r.Tables[t.Name] = &TableResult{t, rowNumbers}
	// Load columns of the table.
	for columnName, _ := range t.Columns {
		if !strings.HasPrefix(columnName, constant.ThePrefix) {
			_, exists := r.Aliases[columnName]
			if exists {
				logg.Warn("ra", "Load", "Column name "+columnName+" duplicates an existing alias")
			}
			r.Aliases[columnName] = &TableColumn{t.Name, columnName}
		}
	}
	return r, st.OK
}
예제 #3
0
// Deletes rows in a table of RA result according to some select conditions.
// The RA result is made a copy before using select conditions.
func findAndDelete(t *table.Table, query *ra.Result, conditions ...ra.Condition) int {
	_, status := query.Copy().MultipleSelect(conditions...)
	if status != st.OK {
		return status
	}
	for _, i := range query.Tables[t.Name].RowNumbers {
		status = t.Delete(i)
		if status != st.OK {
			return status
		}
	}
	return t.Flush()
}
예제 #4
0
파일: delete.go 프로젝트: postfix/DBGo
func (tr *Transaction) Delete(t *table.Table, rowNumber int) int {
	// Execute "before delete" triggers.
	beforeTable, status := tr.DB.Get("~before")
	if status != st.OK {
		return status
	}
	row, status := t.Read(rowNumber)
	if status != st.OK {
		return status
	}
	triggerRA := ra.New()
	_, status = triggerRA.Load(beforeTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "DE", row, nil)
	if status != st.OK {
		return status
	}
	// Update the row.
	status = t.Delete(rowNumber)
	if status != st.OK {
		return status
	}
	// Execute "after delete" triggers.
	afterTable, status := tr.DB.Get("~after")
	if status != st.OK {
		return status
	}
	triggerRA = ra.New()
	_, status = triggerRA.Load(afterTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "DE", row, nil)
	if status != st.OK {
		return status
	}
	// Log the deleted row.
	tr.Log(&UndoDelete{t, rowNumber})
	return st.OK
}
예제 #5
0
func (tr *Transaction) Update(t *table.Table, rowNumber int, row map[string]string) int {
	// Execute "before update" triggers.
	beforeTable, status := tr.DB.Get("~before")
	if status != st.OK {
		return status
	}
	original, status := t.Read(rowNumber)
	if status != st.OK {
		return status
	}
	triggerRA := ra.New()
	_, status = triggerRA.Load(beforeTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "UP", row, original)
	if status != st.OK {
		return status
	}
	// Update the row.
	status = t.Update(rowNumber, row)
	if status != st.OK {
		return status
	}
	// Execute "after update" triggers.
	afterTable, status := tr.DB.Get("~after")
	if status != st.OK {
		return status
	}
	triggerRA = ra.New()
	_, status = triggerRA.Load(afterTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "UP", row, original)
	if status != st.OK {
		return status
	}
	// Log the updated row.
	tr.Log(&UndoUpdate{t, rowNumber, original})
	return st.OK
}
예제 #6
0
파일: insert.go 프로젝트: postfix/DBGo
func (tr *Transaction) Insert(t *table.Table, row map[string]string) int {
	// Execute "before insert" triggers.
	beforeTable, status := tr.DB.Get("~before")
	if status != st.OK {
		return status
	}
	triggerRA := ra.New()
	_, status = triggerRA.Load(beforeTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "IN", row, nil)
	if status != st.OK {
		return status
	}
	// Insert the new row to table.
	numberOfRows, status := t.NumberOfRows()
	if status != st.OK {
		return status
	}
	status = t.Insert(row)
	if status != st.OK {
		return status
	}
	// Execute "after insert" triggers.
	afterTable, status := tr.DB.Get("~after")
	if status != st.OK {
		return status
	}
	triggerRA = ra.New()
	_, status = triggerRA.Load(afterTable)
	if status != st.OK {
		return status
	}
	_, status = triggerRA.Select("TABLE", filter.Eq{}, t.Name)
	if status != st.OK {
		return status
	}
	status = trigger.ExecuteTrigger(tr.DB, t, triggerRA, "IN", row, nil)
	if status != st.OK {
		return status
	}
	// Log the inserted row.
	tr.Log(&UndoInsert{t, numberOfRows})
	return st.OK
}
예제 #7
0
파일: constraint.go 프로젝트: postfix/DBGo
// Look for a value in a table's column, returns true if the value is found.
func find(column, value string, t *table.Table) (bool, int) {
	numberOfRows, status := t.NumberOfRows()
	if status != st.OK {
		return false, status
	}
	for i := 0; i < numberOfRows; i++ {
		row, status := t.Read(i)
		if status != st.OK {
			return false, status
		}
		if row[column] == value {
			return true, st.OK
		}
	}
	return false, st.OK
}
예제 #8
0
파일: nl_join.go 프로젝트: postfix/DBGo
// Relational algebra join using nested loops.
func (r *Result) NLJoin(alias string, t2 *table.Table, name string) (*Result, int) {
	// t1 is the table in RA result.
	t1Column := r.Aliases[alias].ColumnName
	t1 := r.Tables[r.Aliases[alias].TableName]
	t2RowNumbers := make([]int, 0)
	// t2 is the external table.
	t2NumberOfRows, status := t2.NumberOfRows()
	if status != st.OK {
		return r, status
	}
	// Prepare to re-arrange the sequence of row numbers of all existing tables in RA result.
	newRowNumbers := make(map[string][]int)
	for name, _ := range r.Tables {
		newRowNumbers[name] = make([]int, 0)
	}
	// NL begins.
	for i, t1RowNumber := range t1.RowNumbers {
		for t2RowNumber := 0; t2RowNumber < t2NumberOfRows; t2RowNumber++ {
			t1Row, status := t1.Table.Read(t1RowNumber)
			if status != st.OK {
				return r, status
			}
			t2Row, status := t2.Read(t2RowNumber)
			if status != st.OK {
				return r, status
			}
			if t1Row["~del"] != "y" && t2Row["~del"] != "y" && t1Row[t1Column] == t2Row[name] {
				for name, _ := range newRowNumbers {
					newRowNumbers[name] = append(newRowNumbers[name][:], r.Tables[name].RowNumbers[i])
				}
				t2RowNumbers = append(t2RowNumbers[:], t2RowNumber)
			}
		}
	}
	// Re-arrange the sequence of row numbers of all existing tables in RA result.
	for name, rowNumbers := range newRowNumbers {
		r.Tables[name].RowNumbers = rowNumbers
	}
	// Load columns of t2 into RA result.
	r.Load(t2)
	t2Table := r.Tables[t2.Name]
	t2Table.RowNumbers = t2RowNumbers
	return r, st.OK
}
예제 #9
0
// Generate reports to output directory.
func (report *Report) Generate(outputDir string) error {
	var buf bytes.Buffer
	var filename string

	switch report.period {
	case ReportPeriodInfinite:
		fmt.Fprintf(&buf, "All transactions\n\n")
		filename = filepath.Join(outputDir, "all.txt")
	case ReportPeriodYearly:
		fmt.Fprintf(&buf, "%d\n\n", report.from.Year())
		filename = filepath.Join(outputDir, fmt.Sprintf("%d.txt", report.from.Year()))
	case ReportPeriodQuarterly:
		quarterName := fmt.Sprintf("Q%d", (report.from.Month()-1)/3+1)
		fmt.Fprintf(&buf, "%s %d\n\n", quarterName, report.from.Year())
		filename = filepath.Join(outputDir, fmt.Sprintf("%d-%s.txt", report.from.Year(), quarterName))
	case ReportPeriodMonthly:
		fmt.Fprintf(&buf, "%s %d\n\n", report.from.Month(), report.from.Year())
		filename = filepath.Join(outputDir, fmt.Sprintf("%d-%02d.txt", report.from.Year(), report.from.Month()))
	default:
		return fmt.Errorf("unsupported report period %d", report.period)
	}

	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	var accounts []*Account
	for _, account := range report.accounts {
		accounts = append(accounts, account)
	}
	sort.Sort(sortByAccountName(accounts))

	// Account summary
	t := new(table.Table)
	t.SetTitles(table.Row{
		{Content: "account"},
		{Content: "amount"},
		{Content: "cumulative"},
		{Content: "delta"},
	})

	for _, account := range accounts {
		cumulativeStr := balanceToString(account.CumulativeBalance())
		deltaStr := fmt.Sprintf("%+.2f", report.AccountDelta(account.name))

		if cumulativeStr != "-" || deltaStr != "+0.00" {
			t.AddRow(table.Row{
				{Content: account.name.Leaf(), PadLeft: uint(indentAmount * account.name.Depth())},
				{Content: balanceToString(account.FlatBalance()), Align: table.AlignRight},
				{Content: cumulativeStr, Align: table.AlignRight},
				{Content: deltaStr, Align: table.AlignRight},
			})
		}
	}
	buf.Write(t.RenderText())

	// Transaction log
	fmt.Fprintf(&buf, "\nTransactions\n\n")

	t = new(table.Table)
	t.SetTitles(table.Row{
		{Content: "date"},
		{Content: "account"},
		{Content: "debit"},
		{Content: "credit"},
	})

	var prevDate time.Time
	for _, tr := range report.transactions {
		var dateStr string
		if tr.date != prevDate {
			dateStr = tr.date.Format(transactionDateFormat)
		}
		prevDate = tr.date

		t.AddRow(table.Row{
			{Content: dateStr},
			{Content: string(tr.accounts[Dr])},
			{Content: fmt.Sprintf("%.2f", tr.amount), Align: table.AlignRight},
			{Content: ""},
		})
		t.AddRow(table.Row{
			{Content: ""},
			{Content: string(tr.accounts[Cr])},
			{Content: ""},
			{Content: fmt.Sprintf("%.2f", tr.amount), Align: table.AlignRight},
		})
	}
	buf.Write(t.RenderText())

	fmt.Fprintf(file, "%s", buf.Bytes())
	return nil
}