<commit_before>package main

import (
	"database/sql"
	"errors"
	"fmt"
	_ "github.com/bmizerany/pq"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
)

type MigrationRecord struct {
	VersionId int64
	TStamp    time.Time
	IsApplied bool // was this a result of up() or down()
}

type Migration struct {
	Next     int64  // next version, or -1 if none
	Previous int64  // previous version, -1 if none
	Source   string // .go or .sql script
}

type MigrationVersions []int64

// helpers so we can use pkg sort
func (s MigrationVersions) Len() int           { return len(s) }
func (s MigrationVersions) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s MigrationVersions) Less(i, j int) bool { return s[i] < s[j] }

type MigrationMap struct {
	Versions   MigrationVersions   // sorted slice of version keys
	Migrations map[int64]Migration // sources (.sql or .go) keyed by version
	Direction  bool                // sort direction: true -> Up, false -> Down
}

func runMigrations(conf *DBConf, migrationsDir string, target int64) {

	db, err := sql.Open(conf.Driver, conf.OpenStr)
	if err != nil {
		log.Fatal("couldn't open DB:", err)
	}
	defer db.Close()

	current, e := ensureDBVersion(db)
	if e != nil {
		log.Fatalf("couldn't get DB version: %v", e)
	}

	mm, err := collectMigrations(migrationsDir, current, target)
	if err != nil {
		log.Fatal(err)
	}

	if len(mm.Versions) == 0 {
		fmt.Printf("goose: no migrations to run. current version: %d\n", current)
		return
	}

	mm.Sort(current < target)

	fmt.Printf("goose: migrating db environment '%v', current version: %d, target: %d\n",
		conf.Env, current, target)

	for _, v := range mm.Versions {

		var numStatements int
		var e error

		filepath := mm.Migrations[v].Source

		switch path.Ext(filepath) {
		case ".go":
			numStatements, e = runGoMigration(conf, filepath, v, mm.Direction)
		case ".sql":
			numStatements, e = runSQLMigration(db, filepath, v, mm.Direction)
		}

		if e != nil {
			log.Fatalf("FAIL %v, quitting migration", e)
		}

		fmt.Printf("OK   %s (%d statements)\n", path.Base(filepath), numStatements)
	}
}

// collect all the valid looking migration scripts in the 
// migrations folder, and key them by version
func collectMigrations(dirpath string, current, target int64) (mm *MigrationMap, err error) {

	mm = &MigrationMap{
		Migrations: make(map[int64]Migration),
	}

	// extract the numeric component of each migration,
	// filter out any uninteresting files,
	// and ensure we only have one file per migration version.
	filepath.Walk(dirpath, func(name string, info os.FileInfo, err error) error {

		if v, e := numericComponent(name); e == nil {

			if _, ok := mm.Migrations[v]; ok {
				log.Fatalf("more than one file specifies the migration for version %d (%s and %s)",
					v, mm.Versions[v], path.Join(dirpath, name))
			}

			if versionFilter(v, current, target) {
				mm.Append(v, name)
			}
		}

		return nil
	})

	return mm, nil
}

func versionFilter(v, current, target int64) bool {

	// special case - default target value
	if target < 0 {
		return v > current
	}

	if target > current {
		return v > current && v <= target
	}

	if target < current {
		return v <= current && v > target
	}

	return false
}

func (m *MigrationMap) Append(v int64, source string) {
	m.Versions = append(m.Versions, v)
	m.Migrations[v] = Migration{
		Next:     -1,
		Previous: -1,
		Source:   source,
	}
}

func (m *MigrationMap) Sort(direction bool) {
	sort.Sort(m.Versions)

	// set direction, and reverse order if need be
	m.Direction = direction
	if m.Direction == false {
		for i, j := 0, len(m.Versions)-1; i < j; i, j = i+1, j-1 {
			m.Versions[i], m.Versions[j] = m.Versions[j], m.Versions[i]
		}
	}

	// now that we're sorted in the appropriate direction,
	// populate next and previous for each migration
	//
	// work around http://code.google.com/p/go/issues/detail?id=3117
	previousV := int64(-1)
	for _, v := range m.Versions {
		cur := m.Migrations[v]
		cur.Previous = previousV
		m.Migrations[v] = cur

		// if a migration exists at prev, its next is now v
		if prev, ok := m.Migrations[previousV]; ok {
			prev.Next = v
			m.Migrations[previousV] = prev
		}

		previousV = v
	}
}

// look for migration scripts with names in the form:
//  XXX_descriptivename.ext
// where XXX specifies the version number
// and ext specifies the type of migration
func numericComponent(name string) (int64, error) {

	base := path.Base(name)

	if ext := path.Ext(base); ext != ".go" && ext != ".sql" {
		return 0, errors.New("not a recognized migration file type")
	}

	idx := strings.Index(base, "_")
	if idx < 0 {
		return 0, errors.New("no separator found")
	}

	n, e := strconv.ParseInt(base[:idx], 10, 64)
	if e == nil && n == 0 {
		return 0, errors.New("0 is not a valid migration ID")
	}

	return n, e
}

// retrieve the current version for this DB.
// Create and initialize the DB version table if it doesn't exist.
func ensureDBVersion(db *sql.DB) (int64, error) {

	rows, err := db.Query("SELECT version_id, is_applied from goose_db_version ORDER BY tstamp DESC;")
	if err != nil {
		// XXX: cross platform method to detect failure reason
		// for now, assume it was because the table didn't exist, and try to create it
		return 0, createVersionTable(db)
	}

	// The most recent record for each migration specifies
	// whether it has been applied or rolled back.
	// The first version we find that has been applied is the current version.

	toSkip := make([]int64, 0)

	for rows.Next() {
		var row MigrationRecord<commit_msg>ensure migration IDs are greater than zero<commit_after>package main

import (
	"database/sql"
	"errors"
	"fmt"
	_ "github.com/bmizerany/pq"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
)

type MigrationRecord struct {
	VersionId int64
	TStamp    time.Time
	IsApplied bool // was this a result of up() or down()
}

type Migration struct {
	Next     int64  // next version, or -1 if none
	Previous int64  // previous version, -1 if none
	Source   string // .go or .sql script
}

type MigrationVersions []int64

// helpers so we can use pkg sort
func (s MigrationVersions) Len() int           { return len(s) }
func (s MigrationVersions) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s MigrationVersions) Less(i, j int) bool { return s[i] < s[j] }

type MigrationMap struct {
	Versions   MigrationVersions   // sorted slice of version keys
	Migrations map[int64]Migration // sources (.sql or .go) keyed by version
	Direction  bool                // sort direction: true -> Up, false -> Down
}

func runMigrations(conf *DBConf, migrationsDir string, target int64) {

	db, err := sql.Open(conf.Driver, conf.OpenStr)
	if err != nil {
		log.Fatal("couldn't open DB:", err)
	}
	defer db.Close()

	current, e := ensureDBVersion(db)
	if e != nil {
		log.Fatalf("couldn't get DB version: %v", e)
	}

	mm, err := collectMigrations(migrationsDir, current, target)
	if err != nil {
		log.Fatal(err)
	}

	if len(mm.Versions) == 0 {
		fmt.Printf("goose: no migrations to run. current version: %d\n", current)
		return
	}

	mm.Sort(current < target)

	fmt.Printf("goose: migrating db environment '%v', current version: %d, target: %d\n",
		conf.Env, current, target)

	for _, v := range mm.Versions {

		var numStatements int
		var e error

		filepath := mm.Migrations[v].Source

		switch path.Ext(filepath) {
		case ".go":
			numStatements, e = runGoMigration(conf, filepath, v, mm.Direction)
		case ".sql":
			numStatements, e = runSQLMigration(db, filepath, v, mm.Direction)
		}

		if e != nil {
			log.Fatalf("FAIL %v, quitting migration", e)
		}

		fmt.Printf("OK   %s (%d statements)\n", path.Base(filepath), numStatements)
	}
}

// collect all the valid looking migration scripts in the 
// migrations folder, and key them by version
func collectMigrations(dirpath string, current, target int64) (mm *MigrationMap, err error) {

	mm = &MigrationMap{
		Migrations: make(map[int64]Migration),
	}

	// extract the numeric component of each migration,
	// filter out any uninteresting files,
	// and ensure we only have one file per migration version.
	filepath.Walk(dirpath, func(name string, info os.FileInfo, err error) error {

		if v, e := numericComponent(name); e == nil {

			if _, ok := mm.Migrations[v]; ok {
				log.Fatalf("more than one file specifies the migration for version %d (%s and %s)",
					v, mm.Versions[v], path.Join(dirpath, name))
			}

			if versionFilter(v, current, target) {
				mm.Append(v, name)
			}
		}

		return nil
	})

	return mm, nil
}

func versionFilter(v, current, target int64) bool {

	// special case - default target value
	if target < 0 {
		return v > current
	}

	if target > current {
		return v > current && v <= target
	}

	if target < current {
		return v <= current && v > target
	}

	return false
}

func (m *MigrationMap) Append(v int64, source string) {
	m.Versions = append(m.Versions, v)
	m.Migrations[v] = Migration{
		Next:     -1,
		Previous: -1,
		Source:   source,
	}
}

func (m *MigrationMap) Sort(direction bool) {
	sort.Sort(m.Versions)

	// set direction, and reverse order if need be
	m.Direction = direction
	if m.Direction == false {
		for i, j := 0, len(m.Versions)-1; i < j; i, j = i+1, j-1 {
			m.Versions[i], m.Versions[j] = m.Versions[j], m.Versions[i]
		}
	}

	// now that we're sorted in the appropriate direction,
	// populate next and previous for each migration
	//
	// work around http://code.google.com/p/go/issues/detail?id=3117
	previousV := int64(-1)
	for _, v := range m.Versions {
		cur := m.Migrations[v]
		cur.Previous = previousV
		m.Migrations[v] = cur

		// if a migration exists at prev, its next is now v
		if prev, ok := m.Migrations[previousV]; ok {
			prev.Next = v
			m.Migrations[previousV] = prev
		}

		previousV = v
	}
}

// look for migration scripts with names in the form:
//  XXX_descriptivename.ext
// where XXX specifies the version number
// and ext specifies the type of migration
func numericComponent(name string) (int64, error) {

	base := path.Base(name)

	if ext := path.Ext(base); ext != ".go" && ext != ".sql" {
		return 0, errors.New("not a recognized migration file type")
	}

	idx := strings.Index(base, "_")
	if idx < 0 {
		return 0, errors.New("no separator found")
	}

	n, e := strconv.ParseInt(base[:idx], 10, 64)
	if e == nil && n <= 0 {
		return 0, errors.New("migration IDs must be greater than zero")
	}

	return n, e
}

// retrieve the current version for this DB.
// Create and initialize the DB version table if it doesn't exist.
func ensureDBVersion(db *sql.DB) (int64, error) {

	rows, err := db.Query("SELECT version_id, is_applied from goose_db_version ORDER BY tstamp DESC;")
	if err != nil {
		// XXX: cross platform method to detect failure reason
		// for now, assume it was because the table didn't exist, and try to create it
		return 0, createVersionTable(db)
	}

	// The most recent record for each migration specifies
	// whether it has been applied or rolled back.
	// The first version we find that has been applied is the current version.

	toSkip := make([]int64, 0)

	for rows.Next() {
		var row MigrationRecord
{'fraction_non_alphanumeric': 0.12757164675231336, 'fraction_numerical': 0.009792471476057857, 'mean_word_length': 3.2766039185555127, 'pattern_counts': {'":': 4, '<': 20, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1811885', 'n_tokens_mistral': 4106, 'n_tokens_neox': 3795, 'n_words': 1299}