正文
table
auth.users
alter
column
created_at
set
default
now
();
$ renovate apply
Your repo is dirty. Please
commit
the changes
before
applying.
$ git
commit
-a -m
"add updated_at column and set default value for created_at"
$ renovate
apply
The
following
SQLs will be applied:
alter
table
auth.users
add
column
updated_at timestamptz
not
null
;
alter
table
auth.users
alter
column
created_at
set
default
now
();
Continue (y/n)? y
Successfully applied migration to postgres://user@localhost:5432/hello.
Your repo is updated
with
the latest schema. See
`git diff HEAD~1`
for
details.
我的大概想法是:用户可以创建一个 db schema repo,用 git 管理 schema 的修改。用户不必考虑 schema migration,只需在现有的 schema 上修改即可,当
renovate schema plan
时,Renovate 会通过 pg_dump 来获取远端的 schema,然后本地和和远端的 SQL 都会被解析成 AST,二者在 AST 级别对比找不同即可。
有了这个思路,接下来就是一些大的数据结构的定义,比如 postgres 下的一个 schema 可以这样描述:
pub struct Schema {
pub types: BTreeMap<String, DataType>,
pub tables: BTreeMap<String, Table>,
pub views: BTreeMap<String, View>,
pub functions: BTreeMap<String, Function>,
pub triggers: BTreeMap<String, Trigger>,
}
一个 table 可以这么描述:
pub struct Table {
pub columns: BTreeMap,
pub constraints: BTreeMap,
pub privileges: BTreeMap,
}
每个级别的数据都需要实现 Planner trait:
pub trait Planner {
fn diff(&self, remote: &Self) -> Vec;
fn plan(&self, diff: &[Diff]) -> Vec;
}
这样,我们就可以从顶层的 schema 一层层追溯到一个 table 的 column 下的 constraint,进行 diff 并给出 migration plan。整体的架构如下(图是今天画的,大致思路没变):
思路有了,我就开始有一搭没一搭地为每个数据结构写一些基础的 parser,然后实现其 migration planner trait。最初,处理的都是一些比较容易的情况,比如用户修改 index 后,我们可以删除旧的 index,再创建新的 index,如下所示: