og 0.27.0 → 0.28.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ProjectInfo +2 -2
- data/README +8 -4
- data/Rakefile +1 -1
- data/doc/AUTHORS +1 -1
- data/doc/RELEASES +81 -0
- data/examples/README +7 -0
- data/lib/glue/cacheable.rb +152 -0
- data/lib/glue/hierarchical.rb +5 -4
- data/lib/glue/optimistic_locking.rb +0 -1
- data/lib/glue/orderable.rb +46 -44
- data/lib/glue/taggable.rb +7 -4
- data/lib/glue/timestamped.rb +1 -1
- data/lib/og.rb +13 -6
- data/lib/og/entity.rb +226 -9
- data/lib/og/evolution.rb +2 -2
- data/lib/og/ez/clause.rb +147 -0
- data/lib/og/ez/condition.rb +181 -0
- data/lib/og/manager.rb +31 -30
- data/lib/og/relation.rb +5 -5
- data/lib/og/relation/has_many.rb +3 -1
- data/lib/og/relation/joins_many.rb +1 -1
- data/lib/og/store.rb +6 -3
- data/lib/og/store/kirby.rb +3 -5
- data/lib/og/store/mysql.rb +0 -1
- data/lib/og/store/sql.rb +43 -7
- data/lib/og/store/sqlite.rb +97 -11
- data/lib/og/store/sqlite2.rb +231 -0
- data/lib/og/test/testcase.rb +1 -1
- data/lib/og/vendor/mysql.rb +103 -25
- data/test/glue/tc_revisable.rb +11 -11
- data/test/og/CONFIG.rb +20 -8
- data/test/og/mixin/tc_hierarchical.rb +5 -3
- data/test/og/mixin/tc_optimistic_locking.rb +6 -4
- data/test/og/mixin/tc_orderable.rb +22 -22
- data/test/og/mixin/tc_taggable.rb +15 -11
- data/test/og/mixin/tc_timestamped.rb +4 -2
- data/test/og/multi_validations_model.rb +8 -0
- data/test/og/store/tc_filesys.rb +15 -12
- data/test/og/store/tc_kirby.rb +14 -11
- data/test/og/tc_accumulator.rb +1 -3
- data/test/og/tc_cacheable.rb +58 -0
- data/test/og/tc_delete_all.rb +13 -16
- data/test/og/tc_ez.rb +33 -0
- data/test/og/tc_finder.rb +2 -4
- data/test/og/tc_inheritance.rb +3 -3
- data/test/og/tc_inheritance2.rb +2 -3
- data/test/og/tc_join.rb +3 -2
- data/test/og/tc_multi_validations.rb +3 -3
- data/test/og/tc_multiple.rb +3 -6
- data/test/og/tc_override.rb +19 -13
- data/test/og/tc_polymorphic.rb +1 -3
- data/test/og/tc_resolve.rb +32 -0
- data/test/og/tc_reverse.rb +27 -28
- data/test/og/tc_scoped.rb +2 -4
- data/test/og/tc_select.rb +1 -3
- data/test/og/tc_store.rb +3 -8
- data/test/og/tc_validation.rb +2 -2
- data/test/og/tc_validation2.rb +56 -58
- data/test/og/tc_validation_loop.rb +2 -5
- metadata +15 -7
- data/lib/og/vendor/mysql411.rb +0 -306
data/lib/og/store/sqlite.rb
CHANGED
@@ -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
|
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 #{
|
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
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
+
|
data/lib/og/test/testcase.rb
CHANGED
data/lib/og/vendor/mysql.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
# $Id: mysql.rb,v 1.
|
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
|
7
|
+
class Mysql
|
8
8
|
|
9
|
-
VERSION = "4.0-ruby-0.2.
|
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
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
385
|
-
|
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
|