og 0.27.0 → 0.28.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 (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