sqlpostgres 1.2.6 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Changelog.md +18 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +12 -0
  4. data/README.rdoc +5 -2
  5. data/Rakefile +5 -24
  6. data/VERSION +1 -1
  7. data/lib/sqlpostgres/Connection.rb +8 -3
  8. data/lib/sqlpostgres/Cursor.rb +2 -2
  9. data/lib/sqlpostgres/Insert.rb +1 -1
  10. data/lib/sqlpostgres/PgBit.rb +1 -1
  11. data/lib/sqlpostgres/PgBox.rb +1 -1
  12. data/lib/sqlpostgres/PgCidr.rb +1 -1
  13. data/lib/sqlpostgres/PgCircle.rb +1 -1
  14. data/lib/sqlpostgres/PgInet.rb +1 -1
  15. data/lib/sqlpostgres/PgInterval.rb +1 -1
  16. data/lib/sqlpostgres/PgLineSegment.rb +1 -1
  17. data/lib/sqlpostgres/PgMacAddr.rb +1 -1
  18. data/lib/sqlpostgres/PgPath.rb +1 -1
  19. data/lib/sqlpostgres/PgPoint.rb +1 -1
  20. data/lib/sqlpostgres/PgPolygon.rb +1 -1
  21. data/lib/sqlpostgres/PgTime.rb +1 -1
  22. data/lib/sqlpostgres/PgTimeWithTimeZone.rb +1 -1
  23. data/lib/sqlpostgres/PgTimestamp.rb +1 -1
  24. data/lib/sqlpostgres/PgTwoPoints.rb +1 -1
  25. data/lib/sqlpostgres/PgWrapper.rb +1 -1
  26. data/lib/sqlpostgres/Select.rb +25 -25
  27. data/lib/sqlpostgres/Translate.rb +7 -29
  28. data/lib/sqlpostgres/Update.rb +1 -1
  29. data/rake_tasks/db.rake +17 -0
  30. data/rake_tasks/default.rake +1 -0
  31. data/rake_tasks/jeweler.rake +18 -0
  32. data/rake_tasks/test.rake +2 -0
  33. data/rake_tasks/test_spec.rake +3 -0
  34. data/rake_tasks/test_unit.rake +4 -0
  35. data/spec/Translate_spec.rb +533 -0
  36. data/spec/config/.gitignore +1 -0
  37. data/spec/config/config.yml +10 -0
  38. data/spec/config/database.yml.template +6 -0
  39. data/spec/connection_spec.rb +515 -0
  40. data/spec/cursor_spec.rb +288 -0
  41. data/spec/lib/database_config.rb +33 -0
  42. data/spec/lib/database_server.rb +42 -0
  43. data/spec/lib/postgres_template.rb +60 -0
  44. data/spec/lib/target_database_servers.rb +55 -0
  45. data/spec/lib/temporary_table.rb +45 -0
  46. data/spec/lib/test_config.rb +24 -0
  47. data/spec/lib/test_connection.rb +29 -0
  48. data/spec/lib/test_database.rb +57 -0
  49. data/spec/roundtrip_spec.rb +582 -0
  50. data/spec/spec_helper.rb +10 -0
  51. data/spec/support/all_characters.rb +18 -0
  52. data/spec/support/clear_default_connection.rb +5 -0
  53. data/spec/support/temporary_table.rb +24 -0
  54. data/spec/support/test_connections.rb +10 -0
  55. data/sqlpostgres.gemspec +35 -4
  56. data/test/Connection.test.rb +7 -5
  57. data/test/Select.test.rb +1 -1
  58. data/test/TestConfig.rb +9 -0
  59. data/test/TestUtil.rb +17 -3
  60. metadata +66 -9
  61. data/test/Translate.test.rb +0 -354
  62. data/test/roundtrip.test.rb +0 -565
@@ -0,0 +1,288 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ module SqlPostgres
4
+
5
+ describe Cursor do
6
+
7
+ CURSOR_DOES_NOT_EXIST = [PG::Error, /cursor.*does not exist/]
8
+
9
+ let(:select_statement) do
10
+ sql = Select.new
11
+ sql.select('i')
12
+ sql.from('table1')
13
+ sql
14
+ end
15
+
16
+ let(:hold) {nil}
17
+ let(:scroll) {nil}
18
+
19
+ def make_cursor
20
+ cursor = Cursor.new('cursor1',
21
+ select_statement,
22
+ {
23
+ :hold => hold,
24
+ :scroll => scroll,
25
+ },
26
+ connection)
27
+ end
28
+
29
+ def make_cursor_in_transaction
30
+ cursor = nil
31
+ Transaction.new(connection) do
32
+ cursor = make_cursor
33
+ end
34
+ cursor
35
+ end
36
+
37
+ let(:cursor) {make_cursor}
38
+
39
+ shared_context 'table for cursor test' do |table_name|
40
+
41
+ include_context('temporary table',
42
+ :table_name => table_name,
43
+ :columns => ['i int'])
44
+
45
+ before(:each) do
46
+ 5.times do |i|
47
+ sql = Insert.new(table_name, connection)
48
+ sql.insert('i', i)
49
+ sql.exec
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ shared_context 'table1 for cursor test' do
56
+ include_context 'table for cursor test', 'table1'
57
+ end
58
+
59
+ shared_context 'table2 for cursor test' do
60
+ include_context 'table for cursor test', 'table2'
61
+ end
62
+
63
+ describe 'scroll' do
64
+
65
+ include_context 'table1 for cursor test'
66
+ include_context 'table2 for cursor test'
67
+
68
+ let(:select_statement) do
69
+ sql = Select.new
70
+ sql.select('i')
71
+ sql.from('table1')
72
+ sql.join_using('inner', 'table2', 'i')
73
+ sql.where('i % 2 = 0')
74
+ sql
75
+ end
76
+
77
+ shared_examples_for 'is a scroll cursor' do
78
+ test_connection do |test_connection|
79
+ let(:connection) {test_connection}
80
+ specify do
81
+ Transaction.new(connection) do
82
+ cursor.fetch.should == [{'i' => 0}]
83
+ cursor.fetch.should == [{'i' => 2}]
84
+ cursor.fetch('PRIOR').should == [{'i' => 0}]
85
+ cursor.fetch('PRIOR').should == []
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ shared_examples_for 'is a no scroll cursor' do
92
+ test_connection do |test_context|
93
+ let(:connection) {test_connection}
94
+ specify do
95
+ Transaction.new(connection) do
96
+ expect {
97
+ cursor.fetch('PRIOR')
98
+ }.to raise_error PG::Error, /cursor can only scan forward/
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ context '(on)' do
105
+ let(:scroll) {true}
106
+ it_behaves_like 'is a scroll cursor'
107
+ end
108
+
109
+ context '(off)' do
110
+ let(:scroll) {false}
111
+ it_behaves_like 'is a no scroll cursor'
112
+ end
113
+
114
+ context '(default)' do
115
+ let(:scroll) {nil}
116
+ it_behaves_like 'is a no scroll cursor'
117
+ end
118
+
119
+ end
120
+
121
+ describe 'hold' do
122
+
123
+ include_context 'table1 for cursor test'
124
+
125
+ let(:cursor) {make_cursor_in_transaction}
126
+
127
+ shared_examples 'cursor persists after transaction' do
128
+ test_connection do |test_context|
129
+ let(:connection) {test_connection}
130
+ specify do
131
+ cursor = make_cursor_in_transaction
132
+ cursor.fetch.should == [{'i' => 0}]
133
+ cursor.close
134
+ end
135
+ end
136
+ end
137
+
138
+ shared_examples_for 'cursor closed when transaction ends' do
139
+ test_connection do |test_context|
140
+ let(:connection) {test_connection}
141
+ specify do
142
+ cursor = make_cursor_in_transaction
143
+ expect {
144
+ cursor.fetch
145
+ }.to raise_error *CURSOR_DOES_NOT_EXIST
146
+ end
147
+ end
148
+ end
149
+
150
+ context '(on)' do
151
+ let(:hold) {true}
152
+ it_behaves_like 'cursor persists after transaction'
153
+ end
154
+
155
+ context '(off)' do
156
+ let(:hold) {false}
157
+ it_behaves_like 'cursor closed when transaction ends'
158
+ end
159
+
160
+ context '(default)' do
161
+ let(:hold) {nil}
162
+ it_behaves_like 'cursor closed when transaction ends'
163
+ end
164
+
165
+ end
166
+
167
+ describe '#initialize' do
168
+
169
+ context '(taking block)' do
170
+
171
+ test_connection do |test_context|
172
+ let(:connection) {test_connection}
173
+
174
+ include_context 'table1 for cursor test', test_connection
175
+
176
+ specify do
177
+ Transaction.new(connection) do
178
+ Cursor.new('cursor1',
179
+ select_statement,
180
+ {},
181
+ connection) do |cursor|
182
+ cursor.fetch.should == [{'i' => 0}]
183
+ end
184
+ end
185
+
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
194
+ describe '#close' do
195
+
196
+ include_context 'table1 for cursor test'
197
+
198
+ test_connections.each do |test_context, test_connection|
199
+ context test_context do
200
+ let(:connection) {test_connection}
201
+
202
+ it 'cannot be used after being closed' do
203
+ Transaction.new(connection) do
204
+ cursor = make_cursor
205
+ cursor.close
206
+ expect {
207
+ cursor.fetch
208
+ }.to raise_error *CURSOR_DOES_NOT_EXIST
209
+ end
210
+ end
211
+
212
+ end
213
+ end
214
+
215
+
216
+ end
217
+
218
+ describe '#move' do
219
+
220
+ test_connections.each do |test_context, test_connection|
221
+ context test_context do
222
+ let(:connection) {test_connection}
223
+
224
+ include_context 'table1 for cursor test', test_connection
225
+
226
+ specify do
227
+ Transaction.new(connection) do
228
+ cursor = make_cursor
229
+ cursor.move('absolute 2')
230
+ cursor.fetch.should == [{'i' => 2}]
231
+ end
232
+ end
233
+
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ describe '#fetch' do
240
+
241
+ context '(default)' do
242
+
243
+ test_connection do |test_context|
244
+ let(:connection) {test_connection}
245
+
246
+ include_context 'table1 for cursor test', test_connection
247
+
248
+ specify do
249
+ Transaction.new(connection) do
250
+ cursor.fetch.should == [{'i' => 0}]
251
+ cursor.fetch.should == [{'i' => 1}]
252
+ cursor.fetch.should == [{'i' => 2}]
253
+ cursor.fetch.should == [{'i' => 3}]
254
+ cursor.fetch.should == [{'i' => 4}]
255
+ cursor.fetch.should == []
256
+ end
257
+ end
258
+
259
+ end
260
+
261
+ end
262
+
263
+ context '(with count)' do
264
+
265
+ test_connection do |test_context|
266
+ let(:connection) {test_connection}
267
+
268
+ include_context 'table1 for cursor test', test_connection
269
+
270
+ specify do
271
+ Transaction.new(connection) do
272
+ cursor = Cursor.new('cursor1', select_statement, {}, connection)
273
+ cursor.fetch(2).should == [{'i' => 0}, {'i' => 1}]
274
+ cursor.fetch(2).should == [{'i' => 2}, {'i' => 3}]
275
+ cursor.fetch(2).should == [{'i' => 4}]
276
+ cursor.fetch.should == []
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ end
283
+
284
+ end
285
+
286
+ end
287
+
288
+ end
@@ -0,0 +1,33 @@
1
+ require 'forwardable'
2
+
3
+ module TestSupport
4
+ class DatabaseConfig
5
+
6
+ extend Forwardable
7
+
8
+ def initialize
9
+ @config = load_config
10
+ end
11
+
12
+ def_delegator :@config, :map
13
+
14
+ private
15
+
16
+ PATH = File.expand_path('../config/database.yml',
17
+ File.dirname(__FILE__))
18
+ TEMPLATE_PATH = PATH + '.template'
19
+
20
+ def load_config
21
+ YAML.load_file(PATH)
22
+ rescue Errno::ENOENT
23
+ print_config_instructions
24
+ raise 'Missing database config'
25
+ end
26
+
27
+ def print_config_instructions
28
+ puts "Missing config at #{PATH}"
29
+ puts "Please create it by copying and editing #{TEMPLATE_PATH}"
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path('postgres_template', File.dirname(__FILE__))
2
+ require File.expand_path('test_database', File.dirname(__FILE__))
3
+
4
+ module TestSupport
5
+ class DatabaseServer
6
+
7
+ attr_reader :test_databases
8
+
9
+ def initialize(name, connection_args, encodings)
10
+ @server_name = name
11
+ @encodings = encodings
12
+ @connection_args = connection_args
13
+ @template = PostgresTemplate.new(@server_name, connection_args)
14
+ @test_databases = test_databases
15
+ end
16
+
17
+ def test_connections
18
+ @test_databases.map(&:test_connections).flatten
19
+ end
20
+
21
+ def drop_databases
22
+ test_databases.each do |test_database|
23
+ test_database.drop(@template)
24
+ end
25
+ end
26
+
27
+ def create_databases
28
+ test_databases.each do |test_database|
29
+ test_database.create(@template)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def test_databases
36
+ @encodings.map do |encoding|
37
+ TestDatabase.new(@server_name, @connection_args, encoding)
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../lib/sqlpostgres', File.dirname(__FILE__))
2
+ require File.expand_path('test_database', File.dirname(__FILE__))
3
+
4
+ module TestSupport
5
+ class PostgresTemplate
6
+
7
+ def initialize(server_name, connection_args)
8
+ @server_name = server_name
9
+ @connection_args = connection_args
10
+ @connection = template_connection
11
+ end
12
+
13
+ def create_database(db_name, database_encoding)
14
+ return if db_exists?(db_name)
15
+ puts "Creating database #{qualified_db_name(db_name)}"
16
+ create_db(db_name, database_encoding)
17
+ end
18
+
19
+ def drop_database(db_name)
20
+ return unless db_exists?(db_name)
21
+ unless db_name =~ /^#{TestDatabase::NAME_PREFIX}/
22
+ raise "Refusing to drop database #{qualified_db_name(db_name)}"
23
+ end
24
+ puts "Dropping database #{qualified_db_name(db_name)}"
25
+ @connection.exec("drop database #{db_name}")
26
+ end
27
+
28
+ private
29
+
30
+ def qualified_db_name(db_name)
31
+ [@server_name, db_name].join('/')
32
+ end
33
+
34
+ def db_exists?(db_name)
35
+ sql = SqlPostgres::Select.new(@connection)
36
+ sql.select_literal(1)
37
+ sql.from('pg_database')
38
+ sql.where(['datname = %s', db_name])
39
+ !sql.exec.empty?
40
+ end
41
+
42
+ def create_db(db_name, database_encoding)
43
+ statement = [
44
+ 'create database', db_name,
45
+ "with encoding '#{database_encoding}'",
46
+ 'template template0',
47
+ ].join(' ')
48
+ @connection.exec(statement)
49
+ end
50
+
51
+ def template_connection
52
+ SqlPostgres::Connection.new(template_connection_args)
53
+ end
54
+
55
+ def template_connection_args
56
+ @connection_args.merge(:db_name => 'template1')
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,55 @@
1
+ require 'memoizer'
2
+ require 'singleton'
3
+ require File.expand_path('database_config', File.dirname(__FILE__))
4
+ require File.expand_path('database_server', File.dirname(__FILE__))
5
+ require File.expand_path('test_config', File.dirname(__FILE__))
6
+
7
+ module TestSupport
8
+ class TargetDatabaseServers
9
+
10
+ include Memoizer
11
+ include Singleton
12
+
13
+ def test_connections
14
+ database_servers.map(&:test_connections).flatten.map do |test_connection|
15
+ [test_connection.context, test_connection.connection]
16
+ end
17
+ end
18
+ memoize :test_connections
19
+
20
+ def test_connection
21
+ test_connections.last
22
+ end
23
+
24
+ def create_databases
25
+ database_servers.each(&:create_databases)
26
+ end
27
+
28
+ def drop_databases
29
+ database_servers.each(&:drop_databases)
30
+ end
31
+
32
+ private
33
+
34
+ def database_servers
35
+ database_config.map do |config_name, config|
36
+ DatabaseServer.new(config_name, config, encodings)
37
+ end
38
+ end
39
+
40
+ def encodings
41
+ test_config['encodings']
42
+ end
43
+
44
+ def test_config
45
+ TestConfig.new
46
+ end
47
+ memoize :test_config
48
+
49
+ def database_config
50
+ DatabaseConfig.new
51
+ end
52
+ memoize :database_config
53
+
54
+ end
55
+ end