pg_migrate 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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