Skip to content
Snippets Groups Projects
migrate.go 5.41 KiB
Newer Older
  • Learn to ignore specific revisions
  • package sql
    
    import (
    	"database/sql"
    	"fmt"
    )
    
    func (c *conn) migrate() (int, error) {
    	_, err := c.Exec(`
    		create table if not exists migrations (
    			num integer not null,
    
    			at timestamptz not null
    
    		);
    	`)
    	if err != nil {
    		return 0, fmt.Errorf("creating migration table: %v", err)
    	}
    
    	i := 0
    	done := false
    
    
    	var flavorMigrations []migration
    	for _, m := range migrations {
    		if m.flavor == nil || m.flavor == c.flavor {
    			flavorMigrations = append(flavorMigrations, m)
    		}
    	}
    
    
    	for {
    		err := c.ExecTx(func(tx *trans) error {
    			// Within a transaction, perform a single migration.
    			var (
    				num sql.NullInt64
    				n   int
    			)
    			if err := tx.QueryRow(`select max(num) from migrations;`).Scan(&num); err != nil {
    				return fmt.Errorf("select max migration: %v", err)
    			}
    			if num.Valid {
    				n = int(num.Int64)
    			}
    
    			if n >= len(flavorMigrations) {
    
    				done = true
    				return nil
    			}
    
    			migrationNum := n + 1
    
    			for i := range m.stmts {
    				if _, err := tx.Exec(m.stmts[i]); err != nil {
    					return fmt.Errorf("migration %d statement %d failed: %v", migrationNum, i+1, err)
    				}
    
    			}
    
    			q := `insert into migrations (num, at) values ($1, now());`
    			if _, err := tx.Exec(q, migrationNum); err != nil {
    				return fmt.Errorf("update migration table: %v", err)
    			}
    			return nil
    		})
    		if err != nil {
    			return i, err
    		}
    		if done {
    			break
    		}
    		i++
    	}
    
    	return i, nil
    }
    
    type migration struct {
    
    
    	// If flavor is nil the migration will take place for all database backend flavors.
    	// If specified, only for that corresponding flavor, in that case stmts can be written
    	// in the specific SQL dialect.
    	flavor *flavor
    
    }
    
    // All SQL flavors share migration strategies.
    var migrations = []migration{
    	{
    
    			create table client (
    				id text not null primary key,
    				secret text not null,
    				redirect_uris bytea not null, -- JSON array of strings
    				trusted_peers bytea not null, -- JSON array of strings
    				public boolean not null,
    				name text not null,
    				logo_url text not null
    
    			create table auth_request (
    				id text not null primary key,
    				client_id text not null,
    				response_types bytea not null, -- JSON array of strings
    				scopes bytea not null,         -- JSON array of strings
    				redirect_uri text not null,
    				nonce text not null,
    				state text not null,
    				force_approval_prompt boolean not null,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				logged_in boolean not null,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				claims_user_id text not null,
    				claims_username text not null,
    				claims_email text not null,
    				claims_email_verified boolean not null,
    				claims_groups bytea not null, -- JSON array of strings
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				connector_id text not null,
    
    				connector_data bytea,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				expiry timestamptz not null
    
    			create table auth_code (
    				id text not null primary key,
    				client_id text not null,
    				scopes bytea not null, -- JSON array of strings
    				nonce text not null,
    				redirect_uri text not null,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				claims_user_id text not null,
    				claims_username text not null,
    				claims_email text not null,
    				claims_email_verified boolean not null,
    				claims_groups bytea not null, -- JSON array of strings
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				connector_id text not null,
    
    				connector_data bytea,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				expiry timestamptz not null
    
    			create table refresh_token (
    				id text not null primary key,
    				client_id text not null,
    				scopes bytea not null, -- JSON array of strings
    				nonce text not null,
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				claims_user_id text not null,
    				claims_username text not null,
    				claims_email text not null,
    				claims_email_verified boolean not null,
    				claims_groups bytea not null, -- JSON array of strings
    
    Joel Speed's avatar
    Joel Speed committed
    
    
    				connector_id text not null,
    				connector_data bytea
    
    			create table password (
    				email text not null primary key,
    				hash bytea not null,
    				username text not null,
    				user_id text not null
    
    			-- keys is a weird table because we only ever expect there to be a single row
    			create table keys (
    				id text not null primary key,
    				verification_keys bytea not null, -- JSON array
    				signing_key bytea not null,       -- JSON object
    				signing_key_pub bytea not null,   -- JSON object
    
    				next_rotation timestamptz not null
    
    				add column token text not null default '';`,
    			`
    
    				add column created_at timestamptz not null default '0001-01-01 00:00:00 UTC';`,
    			`
    
    				add column last_used timestamptz not null default '0001-01-01 00:00:00 UTC';`,
    		},
    
    			create table offline_session (
    				user_id text not null,
    				conn_id text not null,
    				refresh bytea not null,
    				PRIMARY KEY (user_id, conn_id)
    
    			create table connector (
    				id text not null primary key,
    				type text not null,
    				name text not null,
    				resource_version text not null,
    				config bytea
    
    	{
    		stmts: []string{`
    			alter table auth_code
    				add column claims_preferred_username text not null default '';`,
    			`
    			alter table auth_request
    				add column claims_preferred_username text not null default '';`,
    			`
    			alter table refresh_token
    				add column claims_preferred_username text not null default '';`,
    		},
    	},
    
    	{
    		stmts: []string{`
    			alter table offline_session
    
    				add column connector_data bytea;
    
    	{
    		stmts: []string{`
    			alter table auth_request
    				modify column state varchar(4096);
    			`,
    		},
    		flavor: &flavorMySQL,
    	},