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
data/Changelog.md ADDED
@@ -0,0 +1,18 @@
1
+ ### 1.3.0
2
+
3
+ Enhancements
4
+
5
+ * Support Postgresql 9
6
+ * Some tests converted to rspec
7
+ * Connection can set client encoding
8
+ * More adaptable configuration for databases to run tests against
9
+
10
+ ### 1.2.6
11
+
12
+ Bug fixes
13
+
14
+ * DateTime objects store their full precision
15
+
16
+ ### 1.2.4
17
+
18
+ * First public release
data/Gemfile CHANGED
@@ -4,5 +4,7 @@ gem 'pg', '~> 0.13.2'
4
4
 
5
5
  group :development do
6
6
  gem 'jeweler', '~> 1.8.4'
7
+ gem 'memoizer', '~> 1.0.1'
7
8
  gem 'rake', '~> 10.0.3'
9
+ gem 'rspec', '~> 2.12.0'
8
10
  end
data/Gemfile.lock CHANGED
@@ -1,6 +1,7 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ diff-lcs (1.1.3)
4
5
  git (1.2.5)
5
6
  jeweler (1.8.4)
6
7
  bundler (~> 1.0)
@@ -8,15 +9,26 @@ GEM
8
9
  rake
9
10
  rdoc
10
11
  json (1.7.6)
12
+ memoizer (1.0.1)
11
13
  pg (0.13.2)
12
14
  rake (10.0.3)
13
15
  rdoc (3.12)
14
16
  json (~> 1.4)
17
+ rspec (2.12.0)
18
+ rspec-core (~> 2.12.0)
19
+ rspec-expectations (~> 2.12.0)
20
+ rspec-mocks (~> 2.12.0)
21
+ rspec-core (2.12.2)
22
+ rspec-expectations (2.12.1)
23
+ diff-lcs (~> 1.1.3)
24
+ rspec-mocks (2.12.1)
15
25
 
16
26
  PLATFORMS
17
27
  ruby
18
28
 
19
29
  DEPENDENCIES
20
30
  jeweler (~> 1.8.4)
31
+ memoizer (~> 1.0.1)
21
32
  pg (~> 0.13.2)
22
33
  rake (~> 10.0.3)
34
+ rspec (~> 2.12.0)
data/README.rdoc CHANGED
@@ -47,8 +47,7 @@ The tests are known to pass in MRI 1.8.7 and MRI 1.9.3
47
47
 
48
48
  == POSTGRES VERSIONS
49
49
 
50
- This library works with Postgres 1.8. It does not yet fully support
51
- Postgres 1.9.
50
+ This library works with Postgres 1.8 and Postgres 1.9.
52
51
 
53
52
  == ENCODINGS
54
53
 
@@ -57,3 +56,7 @@ This library only works properly with the SQL-ASCII encoding.
57
56
  == WHOAMI
58
57
 
59
58
  Wayne Conrad <wconrad@yagni.com>
59
+
60
+ == CONTRIBUTORS
61
+
62
+ Sam Kellogg <sam@nickstoys.com>
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'rubygems'
4
-
5
4
  require 'bundler'
5
+
6
6
  begin
7
7
  Bundler.setup(:default, :development)
8
8
  rescue Bundler::BundlerError => e
@@ -10,28 +10,9 @@ rescue Bundler::BundlerError => e
10
10
  $stderr.puts 'Run `bundle install` to install missing gems'
11
11
  exit e.status_code
12
12
  end
13
- require 'rake'
14
13
 
15
- require 'jeweler'
16
- Jeweler::Tasks.new do |gem|
17
- # gem is a Gem::Specification... see
18
- # http://docs.rubygems.org/read/chapter/20 for more options
19
- gem.name = 'sqlpostgres'
20
- gem.homepage = 'http://github.com/wconrad/sqlpostgres'
21
- gem.license = 'MIT'
22
- gem.summary = %Q{library for postgresql queries}
23
- gem.description =
24
- ('A mini-language for building and executing SQL statements '\
25
- 'against a postgresql database. This is a very old library, '\
26
- 'pre-dating active record and lacking many of its refinments. '\
27
- 'New projects will probably not want to use it.')
28
- gem.email = 'wconrad@yagni.com'
29
- gem.authors = ['Wayne Conrad']
30
- # dependencies defined in Gemfile
31
- end
32
- Jeweler::RubygemsDotOrgTasks.new
14
+ $:.unshift(File.dirname(__FILE__) + '/lib')
15
+ Dir['rake_tasks/**/*.rake'].sort.each { |path| load path }
33
16
 
34
- desc "Run Tests"
35
- task :test do
36
- system 'test/test'
37
- end
17
+ require File.expand_path('spec/lib/target_database_servers',
18
+ File.dirname(__FILE__))
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.6
1
+ 1.3.0
@@ -83,6 +83,8 @@ module SqlPostgres
83
83
  # to nil.
84
84
  # 'password'::
85
85
  # Password. nil, the default, means no password.
86
+ # 'encoding'::
87
+ # Client encoding.
86
88
  #
87
89
  # To wrap an existing connection, pass this argument:
88
90
  #
@@ -107,12 +109,15 @@ module SqlPostgres
107
109
  tty = args['tty'] || ""
108
110
  login = args['login']
109
111
  password = args['password']
112
+ client_encoding = args['encoding']
110
113
  @pgconn = @@pgClass.connect(hostName, port, options, tty, dbName,
111
114
  login, password)
115
+ if client_encoding
116
+ @pgconn.set_client_encoding(client_encoding)
117
+ end
112
118
  end
113
119
  @statement_in_exception = args['statement_in_exception']
114
120
  @statement_in_exception = true if @statement_in_exception.nil?
115
- @pgconn.set_client_encoding("unicode")
116
121
  end
117
122
 
118
123
  # close the connection. If it's already closed, do nothing.
@@ -177,8 +182,8 @@ module SqlPostgres
177
182
  unless column.converter.nil?
178
183
  typeCode = pgresult.ftype(i)
179
184
  value = row[i]
180
- args = [value]
181
- args << typeCode if column.converter.arity == 2
185
+ args = [value, @pgconn]
186
+ args << typeCode if column.converter.arity == 3
182
187
  hash[column.as || column.value] =
183
188
  value && column.converter.call(*args)
184
189
  end
@@ -101,8 +101,8 @@ module SqlPostgres
101
101
  @connection.exec(statement)
102
102
  end
103
103
 
104
- # Close the cursor. Once closed, it may closed or fetched from
105
- # again.
104
+ # Close the cursor. Once closed, it may not be closed or fetched
105
+ # from again.
106
106
 
107
107
  def close
108
108
  statement = "close #{@name}"
@@ -140,7 +140,7 @@ module SqlPostgres
140
140
 
141
141
  def insert_bytea(column, value = :no_value)
142
142
  @columns << column
143
- @values << Translate.escape_bytea(value) unless value == :no_value
143
+ @values << Translate.escape_bytea(value, @connection.pgconn) unless value == :no_value
144
144
  end
145
145
 
146
146
  # Insert into a bytea[] (bytea array) column. You must use this
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgTwoPoints'
1
+ require File.expand_path('PgTwoPoints', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgWrapper'
1
+ require File.expand_path('PgWrapper', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgWrapper'
1
+ require File.expand_path('PgWrapper', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgTwoPoints'
1
+ require File.expand_path('PgTwoPoints', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgWrapper'
1
+ require File.expand_path('PgWrapper', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), "PgType")
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -1,4 +1,4 @@
1
- require 'sqlpostgres/PgType'
1
+ require File.expand_path('PgType', File.dirname(__FILE__))
2
2
 
3
3
  module SqlPostgres
4
4
 
@@ -609,29 +609,29 @@ module SqlPostgres
609
609
 
610
610
  # Converters used to translate strings into Ruby types.
611
611
 
612
- BitConverter = proc { |s| PgBit.from_sql(s) }
613
- BooleanConverter = proc { |s| s == 't' }
614
- BoxConverter = proc { |s| PgBox.from_sql(s) }
615
- ByteaConverter = proc { |s| Translate.unescape_bytea(s) }
616
- CidrConverter = proc { |s| PgCidr.from_sql(s) }
617
- CircleConverter = proc { |s| PgCircle.from_sql(s) }
618
- DateConverter = proc { |s| Translate.sql_to_date(s) }
619
- FloatConverter = proc { |s| s.to_f }
620
- InetConverter = proc { |s| PgInet.from_sql(s) }
621
- IntegerConverter = proc { |s| s.to_i }
622
- IntervalConverter = proc { |s| PgInterval.from_sql(s) }
623
- LsegConverter = proc { |s| PgLineSegment.from_sql(s) }
624
- MacAddrConverter = proc { |s| PgMacAddr.from_sql(s) }
625
- PathConverter = proc { |s| PgPath.from_sql(s) }
626
- PointConverter = proc { |s| PgPoint.from_sql(s) }
627
- PolygonConverter = proc { |s| PgPolygon.from_sql(s) }
628
- QCharConverter = proc { |s| Translate.unescape_qchar(s) }
629
- StringConverter = proc { |s| s }
630
- TimeConverter = proc { |s| PgTime.from_sql(s) }
631
- TimeStringConverter = proc { |s| Time.local(*s.split(/:/)) }
632
- TimeWithTimeZoneConverter = proc { |s| PgTimeWithTimeZone.from_sql(s) }
633
- TimestampConverter = proc { |s| PgTimestamp.from_sql(s) }
634
- TimestampTzConverter = proc { |s| Translate.sql_to_datetime(s) }
612
+ BitConverter = proc { |s, server_version| PgBit.from_sql(s) }
613
+ BooleanConverter = proc { |s, server_version| s == 't' }
614
+ BoxConverter = proc { |s, server_version| PgBox.from_sql(s) }
615
+ ByteaConverter = proc { |s, server_version| Translate.unescape_bytea(s, server_version) }
616
+ CidrConverter = proc { |s, server_version| PgCidr.from_sql(s) }
617
+ CircleConverter = proc { |s, server_version| PgCircle.from_sql(s) }
618
+ DateConverter = proc { |s, server_version| Translate.sql_to_date(s) }
619
+ FloatConverter = proc { |s, server_version| s.to_f }
620
+ InetConverter = proc { |s, server_version| PgInet.from_sql(s) }
621
+ IntegerConverter = proc { |s, server_version| s.to_i }
622
+ IntervalConverter = proc { |s, server_version| PgInterval.from_sql(s) }
623
+ LsegConverter = proc { |s, server_version| PgLineSegment.from_sql(s) }
624
+ MacAddrConverter = proc { |s, server_version| PgMacAddr.from_sql(s) }
625
+ PathConverter = proc { |s, server_version| PgPath.from_sql(s) }
626
+ PointConverter = proc { |s, server_version| PgPoint.from_sql(s) }
627
+ PolygonConverter = proc { |s, server_version| PgPolygon.from_sql(s) }
628
+ QCharConverter = proc { |s, server_version| Translate.unescape_qchar(s) }
629
+ StringConverter = proc { |s, server_version| s }
630
+ TimeConverter = proc { |s, server_version| PgTime.from_sql(s) }
631
+ TimeStringConverter = proc { |s, server_version| Time.local(*s.split(/:/)) }
632
+ TimeWithTimeZoneConverter = proc { |s, server_version| PgTimeWithTimeZone.from_sql(s) }
633
+ TimestampConverter = proc { |s, server_version| PgTimestamp.from_sql(s) }
634
+ TimestampTzConverter = proc { |s, server_version| Translate.sql_to_datetime(s) }
635
635
 
636
636
  # Map each base (non-array) type to a converter.
637
637
 
@@ -705,7 +705,7 @@ module SqlPostgres
705
705
  Types::ARRAY_VARCHAR => Types::VARCHAR,
706
706
  }
707
707
 
708
- AutoConverter = proc { |s, type_code|
708
+ AutoConverter = proc { |s, pgconn, type_code|
709
709
  array_element_type = ARRAY_ELEMENT_TYPES[type_code]
710
710
  if !array_element_type.nil?
711
711
  s = Translate.sql_to_array(s)
@@ -713,7 +713,7 @@ module SqlPostgres
713
713
  end
714
714
  converter = CONVERTERS[type_code] || StringConverter
715
715
  Translate.deep_collect(s) do |e|
716
- converter.call(e)
716
+ converter.call(e, pgconn)
717
717
  end
718
718
  }
719
719
 
@@ -270,45 +270,23 @@ module SqlPostgres
270
270
  # \
271
271
  # \x7f-\xff
272
272
 
273
- def escape_bytea(s)
273
+ def escape_bytea(s, pgconn)
274
274
  return "null" if s.nil?
275
275
  return "default" if s == :default
276
- "E'" + PGconn.escape_bytea(Array(s).join) + "'"
276
+ raise s.inspect if s.is_a?(Array) #DEBUG
277
+ value = "'" + pgconn.escape_bytea(s) + "'"
278
+ value = "E" + value if pgconn.server_version < 9_01_00
279
+ value
277
280
  end
278
281
  module_function :escape_bytea
279
282
 
280
- # Unescape octal escape sequences, turning them back into bytes.
281
-
282
- def unescape_octal_escapes(s)
283
- s.gsub(/\\(\d{3})/) do
284
- $1.oct.chr
285
- end.gsub(/\\\\/, '\\')
286
- end
287
- module_function :unescape_octal_escapes
288
-
289
283
  # Unescape a bytea string read from postgres.
290
284
 
291
- def unescape_bytea(s)
292
- if s.respond_to?(:force_encoding)
293
- s = s.force_encoding("ASCII-8BIT")
294
- end
295
- s.gsub(/\\(\\|[0-3][0-7][0-7])/) do
296
- if $1 == "\\"
297
- "\\"
298
- else
299
- $1.oct.chr
300
- end
301
- end
285
+ def unescape_bytea(s, pgconn)
286
+ pgconn.unescape_bytea(s)
302
287
  end
303
288
  module_function :unescape_bytea
304
289
 
305
- # Unescape a text string read from postges.
306
-
307
- def unescape_text(s)
308
- unescape_bytea(s)
309
- end
310
- module_function :unescape_text
311
-
312
290
  # Convert a time to SQL format, including microseconds:
313
291
  # (YYYY-mm-dd HH:MM:SS.uuuuuu)
314
292
 
@@ -99,7 +99,7 @@ module SqlPostgres
99
99
  #**
100
100
 
101
101
  def set_bytea(column, value)
102
- @set_clauses << [column, Translate.escape_bytea(value)].join(' = ')
102
+ @set_clauses << [column, Translate.escape_bytea(value, @connection.pgconn)].join(' = ')
103
103
  end
104
104
 
105
105
  # Set a column to an array.
@@ -0,0 +1,17 @@
1
+ namespace 'test:db' do
2
+
3
+ def target_database_servers
4
+ TestSupport::TargetDatabaseServers.instance
5
+ end
6
+
7
+ desc 'Create test databases'
8
+ task 'create' do
9
+ target_database_servers.create_databases
10
+ end
11
+
12
+ desc 'Drop test databases'
13
+ task 'drop' do
14
+ target_database_servers.drop_databases
15
+ end
16
+
17
+ end