ydbi 0.5.2 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
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