sqlpostgres 1.2.6 → 1.3.0

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.
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