ydbi 0.5.2 → 0.5.7

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 (93) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +35 -0
  3. data/.gitignore +8 -0
  4. data/.travis.yml +15 -0
  5. data/ChangeLog +339 -314
  6. data/Gemfile +5 -0
  7. data/Rakefile +10 -0
  8. data/TODO +44 -0
  9. data/bench/bench.rb +79 -0
  10. data/build/rake_task_lib.rb +186 -0
  11. data/doc/DBD_SPEC.rdoc +88 -0
  12. data/doc/DBI_SPEC.rdoc +157 -0
  13. data/doc/homepage/contact.html +62 -0
  14. data/doc/homepage/development.html +124 -0
  15. data/doc/homepage/index.html +83 -0
  16. data/doc/homepage/ruby-dbi.css +91 -0
  17. data/lib/dbd/Mysql.rb +137 -0
  18. data/lib/dbd/ODBC.rb +89 -0
  19. data/lib/dbd/Pg.rb +188 -0
  20. data/lib/dbd/SQLite.rb +97 -0
  21. data/lib/dbd/SQLite3.rb +124 -0
  22. data/lib/dbd/mysql/database.rb +405 -0
  23. data/lib/dbd/mysql/driver.rb +125 -0
  24. data/lib/dbd/mysql/statement.rb +188 -0
  25. data/lib/dbd/odbc/database.rb +128 -0
  26. data/lib/dbd/odbc/driver.rb +38 -0
  27. data/lib/dbd/odbc/statement.rb +137 -0
  28. data/lib/dbd/pg/database.rb +504 -0
  29. data/lib/dbd/pg/exec.rb +47 -0
  30. data/lib/dbd/pg/statement.rb +160 -0
  31. data/lib/dbd/pg/tuples.rb +121 -0
  32. data/lib/dbd/pg/type.rb +209 -0
  33. data/lib/dbd/sqlite/database.rb +151 -0
  34. data/lib/dbd/sqlite/statement.rb +125 -0
  35. data/lib/dbd/sqlite3/database.rb +201 -0
  36. data/lib/dbd/sqlite3/statement.rb +78 -0
  37. data/lib/dbi.rb +14 -17
  38. data/lib/dbi/utils/date.rb +7 -3
  39. data/lib/dbi/version.rb +1 -1
  40. data/prototypes/types2.rb +237 -0
  41. data/readme.md +15 -0
  42. data/setup.rb +1585 -0
  43. data/test/DBD_TESTS +50 -0
  44. data/test/TESTING +16 -0
  45. data/test/dbd/general/test_database.rb +206 -0
  46. data/test/dbd/general/test_statement.rb +326 -0
  47. data/test/dbd/general/test_types.rb +296 -0
  48. data/test/dbd/mysql/base.rb +26 -0
  49. data/test/dbd/mysql/down.sql +19 -0
  50. data/test/dbd/mysql/test_blob.rb +18 -0
  51. data/test/dbd/mysql/test_new_methods.rb +7 -0
  52. data/test/dbd/mysql/test_patches.rb +111 -0
  53. data/test/dbd/mysql/up.sql +28 -0
  54. data/test/dbd/odbc/base.rb +30 -0
  55. data/test/dbd/odbc/down.sql +19 -0
  56. data/test/dbd/odbc/test_new_methods.rb +12 -0
  57. data/test/dbd/odbc/test_ping.rb +10 -0
  58. data/test/dbd/odbc/test_statement.rb +44 -0
  59. data/test/dbd/odbc/test_transactions.rb +58 -0
  60. data/test/dbd/odbc/up.sql +33 -0
  61. data/test/dbd/postgresql/base.rb +31 -0
  62. data/test/dbd/postgresql/down.sql +31 -0
  63. data/test/dbd/postgresql/test_arrays.rb +179 -0
  64. data/test/dbd/postgresql/test_async.rb +121 -0
  65. data/test/dbd/postgresql/test_blob.rb +36 -0
  66. data/test/dbd/postgresql/test_bytea.rb +87 -0
  67. data/test/dbd/postgresql/test_ping.rb +10 -0
  68. data/test/dbd/postgresql/test_timestamp.rb +77 -0
  69. data/test/dbd/postgresql/test_transactions.rb +58 -0
  70. data/test/dbd/postgresql/testdbipg.rb +307 -0
  71. data/test/dbd/postgresql/up.sql +60 -0
  72. data/test/dbd/sqlite/base.rb +32 -0
  73. data/test/dbd/sqlite/test_database.rb +30 -0
  74. data/test/dbd/sqlite/test_driver.rb +68 -0
  75. data/test/dbd/sqlite/test_statement.rb +112 -0
  76. data/test/dbd/sqlite/up.sql +25 -0
  77. data/test/dbd/sqlite3/base.rb +32 -0
  78. data/test/dbd/sqlite3/test_database.rb +77 -0
  79. data/test/dbd/sqlite3/test_driver.rb +67 -0
  80. data/test/dbd/sqlite3/test_statement.rb +88 -0
  81. data/test/dbd/sqlite3/up.sql +33 -0
  82. data/test/dbi/tc_columninfo.rb +4 -9
  83. data/test/dbi/tc_date.rb +2 -9
  84. data/test/dbi/tc_dbi.rb +3 -9
  85. data/test/dbi/tc_row.rb +17 -23
  86. data/test/dbi/tc_sqlbind.rb +6 -7
  87. data/test/dbi/tc_statementhandle.rb +3 -4
  88. data/test/dbi/tc_time.rb +2 -8
  89. data/test/dbi/tc_timestamp.rb +2 -16
  90. data/test/dbi/tc_types.rb +5 -11
  91. data/test/ts_dbd.rb +131 -0
  92. data/ydbi.gemspec +23 -0
  93. metadata +128 -10
@@ -0,0 +1,78 @@
1
+ #
2
+ # See DBI::BaseStatement.
3
+ #
4
+ class DBI::DBD::SQLite3::Statement < DBI::BaseStatement
5
+ def initialize(sql, db)
6
+ sql.gsub!(/\\\\/) { '\\' } # sqlite underneath does this for us automatically, and it's causing trouble with the rest of the system.
7
+ @sql = sql
8
+ @db = db
9
+ @stmt = db.prepare(sql)
10
+ @result = nil
11
+ rescue ::SQLite3::Exception, RuntimeError => err
12
+ raise DBI::ProgrammingError.new(err.message)
13
+ end
14
+
15
+ #
16
+ # See DBI::BaseStatement#bind_param. This method will also raise
17
+ # DBI::InterfaceError if +param+ is not a Fixnum, to prevent incorrect
18
+ # binding.
19
+ #
20
+ def bind_param(param, value, attribs=nil)
21
+ raise DBI::InterfaceError, "Bound parameter must be an integer" unless param.kind_of? Fixnum
22
+ @stmt.bind_param(param, value)
23
+ end
24
+
25
+ def execute()
26
+ @result = @stmt.execute
27
+ @rows = DBI::SQL.query?(@sql) ? 0 : @db.changes
28
+ end
29
+
30
+ def finish()
31
+ @stmt.close rescue nil
32
+ @result = nil
33
+ end
34
+
35
+ def fetch()
36
+ ret = @result.next
37
+ return ret unless ret
38
+ [ret].flatten
39
+ end
40
+
41
+ def column_info()
42
+ @stmt.columns.zip(@stmt.types).map{|name, type_name|
43
+ m = DBI::DBD::SQLite3.parse_type(type_name)
44
+ h = {
45
+ 'name' => name,
46
+ 'type_name' => m[1],
47
+ 'sql_type' =>
48
+ begin
49
+ DBI.const_get('SQL_'+m[1].upcase)
50
+ rescue NameError
51
+ DBI::SQL_OTHER
52
+ end,
53
+ }
54
+ h['precision'] = m[3].to_i if m[3]
55
+ h['scale'] = m[5].to_i if m[5]
56
+
57
+ case h['type_name']
58
+ when 'double'
59
+ h['dbi_type'] = DBI::Type::Float
60
+ end
61
+
62
+ h
63
+ }
64
+ end
65
+
66
+ def rows()
67
+ @rows
68
+ end
69
+
70
+ def bind_params(*bindvars)
71
+ @stmt.bind_params(bindvars)
72
+ end
73
+
74
+ def cancel()
75
+ @result = nil
76
+ @index = 0
77
+ end
78
+ end
data/lib/dbi.rb CHANGED
@@ -9,16 +9,16 @@ module DBI; end
9
9
  #
10
10
  # Copyright (c) 2001, 2002, 2003 Michael Neumann <mneumann@ntecs.de>
11
11
  # Copyright (c) 2008 Erik Hollensbe <erik@hollensbe.org>
12
- #
12
+ #
13
13
  # All rights reserved.
14
14
  #
15
- # Redistribution and use in source and binary forms, with or without
16
- # modification, are permitted provided that the following conditions
15
+ # Redistribution and use in source and binary forms, with or without
16
+ # modification, are permitted provided that the following conditions
17
17
  # are met:
18
- # 1. Redistributions of source code must retain the above copyright
18
+ # 1. Redistributions of source code must retain the above copyright
19
19
  # notice, this list of conditions and the following disclaimer.
20
- # 2. Redistributions in binary form must reproduce the above copyright
21
- # notice, this list of conditions and the following disclaimer in the
20
+ # 2. Redistributions in binary form must reproduce the above copyright
21
+ # notice, this list of conditions and the following disclaimer in the
22
22
  # documentation and/or other materials provided with the distribution.
23
23
  # 3. The name of the author may not be used to endorse or promote products
24
24
  # derived from this software without specific prior written permission.
@@ -57,6 +57,7 @@ require 'dbi/exceptions'
57
57
  require 'dbi/binary'
58
58
  require 'dbi/handles'
59
59
  require 'dbi/base_classes'
60
+ require 'dbi/version'
60
61
  require "date"
61
62
  require "thread"
62
63
  require 'monitor'
@@ -127,7 +128,7 @@ module DBI
127
128
 
128
129
  class << self
129
130
 
130
- # Establish a database connection.
131
+ # Establish a database connection.
131
132
  #
132
133
  # Format goes as such: "dbi:Driver:database_conn_args"
133
134
  #
@@ -178,7 +179,7 @@ module DBI
178
179
  # Return a list (of String) of the available drivers.
179
180
  #
180
181
  # NOTE:: This is non-functional for gem installations, due to the
181
- # nature of how it currently works. A better solution for
182
+ # nature of how it currently works. A better solution for
182
183
  # this will be provided in DBI 0.6.0.
183
184
  def collect_drivers
184
185
  drivers = { }
@@ -203,7 +204,7 @@ module DBI
203
204
  drivers = []
204
205
  collect_drivers.each do |key, value|
205
206
  drivers.push("dbi:#{key}:")
206
- end
207
+ end
207
208
  return drivers
208
209
  end
209
210
 
@@ -251,7 +252,7 @@ module DBI
251
252
  rescue LoadError => e1
252
253
  # see if you can find it in the path
253
254
  unless @@caseless_driver_name_map
254
- @@caseless_driver_name_map = { }
255
+ @@caseless_driver_name_map = { }
255
256
  collect_drivers.each do |key, value|
256
257
  @@caseless_driver_name_map[key.downcase] = value
257
258
  end
@@ -305,17 +306,13 @@ module DBI
305
306
  # FIXME trace
306
307
  # drh.trace(@@trace_mode, @@trace_output)
307
308
  @@driver_map[driver_name] = [drh, dbd_dr]
308
- return driver_name
309
+ return driver_name
309
310
  else
310
311
  return driver_name
311
312
  end
312
313
  end
313
314
  rescue LoadError, NameError
314
- if $SAFE >= 1
315
- raise InterfaceError, "Could not load driver (#{$!.message}). Note that in SAFE mode >= 1, driver URLs have to be case sensitive!"
316
- else
317
- raise InterfaceError, "Could not load driver (#{$!.message})"
318
- end
315
+ raise InterfaceError, "Could not load driver (#{$!.message})"
319
316
  end
320
317
 
321
318
  # Splits a DBI URL into two components - the database driver name
@@ -326,7 +323,7 @@ module DBI
326
323
  # the proper format for the URL. If it isn't correct, an Interface
327
324
  # error is raised.
328
325
  def parse_url(driver_url)
329
- if driver_url =~ /^(DBI|dbi):([^:]+)(:(.*))$/
326
+ if driver_url =~ /^(DBI|dbi):([^:]+)(:(.*))$/
330
327
  [$2, $4]
331
328
  else
332
329
  raise InterfaceError, "Invalid Data Source Name"
@@ -1,3 +1,7 @@
1
+ require 'deprecated'
2
+ require 'dbi/types'
3
+ require 'dbi/binary'
4
+ require 'dbi/typeutil'
1
5
  module DBI
2
6
  #
3
7
  # Represents a Date.
@@ -30,7 +34,7 @@ module DBI
30
34
  sprintf("%04d-%02d-%02d", @year, @month, @day)
31
35
  end
32
36
 
33
- private
37
+ private
34
38
 
35
39
  # DBI::Date.new(year = 0, month = 0, day = 0)
36
40
  # DBI::Date.new(Date)
@@ -42,10 +46,10 @@ module DBI
42
46
  def initialize(year=0, month=0, day=0)
43
47
  case year
44
48
  when ::Date
45
- @year, @month, @day = year.year, year.month, year.day
49
+ @year, @month, @day = year.year, year.month, year.day
46
50
  @original_date = year
47
51
  when ::Time
48
- @year, @month, @day = year.year, year.month, year.day
52
+ @year, @month, @day = year.year, year.month, year.day
49
53
  @original_time = year
50
54
  else
51
55
  @year, @month, @day = year, month, day
@@ -1,3 +1,3 @@
1
1
  module DBI
2
- VERSION = "0.5.2"
2
+ VERSION = "0.5.7"
3
3
  end
@@ -0,0 +1,237 @@
1
+ require 'date'
2
+ require 'time'
3
+
4
+ #
5
+ # General example, these would be types that would exist in DBI proper
6
+ #
7
+
8
+ module DBI
9
+ class DBI::Type
10
+ def self.parse(obj, type=nil, dbh=nil)
11
+ if type
12
+ sym = ( "to_" + type.to_s ).to_sym
13
+ begin
14
+ return self.__send__(sym, obj)
15
+ rescue ::NoMethodError
16
+ self.to_type(obj)
17
+ end
18
+ else
19
+ return self.to_type(obj)
20
+ end
21
+ end
22
+
23
+ def self.coerce(obj, type=nil, dbh=nil)
24
+ if type
25
+ sym = ( "from_" + type.to_s ).to_sym
26
+ begin
27
+ return self.__send__(sym, obj)
28
+ rescue ::NoMethodError
29
+ self.from_type(obj)
30
+ end
31
+ else
32
+ return self.from_type(obj)
33
+ end
34
+ end
35
+
36
+ def self.from_type(obj)
37
+ obj.to_s rescue obj.to_str rescue obj
38
+ end
39
+
40
+ def self.to_type(obj)
41
+ obj
42
+ end
43
+ end
44
+
45
+ class DBI::Type::Null < DBI::Type
46
+ def self.to_type(obj)
47
+ return obj unless obj
48
+ return nil if obj.to_s.match(/^null$/i)
49
+ return obj
50
+ end
51
+
52
+ def self.from_type(obj)
53
+ obj
54
+ end
55
+ end
56
+
57
+ class DBI::Type::Integer < DBI::Type::Null
58
+ def self.parse(obj)
59
+ obj = super
60
+ return obj unless obj
61
+ return obj.to_i if obj.respond_to? :to_i
62
+ return obj
63
+ end
64
+ end
65
+
66
+ class DBI::Type::Timestamp < DBI::Type::Null
67
+ def self.to_type(obj)
68
+ obj = super
69
+ return obj unless obj
70
+
71
+ case obj
72
+ when ::DateTime
73
+ return obj
74
+ when ::Date
75
+ return ::DateTime.strptime(obj.to_s, "%Y-%m-%d")
76
+ when ::Time
77
+ return ::DateTime.parse(obj.to_s)
78
+ when ::Integer
79
+ return ::DateTime.parse(::Time.at(obj).to_s)
80
+ else
81
+ return ::DateTime.parse(obj.to_s) if obj.respond_to? :to_s
82
+ return ::DateTime.parse(obj.to_str) if obj.respond_to? :to_str
83
+ return obj
84
+ end
85
+ end
86
+
87
+ def self.from_type(obj)
88
+ obj = super
89
+ return obj unless obj
90
+
91
+ case obj
92
+ when ::DateTime
93
+ return obj.to_s # produces ISO8601
94
+ when ::Time
95
+ return obj.iso8601
96
+ when ::Integer
97
+ return ::Time.at(obj).iso8601
98
+ else
99
+ return obj
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ module DBI::DBD
106
+ class Pg
107
+
108
+ #
109
+ # during connect time, after DatabaseHandle initialization, the hash
110
+ # that DatabaseHandle#type_map provides would be tweaked to take
111
+ # advantage of the available date formats.
112
+ #
113
+ # See 'PgDatabaseHandle' below for a mock.
114
+ #
115
+
116
+ class Type
117
+ class Timestamp < DBI::Type::Timestamp
118
+ def self.from_dmy(obj)
119
+ return obj if DBI::Type::Null.parse(obj).nil?
120
+
121
+ case obj
122
+ when ::DateTime, ::Time
123
+ obj.strftime("%d/%m/%Y %H:%M:%S")
124
+ when ::Integer
125
+ ::Time.at(obj).strftime("%d/%m/%Y %H:%M:%S")
126
+ else
127
+ # punt... this will actually try the baseline
128
+ # conversion at this point
129
+ raise "Crap!"
130
+ end
131
+ end
132
+
133
+ def self.to_dmy(obj)
134
+ return obj if DBI::Type::Null.parse(obj).nil?
135
+
136
+ # realistically all there needs to be is a check for the
137
+ # type ruby-pg typically returns and string, but to be
138
+ # complete I'm showing how it could be done if the type was
139
+ # less clear.
140
+
141
+ case obj
142
+ when ::DateTime
143
+ return obj
144
+ when ::Time
145
+ return ::DateTime.parse(obj.to_s)
146
+ else
147
+ return ::DateTime.strptime(obj, "%d/%m/%Y %H:%M:%S")
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ #
156
+ # this is just used to emulate the methods a DatabaseHandle would have to
157
+ # faciliate this.. certainly not a full (or correct) mirroring of the DBI API.
158
+ #
159
+ class DatabaseHandle
160
+
161
+ attr_accessor :columns
162
+
163
+ def outbound_type_map
164
+ {
165
+ 'timestamp' => [DBI::Type::Timestamp]
166
+ }
167
+ end
168
+
169
+ def inbound_type_map
170
+ {
171
+ ::DateTime => [DBI::Type::Timestamp],
172
+ ::Time => [DBI::Type::Timestamp]
173
+ }
174
+ end
175
+
176
+ # humor me while I completely break DBI for the sake of brevity..
177
+ def execute(*bindvars)
178
+ bindvars.collect do |var|
179
+ type_info = inbound_type_map[var.class]
180
+ type_info[0].coerce(var, type_info[1], self)
181
+ end
182
+ end
183
+
184
+ def fetch(*bindvars)
185
+ ret = []
186
+
187
+ bindvars.each_with_index do |var, i|
188
+ type_info = outbound_type_map[columns[i]]
189
+ ret.push type_info[0].parse(var, type_info[1], self)
190
+ end
191
+
192
+ return ret
193
+ end
194
+ end
195
+
196
+ class PgDatabaseHandle < DatabaseHandle
197
+ def outbound_type_map
198
+ {
199
+ 'timestamp' => [DBI::DBD::Pg::Type::Timestamp, :dmy]
200
+ }
201
+ end
202
+
203
+ def inbound_type_map
204
+ {
205
+ ::DateTime => [DBI::DBD::Pg::Type::Timestamp, :dmy],
206
+ ::Time => [DBI::DBD::Pg::Type::Timestamp, :dmy]
207
+ }
208
+ end
209
+ end
210
+
211
+ # ok! now for the functional example:
212
+
213
+ if __FILE__ == $0
214
+
215
+ dbh = DatabaseHandle.new
216
+ dbh.columns = %w(timestamp timestamp)
217
+ # this would go TO the database..
218
+ p dbh.execute(DateTime.now, Time.now)
219
+ # this would come FROM the database...
220
+ p dbh.fetch(Time.now.iso8601, DateTime.now.to_s)
221
+
222
+ # now the Pg example:
223
+ dbh = PgDatabaseHandle.new
224
+ dbh.columns = %w(timestamp timestamp)
225
+
226
+ # this would go TO the database..
227
+ p dbh.execute(DateTime.now, Time.now)
228
+ # this would come FROM the database...
229
+ p dbh.fetch(Time.now.strftime("%d/%m/%Y %H:%M:%S"), DateTime.now.strftime("%d/%m/%Y %H:%M:%S"))
230
+
231
+ # this should fail appropriately
232
+ begin
233
+ dbh.fetch(Time.now.iso8601, DateTime.now.to_s)
234
+ rescue Exception
235
+ puts "this failed like it should"
236
+ end
237
+ end
data/readme.md CHANGED
@@ -1,3 +1,9 @@
1
+ # Release a new ydbi gem
2
+
3
+ * rake ydbd-pg:clobber_package rake ydbd-pg:clobber_package; rake ydbi:gem ydbd-pg:gem
4
+ * gem push ydbd-pg-0.5.4.gem
5
+ * gem push ydbi-0.5.4.gem
6
+
1
7
  # Description
2
8
  The DBI package is a vendor independent interface for accessing databases.
3
9
  It is similar, but not identical to, Perl's DBI module.
@@ -75,6 +81,15 @@
75
81
  gem install dbd-sqlite3
76
82
  gem install dbd-sqlite
77
83
 
84
+ If you have a non standard path of postgres use something like
85
+
86
+ gem install pg -- --with-pg-config=/usr/local/pgsql-10.1/bin/pg_config
87
+
88
+ Or if you are using bundler
89
+
90
+ bundle config build.pg --with-pg-config=/usr/local/pgsql-10.1/bin/pg_config
91
+ bundle install
92
+
78
93
  ## Without rubygems:
79
94
 
80
95
  ruby setup.rb config