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.
- 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
|