2016-09-15 01:11:57 +00:00
|
|
|
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,
|
2016-12-16 19:03:36 +00:00
|
|
|
at timestamptz not null
|
2016-09-15 01:11:57 +00:00
|
|
|
);
|
|
|
|
`)
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("creating migration table: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
done := false
|
|
|
|
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(migrations) {
|
|
|
|
done = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
migrationNum := n + 1
|
|
|
|
m := migrations[n]
|
|
|
|
if _, err := tx.Exec(m.stmt); err != nil {
|
|
|
|
return fmt.Errorf("migration %d failed: %v", migrationNum, 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 {
|
|
|
|
stmt string
|
|
|
|
// TODO(ericchiang): consider adding additional fields like "forDrivers"
|
|
|
|
}
|
|
|
|
|
|
|
|
// All SQL flavors share migration strategies.
|
|
|
|
var migrations = []migration{
|
|
|
|
{
|
|
|
|
stmt: `
|
|
|
|
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,
|
|
|
|
|
|
|
|
logged_in boolean not null,
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
connector_id text not null,
|
|
|
|
connector_data bytea,
|
|
|
|
|
2016-12-16 19:03:36 +00:00
|
|
|
expiry timestamptz not null
|
2016-09-15 01:11:57 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
connector_id text not null,
|
|
|
|
connector_data bytea,
|
|
|
|
|
2016-12-16 19:03:36 +00:00
|
|
|
expiry timestamptz not null
|
2016-09-15 01:11:57 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
connector_id text not null,
|
|
|
|
connector_data bytea
|
|
|
|
);
|
2016-10-05 23:04:11 +00:00
|
|
|
|
|
|
|
create table password (
|
|
|
|
email text not null primary key,
|
|
|
|
hash bytea not null,
|
|
|
|
username text not null,
|
|
|
|
user_id text not null
|
|
|
|
);
|
2016-09-15 01:11:57 +00:00
|
|
|
|
|
|
|
-- 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
|
2016-12-16 19:03:36 +00:00
|
|
|
next_rotation timestamptz not null
|
2016-09-15 01:11:57 +00:00
|
|
|
);
|
2017-02-01 00:11:59 +00:00
|
|
|
|
2016-09-15 01:11:57 +00:00
|
|
|
`,
|
|
|
|
},
|
2016-12-22 23:56:09 +00:00
|
|
|
{
|
|
|
|
stmt: `
|
|
|
|
alter table refresh_token
|
|
|
|
add column token text not null default '';
|
|
|
|
alter table refresh_token
|
|
|
|
add column created_at timestamptz not null default '0001-01-01 00:00:00 UTC';
|
|
|
|
alter table refresh_token
|
|
|
|
add column last_used timestamptz not null default '0001-01-01 00:00:00 UTC';
|
|
|
|
`,
|
|
|
|
},
|
2017-02-01 00:11:59 +00:00
|
|
|
{
|
|
|
|
stmt: `
|
|
|
|
create table offline_session (
|
|
|
|
user_id text not null,
|
|
|
|
conn_id text not null,
|
|
|
|
refresh bytea not null,
|
|
|
|
PRIMARY KEY (user_id, conn_id)
|
|
|
|
);
|
|
|
|
`,
|
|
|
|
},
|
2017-03-23 16:59:33 +00:00
|
|
|
{
|
|
|
|
stmt: `
|
|
|
|
create table connector (
|
|
|
|
id text not null primary key,
|
|
|
|
type text not null,
|
|
|
|
name text not null,
|
|
|
|
resource_version text not null,
|
|
|
|
config bytea
|
|
|
|
);
|
|
|
|
`,
|
|
|
|
},
|
2016-09-15 01:11:57 +00:00
|
|
|
}
|