Skip to content

Commit 8bd62d0

Browse files
authored
Merge pull request diesel-rs#991 from diesel-rs/sg-default-migrations
Don't magically set up timestamp helpers, create a migration file
2 parents 0e22fe3 + 58123cb commit 8bd62d0

15 files changed

Lines changed: 150 additions & 41 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
6161
* Trait bounds along the lines of `T: LoadDsl<Conn>, U: Queryable<T::SqlType,
6262
Conn::Backend>` should be changed to `T: LoadQuery<Conn, U>`.
6363

64+
* Diesel now uses a migration to set up its timestamp helpers. To generate this
65+
migration for your project, run `diesel database setup`.
66+
6467
### Removed
6568

6669
* `#[has_many]` has been removed. Its functionality is now provided by

diesel/src/connection/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,4 @@ pub trait Connection: SimpleConnection + Sized + Send {
148148

149149
#[doc(hidden)] fn silence_notices<F: FnOnce() -> T, T>(&self, f: F) -> T;
150150
#[doc(hidden)] fn transaction_manager(&self) -> &Self::TransactionManager;
151-
#[doc(hidden)] fn setup_helper_functions(&self);
152151
}

diesel/src/migrations/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ fn migration_with_version(migrations_dir: &Path, ver: &str) -> Result<Box<Migrat
203203

204204
#[doc(hidden)]
205205
pub fn setup_database<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
206-
conn.setup_helper_functions();
207206
create_schema_migrations_table_if_needed(conn)
208207
}
209208

diesel/src/mysql/connection/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,6 @@ impl Connection for MysqlConnection {
9797
fn transaction_manager(&self) -> &Self::TransactionManager {
9898
&self.transaction_manager
9999
}
100-
101-
#[doc(hidden)]
102-
fn setup_helper_functions(&self) {
103-
// FIXME: We can implement this pretty easily
104-
}
105100
}
106101

107102
impl MysqlConnection {

diesel/src/pg/connection/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,6 @@ impl Connection for PgConnection {
9595
fn transaction_manager(&self) -> &Self::TransactionManager {
9696
&self.transaction_manager
9797
}
98-
99-
#[doc(hidden)]
100-
fn setup_helper_functions(&self) {
101-
self.batch_execute(
102-
include_str!("setup/timestamp_helpers.sql")
103-
).expect("Error creating timestamp helper functions for Pg");
104-
}
10598
}
10699

107100
impl PgConnection {

diesel/src/pg/connection/setup/timestamp_helpers.sql

Lines changed: 0 additions & 18 deletions
This file was deleted.

diesel/src/sqlite/connection/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,6 @@ impl Connection for SqliteConnection {
9292
fn transaction_manager(&self) -> &Self::TransactionManager {
9393
&self.transaction_manager
9494
}
95-
96-
#[doc(hidden)]
97-
fn setup_helper_functions(&self) {
98-
// this will be implemented at least when timestamps are supported in SQLite
99-
}
10095
}
10196

10297
impl SqliteConnection {

diesel_cli/src/database.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ use std::error::Error;
1818
use std::env;
1919
use std::io::stdout;
2020
use std::path::Path;
21+
#[cfg(feature="postgres")]
22+
use std::fs::{self, File};
23+
#[cfg(feature="postgres")]
24+
use std::io::Write;
2125

2226
enum Backend {
2327
#[cfg(feature="postgres")]
@@ -116,8 +120,10 @@ pub fn reset_database(args: &ArgMatches, migrations_dir: &Path) -> DatabaseResul
116120
pub fn setup_database(args: &ArgMatches, migrations_dir: &Path) -> DatabaseResult<()> {
117121
let database_url = database_url(args);
118122

119-
try!(create_database_if_needed(&database_url));
120-
create_schema_table_and_run_migrations_if_needed(&database_url, migrations_dir)
123+
create_database_if_needed(&database_url)?;
124+
create_default_migration_if_needed(&database_url, migrations_dir)?;
125+
create_schema_table_and_run_migrations_if_needed(&database_url, migrations_dir)?;
126+
Ok(())
121127
}
122128

123129
pub fn drop_database_command(args: &ArgMatches) -> DatabaseResult<()> {
@@ -158,6 +164,31 @@ fn create_database_if_needed(database_url: &str) -> DatabaseResult<()> {
158164
Ok(())
159165
}
160166

167+
fn create_default_migration_if_needed(database_url: &str, migrations_dir: &Path)
168+
-> DatabaseResult<()>
169+
{
170+
let initial_migration_path = migrations_dir.join("00000000000000_diesel_initial_setup");
171+
if initial_migration_path.exists() {
172+
return Ok(())
173+
}
174+
175+
#[allow(unreachable_patterns)]
176+
#[cfg_attr(feature="clippy", allow(single_match))]
177+
match Backend::for_url(database_url) {
178+
#[cfg(feature="postgres")]
179+
Backend::Pg => {
180+
fs::create_dir_all(&initial_migration_path)?;
181+
let mut up_sql = File::create(initial_migration_path.join("up.sql"))?;
182+
up_sql.write_all(include_bytes!("setup_sql/postgres/initial_setup/up.sql"))?;
183+
let mut down_sql = File::create(initial_migration_path.join("down.sql"))?;
184+
down_sql.write_all(include_bytes!("setup_sql/postgres/initial_setup/down.sql"))?;
185+
}
186+
_ => {} // No default migration for this backend
187+
}
188+
189+
Ok(())
190+
}
191+
161192
/// Creates the `__diesel_schema_migrations` table if it doesn't exist. If the
162193
/// table didn't exist, it also runs any pending migrations. Returns a
163194
/// `DatabaseError::ConnectionError` if it can't create the table, and exits
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
2+
DROP FUNCTION IF EXISTS diesel_set_updated_at();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
-- This file was automatically created by Diesel to setup helper functions
2+
-- and other internal bookkeeping. This file is safe to edit, any future
3+
-- changes will be added to existing projects as new migrations.
4+
5+
6+
7+
8+
-- Sets up a trigger for the given table to automatically set a column called
9+
-- `updated_at` whenever the row is modified (unless `updated_at` was included
10+
-- in the modified columns)
11+
--
12+
-- # Example
13+
--
14+
-- ```sql
15+
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
16+
--
17+
-- SELECT diesel_manage_updated_at('users');
18+
-- ```
19+
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
20+
BEGIN
21+
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
22+
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
23+
END;
24+
$$ LANGUAGE plpgsql;
25+
26+
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
27+
BEGIN
28+
IF (
29+
NEW IS DISTINCT FROM OLD AND
30+
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
31+
) THEN
32+
NEW.updated_at := current_timestamp;
33+
END IF;
34+
RETURN NEW;
35+
END;
36+
$$ LANGUAGE plpgsql;

0 commit comments

Comments
 (0)