pg_migrate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ module PgMigrate
2
+
3
+ class SqlReader
4
+
5
+ def initialize
6
+
7
+ end
8
+
9
+
10
+ # read in a migration file,
11
+ # converting lines of text into SQL statements that can be executed with our database connection
12
+ def load_migration(migration_path)
13
+ statements = []
14
+
15
+ current_statement = ""
16
+
17
+ migration_lines = IO.readlines(migration_path)
18
+ migration_lines.each_with_index do |line, index|
19
+ line_stripped = line.strip
20
+
21
+ if line_stripped.empty? || line_stripped.start_with?('--')
22
+ # it's a comment; ignore
23
+ elsif line_stripped.start_with?("\\")
24
+ # it's a psql command; ignore
25
+ else
26
+ current_statement += " " + line_stripped;
27
+
28
+ if line_stripped.end_with?(";")
29
+ if current_statement =~ /^\s*CREATE\s+(OR\s+REPLACE\s+)?FUNCTION/i
30
+ # if we are in a function, a ';' isn't enough to end. We need to see if the last word was one of
31
+ # pltcl, plperl, plpgsql, plpythonu, sql.
32
+ # you can extend languages in postgresql; detecting these isn't supported yet.
33
+
34
+ if current_statement =~ /(plpgsql|plperl|plpythonu|pltcl|sql)\s*;$/i
35
+ statements.push(current_statement[0...-1]) # strip off last ;
36
+ current_statement = ""
37
+ end
38
+
39
+ else
40
+ statements.push(current_statement[0...-1]) # strip off last ;
41
+ current_statement = ""
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ return statements
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,145 @@
1
+ -- pg_migrate bootstrap
2
+ -- purpose: responsible for creating or updating pg_migration internal tables
3
+ -- when: this script should be called before every migration attempt, to be safe.
4
+ -- except: if you know you have not updated the pg_migrate tool itself,
5
+ -- and you know you have run this file at least once before on the current database,
6
+ -- then you don't have to run this again.
7
+
8
+ \set ON_ERROR_STOP 1
9
+
10
+ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
11
+
12
+ CREATE OR REPLACE FUNCTION bootstrap_pg_migrate() RETURNS void AS $$
13
+ DECLARE
14
+ found_pg_migrate information_schema.tables;
15
+ found_pg_migrations information_schema.tables;
16
+
17
+ BEGIN
18
+ BEGIN
19
+ SELECT * INTO STRICT found_pg_migrate FROM information_schema.tables WHERE table_name = 'pg_migrate';
20
+
21
+ EXCEPTION
22
+ WHEN NO_DATA_FOUND THEN
23
+ CREATE TABLE pg_migrate (id BIGSERIAL PRIMARY KEY, template_version VARCHAR(255), builder_version VARCHAR(255), migrator_version VARCHAR(255), database_version VARCHAR(1024));
24
+ CREATE INDEX pg_migrate_unique_index ON pg_migrate (template_version, builder_version, migrator_version, database_version);
25
+
26
+ WHEN TOO_MANY_ROWS THEN
27
+ RAISE EXCEPTION 'Multiple pg_migrate tables. Unique key on information_schema.tables should have prevented this.';
28
+ END;
29
+
30
+ BEGIN
31
+ SELECT * INTO STRICT found_pg_migrations FROM information_schema.tables WHERE table_name = 'pg_migrations';
32
+
33
+ EXCEPTION
34
+ WHEN NO_DATA_FOUND THEN
35
+ CREATE TABLE pg_migrations(
36
+ name VARCHAR(255) PRIMARY KEY,
37
+ ordinal INTEGER NOT NULL,
38
+ created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
39
+ finalized SMALLINT DEFAULT 1,
40
+ pg_migrate_id BIGINT NOT NULL REFERENCES pg_migrate(id));
41
+ WHEN TOO_MANY_ROWS THEN
42
+ RAISE EXCEPTION 'Multiple pg_migrations tables. Unique key on information_schema.tables should have prevented this.';
43
+ END;
44
+
45
+ END;
46
+ $$ LANGUAGE plpgsql;
47
+
48
+
49
+ SELECT bootstrap_pg_migrate();
50
+
51
+ -- verifies that the specified migration name has the expected ordinal. No exception is thrown if no migration
52
+ -- of this name exists, and no exception is thrown if the migration + ordinal pair is found on a single row.
53
+ -- Only if the migrtion is found with the wrong row is an exception thrown.
54
+ CREATE OR REPLACE FUNCTION verify_against_existing_migrations(migration varchar(255), ordinal integer) RETURNS VOID AS $$
55
+ DECLARE
56
+ found_migration pg_migrations;
57
+ BEGIN
58
+ BEGIN
59
+ EXECUTE 'SELECT * FROM pg_migrations WHERE name=$1' INTO STRICT found_migration USING migration;
60
+ EXCEPTION
61
+ WHEN NO_DATA_FOUND THEN
62
+ -- if no data, then this migration is unrun. One more check remains...
63
+ -- if the last run migration's ordinal is 1 less than this one, we are ready to go.
64
+ IF coalesce((SELECT MAX(p.ordinal) FROM pg_migrations as p), -1) <> ordinal - 1 THEN
65
+ RAISE EXCEPTION 'pg_migrate: code=missing_migration, migration=%, ordinal=%, last_ordinal=%', migration, ordinal, (SELECT MAX(p.ordinal) FROM pg_migrations as p);
66
+ END IF;
67
+
68
+ RETURN;
69
+ WHEN TOO_MANY_ROWS THEN
70
+ -- this is certainly an odd scenario, because how could this happen unless the user dropped unique key on 'name' column?
71
+ RAISE EXCEPTION 'pg_migrate: code=pg_migrations_uniqueness_error, migration=%', migration;
72
+ END;
73
+
74
+ -- one row has been found; verify ordinal is correct
75
+ IF found_migration.ordinal <> ordinal THEN
76
+ RAISE EXCEPTION 'pg_migrate: code=incorrect_ordinal, migration=%, expected_ordinal=%, actual_ordinal', migration, ordinal, found_migration.ordinal;
77
+ END IF;
78
+
79
+ END;
80
+ $$ LANGUAGE plpgsql;
81
+
82
+
83
+ -- checks if the current script has been executed or not, and throws an exception if so
84
+ -- callers should check for 'pg_migrate: code=migration_exists' to know whether they should quietly ignore exception.
85
+ -- psql can only do this by calling \set ON_ERROR_STOP 1 (which is done by templates already)
86
+ CREATE OR REPLACE FUNCTION bypass_existing_migration(migration varchar(255)) RETURNS VOID AS $$
87
+ DECLARE
88
+ found_migration pg_migrations;
89
+ BEGIN
90
+ BEGIN
91
+ EXECUTE 'SELECT * FROM pg_migrations WHERE name=$1' INTO STRICT found_migration USING migration ;
92
+ EXCEPTION
93
+ WHEN NO_DATA_FOUND THEN
94
+ -- if no data, then success. just return with no exception thrown
95
+ RETURN;
96
+ WHEN TOO_MANY_ROWS THEN
97
+ -- this path should never happen because this same condition is already checked via 'verify_against_existing_migrations'
98
+ RAISE EXCEPTION 'pg_migrate: code=pg_migrations_uniqueness_error, migration=%', migration;
99
+ END;
100
+
101
+ RAISE EXCEPTION 'pg_migrate: code=migration_exists, migration=%', migration;
102
+ END;
103
+ $$ LANGUAGE plpgsql;
104
+
105
+
106
+ -- used to mark a migration as finished
107
+ CREATE OR REPLACE FUNCTION record_migration(migration varchar(255), ordinal INTEGER, template_version VARCHAR(255), builder_version VARCHAR(255)) RETURNS VOID AS $$
108
+ DECLARE
109
+ found_pg_migrate_id BIGINT;
110
+ migrator_version VARCHAR(255);
111
+ BEGIN
112
+
113
+ EXECUTE 'SELECT current_setting(''application_name'')' INTO migrator_version;
114
+
115
+ BEGIN
116
+ -- first look for existing pg_migrate row satisfying version columns
117
+ -- but if not found, create that row
118
+ -- in either case, found_pg_migrate_id will have the row id
119
+ EXECUTE 'SELECT id FROM pg_migrate WHERE
120
+ template_version=$1 and builder_version=$2 and migrator_version=$3 and database_version=$4'
121
+ INTO found_pg_migrate_id USING template_version, builder_version, migrator_version, (select version());
122
+ EXCEPTION
123
+ WHEN NO_DATA_FOUND THEN
124
+ found_pg_migrate_id = NULL;
125
+ WHEN TOO_MANY_ROWS THEN
126
+ -- this path should never occur because of the multi-column index on pg_migrate
127
+ RAISE EXCEPTION 'pg_migrate: code=pg_migrate_uniqueness_error, migration=%, ordinal=%, template_version=%, builder_version=%, migrator_version=%, database_version', migration, ordinal, template_version, builder_version, migrator_version, (select version());
128
+ END;
129
+
130
+ IF found_pg_migrate_id IS NULL THEN
131
+ INSERT INTO pg_migrate(id, template_version, builder_version, migrator_version, database_version)
132
+ VALUES (default, template_version, builder_version, migrator_version, (select version())) RETURNING id INTO found_pg_migrate_id;
133
+ END IF;
134
+
135
+ -- create a new record in pg_migrations table, ensuring this migration won't be run again
136
+ EXECUTE 'INSERT INTO pg_migrations(name, ordinal, created, finalized, pg_migrate_id)
137
+ VALUES ($1, $2, CURRENT_TIMESTAMP, 1, $3)' USING migration, ordinal, found_pg_migrate_id;
138
+
139
+ END;
140
+ $$ LANGUAGE plpgsql;
141
+
142
+ COMMIT;
143
+
144
+
145
+
@@ -0,0 +1,28 @@
1
+ -- beginning pg_migrate header, migration=<%= migration_def.name %>
2
+ \set ON_ERROR_STOP 1
3
+
4
+ BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
5
+
6
+ -- we want to ensure no one else is running this migration concurrently
7
+ LOCK TABLE pg_migrations IN ACCESS EXCLUSIVE MODE;
8
+
9
+ SELECT verify_against_existing_migrations('<%= migration_def.name %>', <%= migration_def.ordinal %>);
10
+
11
+ -- to allow exception in CHECK_IF_MIGRATED to cause rollback, not failure
12
+ \set ON_ERROR_STOP 0
13
+
14
+ SELECT bypass_existing_migration('<%= migration_def.name %>');
15
+
16
+ -- if the content of the migration is bad, we want psql to stop.
17
+ \set ON_ERROR_STOP 1
18
+
19
+ -- beginning user sql, migration=<%= migration_def.name %>
20
+
21
+ <%= migration_content %>
22
+
23
+ -- beginning pg_migrate footer, migration=<%= migration_def.name %>
24
+
25
+ SELECT record_migration('<%= migration_def.name %>', <%= migration_def.ordinal %>, '0.0.1', '<%= builder_version %>');
26
+
27
+ COMMIT;
28
+ -- end pg_migrate migration, migration=<%= migration_def.name %>
@@ -0,0 +1,3 @@
1
+ module PgMigrate
2
+ VERSION = "0.0.1"
3
+ end
data/lib/pg_migrate.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'logging'
2
+ require 'pg'
3
+ require 'thor'
4
+ require "pg_migrate/version"
5
+ require "pg_migrate/migration"
6
+ require "pg_migrate/sql_reader"
7
+ require "pg_migrate/manifest_reader"
8
+ require "pg_migrate/migrator"
9
+ require "pg_migrate/config_parser"
10
+ require "pg_migrate/builder"
11
+ require "pg_migrate/command_line"
12
+
13
+ # name of the manifest file
14
+ MANIFEST_FILENAME = 'manifest'
15
+ # name of the 'forward' migration folder
16
+ UP_DIRNAME = 'up'
17
+ # name of the 'backwards' migration folder
18
+ DOWN_DIRNAME = 'down'
19
+ # name of the 'test' migration folder
20
+ TESTDIRNAME = 'test'
21
+ # name of the bootstrap.sql file
22
+ BOOTSTRAP_FILENAME = "bootstrap.sql"
23
+ # built manifest version header
24
+ BUILDER_VERSION_HEADER="# pg_migrate-"
25
+
26
+
27
+ ### SQL CONSTANTS ###
28
+ PG_MIGRATE_TABLE = "pg_migrate"
29
+ PG_MIGRATIONS_TABLE = "pg_migrations"
30
+
31
+
32
+
33
+ module PgMigrate
34
+ # Your code goes here...
35
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/pg_migrate/version', __FILE__)
3
+ lib=File.expand_path('../lib', __FILE__)
4
+
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.authors = ["Seth Call"]
8
+ gem.email = ["sethcall@gmail.com"]
9
+ gem.description = %q{Simple migration tool focused on Postgresql}
10
+ gem.summary = %q{Create migration scripts in raw SQL that work regardless if they are run from the pg_migrate command-line, psql, or native code integration. More documentation exists on the project homepage.)}
11
+ gem.homepage = "https://github.com/sethcall/pg_migrate"
12
+
13
+ gem.files = `git ls-files`.split($\)
14
+ gem.files += `cd #{lib}/pg_migrate/templates; git ls-files *.erb`.split($\).map {|f| "lib/pg_migrate/templates/#{f}"}
15
+ puts gem.files
16
+ gem.files.delete("lib/pg_migrate/templates")
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.name = "pg_migrate"
20
+ gem.require_paths = ["lib"]
21
+ gem.version = PgMigrate::VERSION
22
+
23
+ gem.add_dependency('logging', '1.7.2')
24
+
25
+ gem.add_dependency('pg', '0.14.0')
26
+ gem.add_dependency('thor', '0.15.4')
27
+ end
28
+
data/spec/database.yml ADDED
@@ -0,0 +1,9 @@
1
+ test:
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: pg_migrate_test
5
+ pool: 5
6
+ username: postgres
7
+ password: postgres
8
+ host: localhost
9
+ port: 5432
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Builder do
4
+
5
+ before(:all) do
6
+ @manifest_reader = ManifestReader.new
7
+ @sql_reader = SqlReader.new
8
+ @standard_builder = Builder.new(@manifest_reader, @sql_reader)
9
+ @dbutil = DbUtility.new
10
+ end
11
+
12
+ it "create bootstrap.sql" do
13
+ standard_builder = @standard_builder
14
+ target = Files.create :path => "target/bootstrap_test", :timestamp => false do
15
+ standard_builder.create_bootstrap_script(Dir.pwd)
16
+
17
+ # the .sql file should exist after
18
+ FileTest::exists?(BOOTSTRAP_FILENAME).should == true
19
+
20
+ content = nil
21
+
22
+ # dynamic content should be in the file
23
+ File.open(BOOTSTRAP_FILENAME, 'r') { |reader| content = reader.read }
24
+
25
+ content.start_with?('-- pg_migrate bootstrap').should == true
26
+ end
27
+
28
+ end
29
+
30
+ it "creates indempotent migrations" do
31
+
32
+ def run_bootstrap(output_dir)
33
+ run_migration(BOOTSTRAP_FILENAME, output_dir)
34
+ end
35
+
36
+ def run_migration(migration_path, output_dir)
37
+ @dbutil.connect_test_database() do |conn|
38
+ statements = @sql_reader.load_migration(File.join(output_dir, UP_DIRNAME, migration_path))
39
+
40
+ statements.each do |statement|
41
+ conn.exec(statement)
42
+ end
43
+ end
44
+ end
45
+
46
+ def verify_bootstrap()
47
+ # come back in, and verify that the bootstrap tables are there
48
+ @dbutil.connect_test_database() do |conn|
49
+ conn.exec("SELECT table_name FROM information_schema.tables WHERE table_name = $1", [PG_MIGRATE_TABLE]) do |result|
50
+ result.ntuples.should == 1
51
+ result.getvalue(0, 0).should == PG_MIGRATE_TABLE
52
+ end
53
+
54
+ conn.exec("SELECT table_name FROM information_schema.tables WHERE table_name = $1", [PG_MIGRATIONS_TABLE]) do |result|
55
+ result.ntuples.should == 1
56
+ result.getvalue(0, 0).should == PG_MIGRATIONS_TABLE
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+ single_manifest=File.expand_path('spec/pg_migrate/input_manifests/single_manifest')
63
+ single_manifest = File.join(single_manifest, '.')
64
+
65
+ input_dir = nil
66
+ target = Files.create :path => "target", :timestamp => false do
67
+ input_dir = dir "input_single_manifest", :src => single_manifest do
68
+
69
+ end
70
+ end
71
+
72
+ output_dir = File.join('target', 'output_single_manifest')
73
+
74
+ @standard_builder.build(input_dir, output_dir)
75
+
76
+ @dbutil.create_new_test_database()
77
+
78
+ # run bootstrap once, and verify the tables now exist
79
+ run_bootstrap(output_dir)
80
+ verify_bootstrap()
81
+
82
+ # run bootstrap again, and verify no error (implicitly), and that the tables now exist
83
+ run_bootstrap(output_dir)
84
+ verify_bootstrap()
85
+
86
+ # now run single1.sql
87
+ run_migration('single1.sql', output_dir)
88
+
89
+ @dbutil.connect_test_database() do |conn|
90
+ conn.exec("SELECT table_name FROM information_schema.tables WHERE table_name = $1", ["emp"]) do |result|
91
+ result.ntuples.should == 1
92
+ result.getvalue(0, 0).should == "emp"
93
+ end
94
+ end
95
+
96
+ # run it again. a very certain exception should occur... 'pg_migrate: code=migration_exists'
97
+ begin
98
+ run_migration('single1.sql', output_dir)
99
+ false.should == true
100
+ rescue Exception => e
101
+ e.message.index('pg_migrate: code=migration_exists').should_not == nil
102
+ end
103
+
104
+ @dbutil.connect_test_database() do |conn|
105
+ conn.exec("SELECT table_name FROM information_schema.tables WHERE table_name = $1", ["emp"]) do |result|
106
+ result.ntuples.should == 1
107
+ result.getvalue(0, 0).should == "emp"
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe ConfigParser do
4
+ it "parse my test database.yml file" do
5
+ config = ConfigParser.rails("spec/database.yml", "test")
6
+ config.should == {
7
+ :dbname => "pg_migrate_test",
8
+ :user => "postgres",
9
+ :password => "postgres",
10
+ :host => "localhost",
11
+ :port => 5432
12
+ }
13
+ end
14
+
15
+ it "run single migration" do
16
+ config = ConfigParser.rails("spec/database.yml", "test")
17
+ end
18
+
19
+ end
@@ -0,0 +1,73 @@
1
+ module PgMigrate
2
+
3
+ class DbUtility
4
+
5
+ DEFAULT_OPTIONS = {
6
+ :dbtestname => "pg_migrate_test",
7
+ :dbsuperuser => "postgres",
8
+ :dbsuperpass => "postgres",
9
+ :dbhost => "localhost",
10
+ :dbport => 5432
11
+ }
12
+
13
+ def initialize(options=DEFAULT_OPTIONS)
14
+
15
+ options = DEFAULT_OPTIONS.merge(options)
16
+
17
+ @dbtestname = options[:dbtestname]
18
+ @dbsuperuser = options[:dbsuperuser]
19
+ @dbsuperpass = options[:dbsuperpass]
20
+ @dbhost = options[:dbhost]
21
+ @dbport = options[:dbport]
22
+ end
23
+
24
+ def pg_connection_hasher()
25
+ return {
26
+ :port => @dbport,
27
+ :user => @dbsuperuser,
28
+ :password => @dbsuperpass,
29
+ :host => @dbhost
30
+ }
31
+ end
32
+
33
+
34
+ def create_new_test_database()
35
+
36
+ # this will presumably do the right default thing,
37
+ # to get us into a 'default' database where we can execute 'create database' from
38
+ conn_properties = pg_connection_hasher
39
+
40
+ conn_properties.delete(:dbname)
41
+
42
+ conn = PG::Connection.new(conn_properties)
43
+
44
+ conn.exec("DROP DATABASE IF EXISTS #{@dbtestname}").clear
45
+ conn.exec("CREATE DATABASE #{@dbtestname}").clear
46
+
47
+ conn.close
48
+
49
+ end
50
+
51
+ def connect_test_database(&block)
52
+ conn = nil
53
+
54
+ begin
55
+ conn_properties = pg_connection_hasher
56
+
57
+ conn_properties[:dbname] = @dbtestname
58
+ conn = PG::Connection.open(conn_properties)
59
+
60
+ yield conn
61
+
62
+ ensure
63
+ if !conn.nil?
64
+ conn.close
65
+ end
66
+ end
67
+
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,4 @@
1
+ # name=single
2
+ # this is a comment
3
+
4
+ single1.sql
@@ -0,0 +1,29 @@
1
+ -- this is a SQL comment
2
+
3
+ select 1;
4
+
5
+ select 2;
6
+
7
+ select
8
+ 3;
9
+
10
+ create table emp(id BIGSERIAL PRIMARY KEY, name varchar(255));
11
+
12
+ -- a sql function
13
+ CREATE FUNCTION clean_emp() RETURNS void AS '
14
+ DELETE FROM emp;
15
+ ' LANGUAGE SQL;
16
+
17
+ -- a sql function on one line
18
+ CREATE FUNCTION clean_emp2() RETURNS void AS 'DELETE FROM emp;' LANGUAGE SQL;
19
+
20
+ CREATE FUNCTION populate() RETURNS integer AS $$
21
+ DECLARE
22
+ -- declarations
23
+ BEGIN
24
+ PERFORM clean_emp2();
25
+ END;
26
+ $$ LANGUAGE plpgsql;
27
+
28
+
29
+
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe ManifestReader do
4
+
5
+ before(:all) do
6
+ @manifest_reader = ManifestReader.new
7
+ end
8
+
9
+ it "load single manifest" do
10
+ manifest = @manifest_reader.load_input_manifest("spec/pg_migrate/input_manifests/single_manifest")
11
+
12
+ manifest.length.should == 1
13
+ manifest[0].name.should == "single1.sql"
14
+ end
15
+
16
+ it "fail on bad manifest reference" do
17
+ expect { @manifest_reader.validate_migration_paths('absolutely_nowhere_real', ["migration1"]) }.to raise_exception
18
+ end
19
+
20
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe Migrator do
4
+
5
+ before(:all) do
6
+ @manifest_reader = ManifestReader.new
7
+ @sql_reader = SqlReader.new
8
+ @standard_builder = Builder.new(@manifest_reader, @sql_reader)
9
+ @standard_migrator = Migrator.new(@manifest_reader, @sql_reader)
10
+ @dbutil = DbUtility.new
11
+ end
12
+
13
+ it "migrate single_manifest" do
14
+
15
+ def migrate_it(output_dir)
16
+ @dbutil.connect_test_database do |conn|
17
+
18
+ standard_migrator = Migrator.new(@manifest_reader, @sql_reader, :pgconn=>conn)
19
+ standard_migrator.migrate(output_dir)
20
+
21
+ conn.transaction do |transaction|
22
+ transaction.exec("SELECT table_name FROM information_schema.tables WHERE table_name = $1", ["emp"]) do |result|
23
+ result.ntuples.should == 1
24
+ result.getvalue(0, 0).should == "emp"
25
+ end
26
+
27
+ pg_migration_id = nil
28
+ transaction.exec("SELECT * FROM pg_migrations") do |result|
29
+ result.ntuples.should == 1
30
+ result[0]["name"].should == "single1.sql"
31
+ result[0]["ordinal"].should == "0"
32
+ pg_migration_id = result[0]["pg_migrate_id"]
33
+ end
34
+ pg_migration_id.should_not == nil
35
+
36
+ # verify that a database row in pg_migrate was created as side-effect
37
+ transaction.exec("SELECT * FROM pg_migrate WHERE id = $1", [pg_migration_id]) do |result|
38
+ result.ntuples.should == 1
39
+ result[0]["template_version"].should == "0.0.1"
40
+ result[0]["builder_version"].should == "pg_migrate_ruby-#{PgMigrate::VERSION}"
41
+ result[0]["migrator_version"].should == "pg_migrate_ruby-#{PgMigrate::VERSION}"
42
+ result[0]["database_version"].should_not be nil
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ single_manifest=File.expand_path('spec/pg_migrate/input_manifests/single_manifest')
49
+ single_manifest = File.join(single_manifest, '.')
50
+
51
+ input_dir = nil
52
+ target = Files.create :path => "target", :timestamp => false do
53
+ input_dir = dir "input_single_manifest", :src => single_manifest do
54
+
55
+ end
56
+ end
57
+
58
+ output_dir = File.join('target', 'output_single_manifest')
59
+
60
+ @standard_builder.build(input_dir, output_dir, :force => true)
61
+
62
+ @dbutil.create_new_test_database
63
+
64
+ migrate_it(output_dir)
65
+ migrate_it(output_dir)
66
+ end
67
+ end
68
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe SqlReader do
4
+
5
+ before(:all) do
6
+ @sql_reader = SqlReader.new
7
+ end
8
+
9
+ it "load single migration" do
10
+ migrations = @sql_reader.load_migration("spec/pg_migrate/input_manifests/single_manifest/up/single1.sql")
11
+
12
+ migrations.length.should == 7
13
+ migrations[0] = "select 1"
14
+ migrations[1] = "select 2"
15
+ migrations[2] = "select 3"
16
+ migrations[3] = "create table emp()"
17
+ migrations[4] = "CREATE FUNCTION clean_emp() RETURNS void AS ' DELETE FROM emp; ' LANGUAGE SQL"
18
+ migrations[5] = "CREATE FUNCTION clean_emp2() RETURNS void AS 'DELETE FROM emp;' LANGUAGE SQL"
19
+ migrations[6] = "CREATE FUNCTION populate() RETURNS integer AS $$ DECLARE BEGIN PERFORM select 1; END; $$ LANGUAGE plpgsql"
20
+ end
21
+
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'logging'
2
+
3
+ # bootstrap logger
4
+ Logging.logger.root.level = :debug
5
+ Logging.logger.root.appenders = Logging.appenders.stdout
6
+
7
+ require 'pg_migrate'
8
+ require 'pg_migrate/db_utility'
9
+ require 'files'
10
+ require 'fileutils'
11
+
12
+ target = File.join(File.dirname(__FILE__), '..', 'target')
13
+ FileUtils::rm_r(target, :force => true)
14
+
15
+ include PgMigrate