og 0.27.0 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/ProjectInfo +2 -2
  2. data/README +8 -4
  3. data/Rakefile +1 -1
  4. data/doc/AUTHORS +1 -1
  5. data/doc/RELEASES +81 -0
  6. data/examples/README +7 -0
  7. data/lib/glue/cacheable.rb +152 -0
  8. data/lib/glue/hierarchical.rb +5 -4
  9. data/lib/glue/optimistic_locking.rb +0 -1
  10. data/lib/glue/orderable.rb +46 -44
  11. data/lib/glue/taggable.rb +7 -4
  12. data/lib/glue/timestamped.rb +1 -1
  13. data/lib/og.rb +13 -6
  14. data/lib/og/entity.rb +226 -9
  15. data/lib/og/evolution.rb +2 -2
  16. data/lib/og/ez/clause.rb +147 -0
  17. data/lib/og/ez/condition.rb +181 -0
  18. data/lib/og/manager.rb +31 -30
  19. data/lib/og/relation.rb +5 -5
  20. data/lib/og/relation/has_many.rb +3 -1
  21. data/lib/og/relation/joins_many.rb +1 -1
  22. data/lib/og/store.rb +6 -3
  23. data/lib/og/store/kirby.rb +3 -5
  24. data/lib/og/store/mysql.rb +0 -1
  25. data/lib/og/store/sql.rb +43 -7
  26. data/lib/og/store/sqlite.rb +97 -11
  27. data/lib/og/store/sqlite2.rb +231 -0
  28. data/lib/og/test/testcase.rb +1 -1
  29. data/lib/og/vendor/mysql.rb +103 -25
  30. data/test/glue/tc_revisable.rb +11 -11
  31. data/test/og/CONFIG.rb +20 -8
  32. data/test/og/mixin/tc_hierarchical.rb +5 -3
  33. data/test/og/mixin/tc_optimistic_locking.rb +6 -4
  34. data/test/og/mixin/tc_orderable.rb +22 -22
  35. data/test/og/mixin/tc_taggable.rb +15 -11
  36. data/test/og/mixin/tc_timestamped.rb +4 -2
  37. data/test/og/multi_validations_model.rb +8 -0
  38. data/test/og/store/tc_filesys.rb +15 -12
  39. data/test/og/store/tc_kirby.rb +14 -11
  40. data/test/og/tc_accumulator.rb +1 -3
  41. data/test/og/tc_cacheable.rb +58 -0
  42. data/test/og/tc_delete_all.rb +13 -16
  43. data/test/og/tc_ez.rb +33 -0
  44. data/test/og/tc_finder.rb +2 -4
  45. data/test/og/tc_inheritance.rb +3 -3
  46. data/test/og/tc_inheritance2.rb +2 -3
  47. data/test/og/tc_join.rb +3 -2
  48. data/test/og/tc_multi_validations.rb +3 -3
  49. data/test/og/tc_multiple.rb +3 -6
  50. data/test/og/tc_override.rb +19 -13
  51. data/test/og/tc_polymorphic.rb +1 -3
  52. data/test/og/tc_resolve.rb +32 -0
  53. data/test/og/tc_reverse.rb +27 -28
  54. data/test/og/tc_scoped.rb +2 -4
  55. data/test/og/tc_select.rb +1 -3
  56. data/test/og/tc_store.rb +3 -8
  57. data/test/og/tc_validation.rb +2 -2
  58. data/test/og/tc_validation2.rb +56 -58
  59. data/test/og/tc_validation_loop.rb +2 -5
  60. metadata +15 -7
  61. data/lib/og/vendor/mysql411.rb +0 -306
@@ -6,6 +6,7 @@ rescue Object => ex
6
6
  end
7
7
 
8
8
  require 'fileutils'
9
+ require 'set'
9
10
 
10
11
  require 'og/store/sql'
11
12
 
@@ -117,12 +118,46 @@ class SqliteStore < SqlStore
117
118
  @conn.changes
118
119
  end
119
120
 
121
+ def list_tables
122
+ rset = @conn.query("select name from sqlite_master where type = 'table'")
123
+ rset.map { |r| r[0] }
124
+ end
125
+
126
+ def list_fields(table)
127
+ rset = @conn.query("pragma table_info(#{table})")
128
+ columns = rset.columns
129
+ rows = rset.entries
130
+ idx = columns.index('name')
131
+ rows.map { |r| r[idx] }
132
+ end
133
+
120
134
  private
121
135
 
122
- def create_table(klass)
136
+ def property_to_field(klass, p)
137
+ field = []
138
+
139
+ klass.index(p.symbol) if p.index
140
+
141
+ field = field_for_property(p)
142
+
143
+ if p.sql
144
+ field << " #{p.sql}"
145
+ else
146
+ field << " #{type_for_class(p.klass)}"
147
+ field << " UNIQUE" if p.unique
148
+ field << " DEFAULT #{p.default.inspect} NOT NULL" if p.default
149
+ field << " #{p.extra_sql}" if p.extra_sql
150
+ end
151
+
152
+ field
153
+ end
154
+
155
+ def create_table_ddl(klass, type = nil, table_name = nil)
123
156
  fields = fields_for_class(klass)
157
+ type ||= ''
158
+ table_name ||= klass::OGTABLE
124
159
 
125
- sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
160
+ sql = "CREATE #{type} TABLE #{table_name} (#{fields.join(', ')}"
126
161
 
127
162
  # Create table constraints.
128
163
 
@@ -130,7 +165,7 @@ private
130
165
  sql << ", #{constraints.join(', ')}"
131
166
  end
132
167
 
133
- sql << ");"
168
+ sql << ")"
134
169
 
135
170
  # Create indices.
136
171
 
@@ -144,16 +179,67 @@ private
144
179
  end
145
180
  end
146
181
 
147
- begin
182
+ sql
183
+ end
184
+
185
+ def create_table(klass)
186
+ unless list_tables.include?(klass::OGTABLE)
187
+ sql = create_table_ddl(klass)
188
+
148
189
  @conn.query(sql).close
149
190
  Logger.info "Created table '#{klass::OGTABLE}'."
150
- rescue Object => ex
151
- # gmosx: any idea how to better test this?
152
- if ex.to_s =~ /table .* already exists/i
153
- Logger.debug 'Table already exists' if $DBG
154
- return
155
- else
156
- raise
191
+ else
192
+ Logger.debug "Table '#{klass::OGTABLE}' already exists" if $DBG
193
+
194
+ actual_fields = Set.new(list_fields(klass::OGTABLE))
195
+ defined_fields = Set.new(klass.properties.values.map { |f| field_for_property(f) } )
196
+
197
+ unless defined_fields == actual_fields
198
+ Logger.debug "Field mismatch in '#{klass::OGTABLE}'. Attempting to correct..."
199
+
200
+ fields_to_add = defined_fields - actual_fields
201
+ fields_to_delete = actual_fields - defined_fields
202
+
203
+ fields_to_add.each do |field|
204
+ if @options[:evolve_schema] == true
205
+ Logger.debug "Adding field '#{field}' to '#{klass::OGTABLE}'"
206
+ sql = "ALTER TABLE #{klass::OGTABLE} ADD COLUMN #{property_to_field(klass, klass.properties[field.to_sym])}"
207
+ @conn.query(sql)
208
+ @conn.query("VACUUM #{klass::OGTABLE}")
209
+ else
210
+ Logger.warn "Table '#{klass::OGTABLE} is missing field '#{field}' and :evolve_schema is not set to true or :cautious!"
211
+ end
212
+ end
213
+
214
+ # Sqlite3 does _NOT_ support removing columns via alter table. You have to drop the table.
215
+
216
+ unless fields_to_delete.empty?
217
+ if (@options[:evolve_schema] == true) and (@options[:evolve_schema_cautious] == false)
218
+ Logger.warn "Removing obsolete fields '#{fields_to_delete.to_a.join(', ')}' from '#{klass::OGTABLE}'!"
219
+
220
+ field_list = klass.properties.values.map { |f| field_for_property(f) }.join(', ')
221
+ backup_table = "#{klass::OGTABLE}_backup"
222
+
223
+ sql_transaction = [
224
+ "BEGIN TRANSACTION",
225
+ "#{create_table_ddl(klass, 'temporary', backup_table)}",
226
+ "INSERT INTO #{backup_table} SELECT #{field_list} FROM #{klass::OGTABLE}",
227
+ "DROP TABLE #{klass::OGTABLE}",
228
+ "#{create_table_ddl(klass)}",
229
+ "INSERT INTO #{klass::OGTABLE} SELECT #{field_list} FROM #{backup_table}",
230
+ "DROP TABLE #{backup_table}",
231
+ "COMMIT"
232
+ ]
233
+
234
+ sql_transaction.each do |s|
235
+ Logger.debug s if $DBG
236
+ @conn.query(s)
237
+ end
238
+ else
239
+ Logger.warn "Table '#{klass::OGTABLE}' contains obsolete fields '#{fields_to_delete.to_a.join(', ')}' " +
240
+ "and :evolve_schema is not set to true or is in cautious mode!"
241
+ end
242
+ end
157
243
  end
158
244
  end
159
245
 
@@ -0,0 +1,231 @@
1
+ begin
2
+ require 'sqlite'
3
+ rescue Object => ex
4
+ Logger.error 'Ruby-Sqlite bindings are not installed!'
5
+ Logger.error ex
6
+ end
7
+
8
+ require 'fileutils'
9
+
10
+ require 'og/store/sql'
11
+
12
+ #--
13
+ # Customize the standard Sqlite2 resultset to make
14
+ # more compatible with Og.
15
+ #++
16
+
17
+ class SQLite::ResultSet # :nodoc: all
18
+ alias_method :blank?, :eof?
19
+
20
+ def each_row
21
+ each do |row|
22
+ yield(row, 0)
23
+ end
24
+ end
25
+
26
+ def first_value
27
+ val = self.next[0]
28
+ close
29
+ return val
30
+ end
31
+
32
+ alias_method :fields, :columns
33
+ end
34
+
35
+ module Og
36
+
37
+ # A Store that persists objects into an Sqlite2 database.
38
+ # To read documentation about the methods, consult the documentation
39
+ # for SqlStore and Store.
40
+
41
+ class Sqlite2Store < SqlStore
42
+
43
+ # Override if needed.
44
+
45
+ def self.db_filename(options)
46
+ options[:name] ||= 'data'
47
+ "#{options[:name]}.db"
48
+ end
49
+
50
+ def self.destroy(options)
51
+ begin
52
+ FileUtils.rm(db_filename(options))
53
+ super
54
+ rescue Object
55
+ Logger.info "Cannot drop '#{options[:name]}'!"
56
+ end
57
+ end
58
+
59
+ # Initialize the Sqlite store.
60
+ # This store provides a default name.
61
+
62
+ def initialize(options)
63
+ super
64
+ @conn = SQLite::Database.new(self.class.db_filename(options))
65
+ end
66
+
67
+ def close
68
+ @conn.close
69
+ super
70
+ end
71
+
72
+ def enchant(klass, manager)
73
+ if klass.ann.self.primary_key.symbol == :oid
74
+ unless klass.properties.include? :oid
75
+ klass.property :oid, Fixnum, :sql => 'integer PRIMARY KEY'
76
+ end
77
+ end
78
+
79
+ super
80
+ end
81
+
82
+ def query(sql)
83
+ Logger.debug sql if $DBG
84
+ return @conn.query(sql)
85
+ rescue Exception => ex
86
+ handle_sql_exception(ex, sql)
87
+ end
88
+
89
+ def exec(sql)
90
+ Logger.debug sql if $DBG
91
+ @conn.query(sql).close
92
+ rescue => ex
93
+ handle_sql_exception(ex, sql)
94
+ end
95
+
96
+ def start
97
+ @conn.transaction if @transaction_nesting < 1
98
+ @transaction_nesting += 1
99
+ end
100
+
101
+ def commit
102
+ @transaction_nesting -= 1
103
+ @conn.commit if @transaction_nesting < 1
104
+ end
105
+
106
+ def rollback
107
+ @transaction_nesting -= 1
108
+ @conn.rollback if @transaction_nesting < 1
109
+ end
110
+
111
+ def last_insert_rowid
112
+ conn.query("SELECT last_insert_rowid()").first_value.to_i
113
+ end
114
+
115
+ def sql_update(sql)
116
+ exec(sql)
117
+ @conn.changes
118
+ end
119
+
120
+ private
121
+
122
+ def create_table(klass)
123
+ fields = fields_for_class(klass)
124
+
125
+ sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
126
+
127
+ # Create table constraints.
128
+
129
+ if constraints = klass.ann.self[:sql_constraint]
130
+ sql << ", #{constraints.join(', ')}"
131
+ end
132
+
133
+ sql << ");"
134
+
135
+ # Create indices.
136
+
137
+ if indices = klass.ann.self[:index]
138
+ for data in indices
139
+ idx, options = *data
140
+ idx = idx.to_s
141
+ pre_sql, post_sql = options[:pre], options[:post]
142
+ idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
143
+ sql << " CREATE #{pre_sql} INDEX #{klass::OGTABLE}_#{idxname}_idx #{post_sql} ON #{klass::OGTABLE} (#{idx});"
144
+ end
145
+ end
146
+
147
+ begin
148
+ @conn.query(sql).close
149
+ Logger.info "Created table '#{klass::OGTABLE}'."
150
+ rescue Object => ex
151
+ # gmosx: any idea how to better test this?
152
+ if ex.to_s =~ /table .* already exists/i
153
+ Logger.debug 'Table already exists' if $DBG
154
+ return
155
+ else
156
+ raise
157
+ end
158
+ end
159
+
160
+ # Create join tables if needed. Join tables are used in
161
+ # 'many_to_many' relations.
162
+
163
+ if join_tables = klass.ann.self[:join_tables]
164
+ for info in join_tables
165
+ begin
166
+ create_join_table_sql(info).each do |sql|
167
+ @conn.query(sql).close
168
+ end
169
+ Logger.debug "Created jointable '#{info[:table]}'."
170
+ rescue Object => ex
171
+ # gmosx: any idea how to better test this?
172
+ if ex.to_s =~ /table .* already exists/i
173
+ Logger.debug 'Join table already exists' if $DBG
174
+ else
175
+ raise
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ def create_field_map(klass)
183
+ res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
184
+ map = {}
185
+
186
+ fields = res.columns
187
+
188
+ res.fields.size.times do |i|
189
+ map[fields[i].intern] = i
190
+ end
191
+
192
+ return map
193
+ ensure
194
+ res.close if res
195
+ end
196
+
197
+ def eval_og_insert(klass)
198
+ pk = klass.primary_key.symbol
199
+ props = klass.properties.values.dup
200
+ values = props.collect { |p| write_prop(p) }.join(',')
201
+
202
+ if klass.schema_inheritance?
203
+ props << Property.new(:symbol => :ogtype, :klass => String)
204
+ values << ", '#{klass}'"
205
+ end
206
+
207
+ sql = "INSERT INTO #{klass::OGTABLE} (#{props.collect {|p| field_for_property(p)}.join(',')}) VALUES (#{values})"
208
+
209
+ klass.class_eval %{
210
+ def og_insert(store)
211
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :pre) if klass.respond_to?(:advices)}
212
+ store.query("#{sql}").close
213
+ @#{pk} = store.last_insert_rowid
214
+ #{Glue::Aspects.gen_advice_code(:og_insert, klass.advices, :post) if klass.respond_to?(:advices)}
215
+ end
216
+ }
217
+ end
218
+
219
+ end
220
+
221
+ end
222
+
223
+ # * George Moschovitis <gm@navel.gr>
224
+ # * Ghislain Mary
225
+ # * Mitchell Foral
226
+
227
+
228
+
229
+
230
+
231
+
@@ -1,7 +1,7 @@
1
1
  require 'test/unit'
2
2
  require 'test/unit/assertions'
3
3
 
4
- require 'mega/orm_support'
4
+ require 'facet/ormsupport'
5
5
 
6
6
  require 'glue'
7
7
  require 'glue/fixture'
@@ -1,14 +1,15 @@
1
- # $Id: mysql.rb,v 1.1 2004/02/24 15:42:29 webster132 Exp $
1
+ # $Id: mysql.rb,v 1.24 2005/02/12 11:37:15 tommy Exp $
2
2
  #
3
- # Copyright (C) 2003 TOMITA Masahiro
3
+ # Copyright (C) 2003-2005 TOMITA Masahiro
4
4
  # tommy@tmtm.org
5
5
  #
6
6
 
7
- class Mysql # :nodoc: all
7
+ class Mysql
8
8
 
9
- VERSION = "4.0-ruby-0.2.4"
9
+ VERSION = "4.0-ruby-0.2.5"
10
10
 
11
11
  require "socket"
12
+ require "digest/sha1"
12
13
 
13
14
  MAX_PACKET_LENGTH = 256*256*256-1
14
15
  MAX_ALLOWED_PACKET = 1024*1024*1024
@@ -51,11 +52,15 @@ class Mysql # :nodoc: all
51
52
  CLIENT_ODBC = 1 << 6
52
53
  CLIENT_LOCAL_FILES = 1 << 7
53
54
  CLIENT_IGNORE_SPACE = 1 << 8
55
+ CLIENT_PROTOCOL_41 = 1 << 9
54
56
  CLIENT_INTERACTIVE = 1 << 10
55
57
  CLIENT_SSL = 1 << 11
56
58
  CLIENT_IGNORE_SIGPIPE = 1 << 12
57
59
  CLIENT_TRANSACTIONS = 1 << 13
60
+ CLIENT_RESERVED = 1 << 14
61
+ CLIENT_SECURE_CONNECTION = 1 << 15
58
62
  CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
63
+ PROTO_AUTH41 = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION
59
64
 
60
65
  # Connection Option
61
66
  OPT_CONNECT_TIMEOUT = 0
@@ -115,19 +120,35 @@ class Mysql # :nodoc: all
115
120
  @server_capabilities, = a.slice!(0,2).unpack("v")
116
121
  end
117
122
  if a.size >= 16 then
118
- @server_language, @server_status = a.unpack("cv")
123
+ @server_language, @server_status = a.slice!(0,3).unpack("cv")
119
124
  end
120
125
 
121
126
  flag = 0 if flag == nil
122
127
  flag |= @client_flag | CLIENT_CAPABILITIES
123
128
  flag |= CLIENT_CONNECT_WITH_DB if db
124
- data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+(user||"")+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)
125
- if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
126
- data << "\0"+db
129
+
130
+ @pre_411 = (0 == @server_capabilities & PROTO_AUTH41)
131
+ if @pre_411
132
+ data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+
133
+ (user||"")+"\0"+
134
+ scramble(passwd, @scramble_buff, @protocol_version==9)
135
+ else
136
+ dummy, @salt2 = a.unpack("a13a12")
137
+ @scramble_buff += @salt2
138
+ flag |= PROTO_AUTH41
139
+ data = Net::int4str(flag) + Net::int4str(@max_allowed_packet) +
140
+ ([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+
141
+ scramble41(passwd, @scramble_buff)
142
+ end
143
+
144
+ if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0
145
+ data << "\0" if @pre_411
146
+ data << db
127
147
  @db = db.dup
128
148
  end
129
149
  write data
130
150
  read
151
+ ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))
131
152
  self
132
153
  end
133
154
  alias :connect :real_connect
@@ -182,7 +203,11 @@ class Mysql # :nodoc: all
182
203
  end
183
204
 
184
205
  def change_user(user="", passwd="", db="")
185
- data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
206
+ if @pre_411
207
+ data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
208
+ else
209
+ data = user+"\0"+scramble41(passwd, @scramble_buff)+db
210
+ end
186
211
  command COM_CHANGE_USER, data
187
212
  @user = user
188
213
  @passwd = passwd
@@ -243,7 +268,11 @@ class Mysql # :nodoc: all
243
268
 
244
269
  def list_fields(table, field=nil)
245
270
  command COM_FIELD_LIST, "#{table}\0#{field}", true
246
- f = read_rows 6
271
+ if @pre_411
272
+ f = read_rows 6
273
+ else
274
+ f = read_rows 7
275
+ end
247
276
  fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
248
277
  res = Result::new self, fields, f.length
249
278
  res.eof = true
@@ -253,7 +282,11 @@ class Mysql # :nodoc: all
253
282
  def list_processes()
254
283
  data = command COM_PROCESS_INFO
255
284
  @field_count = get_length data
256
- fields = read_rows 5
285
+ if @pre_411
286
+ fields = read_rows 5
287
+ else
288
+ fields = read_rows 7
289
+ end
257
290
  @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
258
291
  @status = :STATUS_GET_RESULT
259
292
  store_result
@@ -311,7 +344,11 @@ class Mysql # :nodoc: all
311
344
 
312
345
  def read_one_row(field_count)
313
346
  data = read
314
- return if data[0] == 254 and data.length == 1
347
+ if data[0] == 254 and data.length == 1 ## EOF
348
+ return
349
+ elsif data[0] == 254 and data.length == 5
350
+ return
351
+ end
315
352
  rec = []
316
353
  field_count.times do
317
354
  len = get_length data
@@ -363,7 +400,11 @@ class Mysql # :nodoc: all
363
400
  end
364
401
  else
365
402
  @extra_info = get_length(data, true)
366
- fields = read_rows 5
403
+ if @pre_411
404
+ fields = read_rows(5)
405
+ else
406
+ fields = read_rows(7)
407
+ end
367
408
  @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
368
409
  @status = :STATUS_GET_RESULT
369
410
  end
@@ -373,19 +414,34 @@ class Mysql # :nodoc: all
373
414
  def unpack_fields(data, long_flag_protocol)
374
415
  ret = []
375
416
  data.each do |f|
376
- table = org_table = f[0]
377
- name = f[1]
378
- length = f[2][0]+f[2][1]*256+f[2][2]*256*256
379
- type = f[3][0]
380
- if long_flag_protocol then
381
- flags = f[4][0]+f[4][1]*256
382
- decimals = f[4][2]
417
+ if @pre_411
418
+ table = org_table = f[0]
419
+ name = f[1]
420
+ length = f[2][0]+f[2][1]*256+f[2][2]*256*256
421
+ type = f[3][0]
422
+ if long_flag_protocol then
423
+ flags = f[4][0]+f[4][1]*256
424
+ decimals = f[4][2]
425
+ else
426
+ flags = f[4][0]
427
+ decimals = f[4][1]
428
+ end
429
+ def_value = f[5]
430
+ max_length = 0
383
431
  else
384
- flags = f[4][0]
385
- decimals = f[4][1]
432
+ catalog = f[0]
433
+ db = f[1]
434
+ table = f[2]
435
+ org_table = f[3]
436
+ name = f[4]
437
+ org_name = f[5]
438
+ length = f[6][2]+f[6][3]*256+f[6][4]*256*256
439
+ type = f[6][6]
440
+ flags = f[6][7]+f[6][8]*256
441
+ decimals = f[6][9]
442
+ def_value = ""
443
+ max_length = 0
386
444
  end
387
- def_value = f[5]
388
- max_length = 0
389
445
  ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
390
446
  end
391
447
  ret
@@ -489,6 +545,16 @@ class Mysql # :nodoc: all
489
545
  to.join
490
546
  end
491
547
 
548
+ def scramble41(password, message)
549
+ return 0x00.chr if password.nil? or password.empty?
550
+ buf = [0x14]
551
+ s1 = Digest::SHA1.new(password).digest
552
+ s2 = Digest::SHA1.new(s1).digest
553
+ x = Digest::SHA1.new(message + s2).digest
554
+ (0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
555
+ buf.pack("C*")
556
+ end
557
+
492
558
  def error(errno)
493
559
  @errno = errno
494
560
  @error = Error::err errno
@@ -574,7 +640,6 @@ class Mysql # :nodoc: all
574
640
  def free()
575
641
  @handle.skip_result
576
642
  @handle = @fields = @data = nil
577
- GC::start
578
643
  end
579
644
 
580
645
  def num_fields()
@@ -1022,6 +1087,9 @@ class Mysql # :nodoc: all
1022
1087
  end
1023
1088
  @sock.sync = true
1024
1089
  buf.join
1090
+ rescue
1091
+ errno = Error::CR_SERVER_LOST
1092
+ raise Error::new(errno, Error::err(errno))
1025
1093
  end
1026
1094
 
1027
1095
  def write(data)
@@ -1039,6 +1107,9 @@ class Mysql # :nodoc: all
1039
1107
  @pkt_nr = @pkt_nr + 1 & 0xff
1040
1108
  @sock.sync = true
1041
1109
  @sock.flush
1110
+ rescue
1111
+ errno = Error::CR_SERVER_LOST
1112
+ raise Error::new(errno, Error::err(errno))
1042
1113
  end
1043
1114
 
1044
1115
  def close()
@@ -1085,6 +1156,13 @@ class << Mysql
1085
1156
  end
1086
1157
  alias :connect :real_connect
1087
1158
 
1159
+ def finalizer(net)
1160
+ proc {
1161
+ net.clear
1162
+ net.write Mysql::COM_QUIT.chr
1163
+ }
1164
+ end
1165
+
1088
1166
  def escape_string(str)
1089
1167
  str.gsub(/([\0\n\r\032\'\"\\])/) do
1090
1168
  case $1