og 0.5.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/AUTHORS +19 -0
- data/LICENSE +32 -0
- data/README.og +104 -0
- data/RELEASES.og +23 -0
- data/examples/og/README +4 -0
- data/examples/og/run.rb +251 -0
- data/lib/glue.rb +52 -0
- data/lib/glue/array.rb +84 -0
- data/lib/glue/cache.rb +140 -0
- data/lib/glue/hash.rb +143 -0
- data/lib/glue/inflector.rb +91 -0
- data/lib/glue/logger.rb +51 -0
- data/lib/glue/macro.rb +56 -0
- data/lib/glue/mixins.rb +45 -0
- data/lib/glue/number.rb +30 -0
- data/lib/glue/pool.rb +63 -0
- data/lib/glue/property.rb +301 -0
- data/lib/glue/string.rb +224 -0
- data/lib/glue/time.rb +93 -0
- data/lib/og.rb +411 -0
- data/lib/og/backend.rb +258 -0
- data/lib/og/backends/mysql.rb +360 -0
- data/lib/og/backends/psql.rb +359 -0
- data/lib/og/connection.rb +265 -0
- data/lib/og/meta.rb +139 -0
- data/lib/og/version.rb +8 -0
- data/test/tc_og.rb +179 -0
- metadata +71 -0
data/lib/og/backend.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
#
|
4
|
+
# (c) 2004 Navel, all rights reserved.
|
5
|
+
# $Id: backend.rb 155 2004-11-13 20:32:12Z gmosx $
|
6
|
+
|
7
|
+
require "yaml"
|
8
|
+
|
9
|
+
require "og/connection"
|
10
|
+
|
11
|
+
module Og
|
12
|
+
|
13
|
+
# = OgUtils
|
14
|
+
#
|
15
|
+
# A collection of useful utilities.
|
16
|
+
#
|
17
|
+
module Utils
|
18
|
+
|
19
|
+
# The name of the SQL table where objects of this class are stored.
|
20
|
+
# The Module separators are replaced with _ and NOT stripped out so
|
21
|
+
# that we can convert back to the original notation if needed.
|
22
|
+
# The leading module if available is removed.
|
23
|
+
#
|
24
|
+
def self.table(klass)
|
25
|
+
return "_#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the props that will be included in the insert query.
|
29
|
+
# For some backends the oid should be stripped.
|
30
|
+
#
|
31
|
+
def self.props_for_insert(klass)
|
32
|
+
klass.__props
|
33
|
+
end
|
34
|
+
|
35
|
+
# Precompile the insert code for the given class.
|
36
|
+
# The generated code sets the oid when inserting!
|
37
|
+
#
|
38
|
+
def self.eval_og_insert(klass)
|
39
|
+
props = props_for_insert(klass)
|
40
|
+
|
41
|
+
values = props.collect { |p| write_prop(p) }
|
42
|
+
|
43
|
+
sql = "INSERT INTO #{table(klass)} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values.join(',')})"
|
44
|
+
|
45
|
+
if klass.instance_methods.include?("og_pre_insert")
|
46
|
+
pre_cb = "og_pre_insert(conn);"
|
47
|
+
else
|
48
|
+
pre_cb = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
if klass.instance_methods.include?("og_post_insert")
|
52
|
+
post_cb = "og_post_insert(conn);"
|
53
|
+
else
|
54
|
+
post_cb = ""
|
55
|
+
end
|
56
|
+
|
57
|
+
if klass.instance_methods.include?("og_pre_insert_update")
|
58
|
+
pre_cb << "og_pre_insert_update(conn);"
|
59
|
+
end
|
60
|
+
|
61
|
+
if klass.instance_methods.include?("og_post_insert_update")
|
62
|
+
post_cb << "og_post_insert_update(conn);"
|
63
|
+
end
|
64
|
+
|
65
|
+
klass.class_eval %{
|
66
|
+
def og_insert(conn)
|
67
|
+
#{insert_code(klass, sql, pre_cb, post_cb)}
|
68
|
+
end
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
# Precompile the update code for the given class.
|
73
|
+
# Ignore the oid when updating!
|
74
|
+
#
|
75
|
+
def self.eval_og_update(klass)
|
76
|
+
props = klass.__props.reject { |p| :oid == p.symbol }
|
77
|
+
|
78
|
+
updates = props.collect { |p|
|
79
|
+
"#{p.name}=#{write_prop(p)}"
|
80
|
+
}
|
81
|
+
|
82
|
+
sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}"
|
83
|
+
|
84
|
+
if klass.instance_methods.include?("og_pre_update")
|
85
|
+
pre_cb = "og_pre_update(conn);"
|
86
|
+
else
|
87
|
+
pre_cb = ""
|
88
|
+
end
|
89
|
+
|
90
|
+
if klass.instance_methods.include?("og_post_update")
|
91
|
+
post_cb = "og_post_update(conn);"
|
92
|
+
else
|
93
|
+
post_cb = ""
|
94
|
+
end
|
95
|
+
|
96
|
+
if klass.instance_methods.include?("og_pre_insert_update")
|
97
|
+
pre_cb << "og_pre_insert_update(conn);"
|
98
|
+
end
|
99
|
+
|
100
|
+
if klass.instance_methods.include?("og_post_insert_update")
|
101
|
+
post_cb << "og_post_insert_update(conn);"
|
102
|
+
end
|
103
|
+
|
104
|
+
klass.class_eval %{
|
105
|
+
def og_update(conn)
|
106
|
+
#{pre_cb}
|
107
|
+
conn.exec "#{sql}"
|
108
|
+
#{post_cb}
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
# Precompile the code to read objects of the given class
|
114
|
+
# from the backend. In order to allow for changing field/attribute
|
115
|
+
# orders we have to use a field mapping hash.
|
116
|
+
#
|
117
|
+
def self.eval_og_deserialize(klass, og)
|
118
|
+
calc_field_index(klass, og)
|
119
|
+
|
120
|
+
props = klass.__props
|
121
|
+
code = []
|
122
|
+
|
123
|
+
props.each do |p|
|
124
|
+
if idx = og.managed_classes[klass].field_index[p.name]
|
125
|
+
# more fault tolerant if a new field is added and it
|
126
|
+
# doesnt exist in the database.
|
127
|
+
code << "@#{p.name} = #{Og::Utils.read_prop(p, idx)}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
klass.class_eval %{
|
132
|
+
def og_deserialize(res, tuple = nil)
|
133
|
+
#{code.join('; ')}
|
134
|
+
end
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
# = Backend
|
141
|
+
#
|
142
|
+
# Abstract backend. A backend communicates with the RDBMS.
|
143
|
+
# This is the base class for the various backend implementations.
|
144
|
+
#
|
145
|
+
class Backend
|
146
|
+
|
147
|
+
# The actual connection to the database
|
148
|
+
attr_accessor :conn
|
149
|
+
|
150
|
+
# Intitialize the connection to the RDBMS.
|
151
|
+
#
|
152
|
+
def initialize(config)
|
153
|
+
raise "Not implemented"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Close the connection to the RDBMS.
|
157
|
+
#
|
158
|
+
def close()
|
159
|
+
@conn.close()
|
160
|
+
end
|
161
|
+
|
162
|
+
# Create the database.
|
163
|
+
#
|
164
|
+
def self.create_db(database, user = nil, password = nil)
|
165
|
+
$log.info "Creating database '#{database}'."
|
166
|
+
end
|
167
|
+
|
168
|
+
# Drop the database.
|
169
|
+
#
|
170
|
+
def self.drop_db(database, user = nil, password = nil)
|
171
|
+
$log.info "Dropping database '#{database}'."
|
172
|
+
end
|
173
|
+
|
174
|
+
# Execute an SQL query and return the result.
|
175
|
+
#
|
176
|
+
def query(sql)
|
177
|
+
raise "Not implemented"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Execute an SQL query, no result returned.
|
181
|
+
#
|
182
|
+
def exec(sql)
|
183
|
+
raise "Not implemented"
|
184
|
+
end
|
185
|
+
|
186
|
+
# Execute an SQL query and return the result. Wrapped in a rescue
|
187
|
+
# block.
|
188
|
+
#
|
189
|
+
def safe_query(sql)
|
190
|
+
raise "Not implemented"
|
191
|
+
end
|
192
|
+
|
193
|
+
# Execute an SQL query, no result returned. Wrapped in a rescue
|
194
|
+
# block.
|
195
|
+
#
|
196
|
+
def safe_exec(sql)
|
197
|
+
raise "Not implemented"
|
198
|
+
end
|
199
|
+
|
200
|
+
# Check if it is a valid resultset.
|
201
|
+
#
|
202
|
+
def valid?(res)
|
203
|
+
raise "Not implemented"
|
204
|
+
end
|
205
|
+
|
206
|
+
# Start a new transaction.
|
207
|
+
#
|
208
|
+
def start
|
209
|
+
exec "START TRANSACTION"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Commit a transaction.
|
213
|
+
#
|
214
|
+
def commit
|
215
|
+
exec "COMMIT"
|
216
|
+
end
|
217
|
+
|
218
|
+
# Rollback transaction.
|
219
|
+
#
|
220
|
+
def rollback
|
221
|
+
exec "ROLLBACK"
|
222
|
+
end
|
223
|
+
|
224
|
+
# Create the managed object table. The properties of the
|
225
|
+
# object are mapped to the table columns. Additional sql relations
|
226
|
+
# and constrains are created (indicices, sequences, etc).
|
227
|
+
#
|
228
|
+
def create_table(klass)
|
229
|
+
return if query("SELECT * FROM #{klass::DBTABLE} LIMIT 1")
|
230
|
+
end
|
231
|
+
|
232
|
+
# Drop the managed object table
|
233
|
+
#
|
234
|
+
def drop_table(klass)
|
235
|
+
exec "DROP TABLE #{klass::DBTABLE}"
|
236
|
+
end
|
237
|
+
|
238
|
+
# Deserialize one row of the resultset.
|
239
|
+
#
|
240
|
+
def deserialize_one(res, klass)
|
241
|
+
raise "Not implemented"
|
242
|
+
end
|
243
|
+
|
244
|
+
# Deserialize all rows of the resultset.
|
245
|
+
#
|
246
|
+
def deserialize_all(res, klass)
|
247
|
+
raise "Not implemented"
|
248
|
+
end
|
249
|
+
|
250
|
+
# Return a single integer value from the resultset.
|
251
|
+
#
|
252
|
+
def get_int(res, idx = 0)
|
253
|
+
raise "Not implemented"
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
end # module
|
@@ -0,0 +1,360 @@
|
|
1
|
+
# code:
|
2
|
+
# * George Moschovitis <gm@navel.gr>
|
3
|
+
# * Elias Athanasopoulos <elathan@navel.gr>
|
4
|
+
#
|
5
|
+
# (c) 2004 Navel, all rights reserved.
|
6
|
+
# $Id: mysql.rb 159 2004-11-18 10:18:30Z gmosx $
|
7
|
+
|
8
|
+
require "mysql"
|
9
|
+
|
10
|
+
require "og/backend"
|
11
|
+
|
12
|
+
module Og
|
13
|
+
|
14
|
+
# = Utils
|
15
|
+
#
|
16
|
+
# A collection of useful utilities.
|
17
|
+
#
|
18
|
+
module Utils
|
19
|
+
|
20
|
+
# Escape an SQL string
|
21
|
+
#
|
22
|
+
def self.escape(str)
|
23
|
+
return nil unless str
|
24
|
+
return Mysql.quote(str)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convert a ruby time to an sql timestamp.
|
28
|
+
# TODO: Optimize this
|
29
|
+
#
|
30
|
+
def self.timestamp(time = Time.now)
|
31
|
+
return nil unless time
|
32
|
+
return time.strftime("%Y%m%d%H%M%S")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Output YYY-mm-dd
|
36
|
+
# TODO: Optimize this
|
37
|
+
#
|
38
|
+
def self.date(date)
|
39
|
+
return nil unless date
|
40
|
+
return "#{date.year}-#{date.month}-#{date.mday}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Parse sql datetime
|
44
|
+
# TODO: Optimize this
|
45
|
+
#
|
46
|
+
def self.parse_timestamp(str)
|
47
|
+
return Time.parse(str)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Input YYYY-mm-dd
|
51
|
+
# TODO: Optimize this
|
52
|
+
#
|
53
|
+
def self.parse_date(str)
|
54
|
+
return nil unless str
|
55
|
+
return Date.strptime(str)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return an sql string evaluator for the property.
|
59
|
+
# No need to optimize this, used only to precalculate code.
|
60
|
+
# YAML is used to store general Ruby objects to be more
|
61
|
+
# portable.
|
62
|
+
#
|
63
|
+
# FIXME: add extra handling for float.
|
64
|
+
#
|
65
|
+
def self.write_prop(p)
|
66
|
+
if p.klass.ancestors.include?(Integer)
|
67
|
+
return "#\{@#{p.symbol} || 'NULL'\}"
|
68
|
+
elsif p.klass.ancestors.include?(Float)
|
69
|
+
return "#\{@#{p.symbol} || 'NULL'\}"
|
70
|
+
elsif p.klass.ancestors.include?(String)
|
71
|
+
return "'#\{Og::Utils.escape(@#{p.symbol})\}'"
|
72
|
+
elsif p.klass.ancestors.include?(Time)
|
73
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::Utils.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
|
74
|
+
elsif p.klass.ancestors.include?(Date)
|
75
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::Utils.date(@#{p.symbol})\}'" : 'NULL'\}|
|
76
|
+
elsif p.klass.ancestors.include?(TrueClass)
|
77
|
+
return "#\{@#{p.symbol} || 'NULL'\}"
|
78
|
+
else
|
79
|
+
return %|#\{@#{p.symbol} ? "'#\{Og::Utils.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return an evaluator for reading the property.
|
84
|
+
# No need to optimize this, used only to precalculate code.
|
85
|
+
#
|
86
|
+
def self.read_prop(p, idx)
|
87
|
+
if p.klass.ancestors.include?(Integer)
|
88
|
+
return "res[#{idx}].to_i()"
|
89
|
+
elsif p.klass.ancestors.include?(Float)
|
90
|
+
return "res[#{idx}].to_f()"
|
91
|
+
elsif p.klass.ancestors.include?(String)
|
92
|
+
return "res[#{idx}]"
|
93
|
+
elsif p.klass.ancestors.include?(Time)
|
94
|
+
return "Og::Utils.parse_timestamp(res[#{idx}])"
|
95
|
+
elsif p.klass.ancestors.include?(Date)
|
96
|
+
return "Og::Utils.parse_date(res[#{idx}])"
|
97
|
+
elsif p.klass.ancestors.include?(TrueClass)
|
98
|
+
return "('true' == res[#{idx}])"
|
99
|
+
else
|
100
|
+
return "YAML::load(res[#{idx}])"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the props that will be included in the insert query.
|
105
|
+
# The oid property is rejected because it is mapped to an
|
106
|
+
# AUTO_INCREMENT column.
|
107
|
+
#
|
108
|
+
def self.props_for_insert(klass)
|
109
|
+
klass.__props.reject { |p| :oid == p.symbol }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the code that actually inserts the object into the
|
113
|
+
# database. Returns the code as String.
|
114
|
+
#
|
115
|
+
def self.insert_code(klass, sql, pre_cb, post_cb)
|
116
|
+
%{
|
117
|
+
#{pre_cb}
|
118
|
+
conn.exec "#{sql}"
|
119
|
+
@oid = conn.db.conn.insert_id()
|
120
|
+
#{post_cb}
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
# generate the mapping of the database fields to the
|
125
|
+
# object properties.
|
126
|
+
#
|
127
|
+
def self.calc_field_index(klass, og)
|
128
|
+
res = og.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
|
129
|
+
meta = og.managed_classes[klass]
|
130
|
+
|
131
|
+
for idx in (0...res.num_fields)
|
132
|
+
meta.field_index[res.fetch_field.name] = idx
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generate the property for oid
|
137
|
+
#
|
138
|
+
def self.eval_og_oid(klass)
|
139
|
+
klass.class_eval %{
|
140
|
+
prop_accessor :oid, Fixnum, :sql => "integer AUTO_INCREMENT PRIMARY KEY"
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# = MysqlBackend
|
146
|
+
#
|
147
|
+
# Implements a MySQL powered backend.
|
148
|
+
#
|
149
|
+
class MysqlBackend < Og::Backend
|
150
|
+
|
151
|
+
# A mapping between Ruby and SQL types.
|
152
|
+
#
|
153
|
+
TYPEMAP = {
|
154
|
+
Integer => "integer",
|
155
|
+
Fixnum => "integer",
|
156
|
+
Float => "float",
|
157
|
+
String => "text",
|
158
|
+
Time => "timestamp",
|
159
|
+
Date => "date",
|
160
|
+
TrueClass => "boolean",
|
161
|
+
Object => "text",
|
162
|
+
Array => "text",
|
163
|
+
Hash => "text"
|
164
|
+
}
|
165
|
+
|
166
|
+
# Intitialize the connection to the RDBMS.
|
167
|
+
#
|
168
|
+
def initialize(config)
|
169
|
+
begin
|
170
|
+
@conn = Mysql.connect(config[:address], config[:user],
|
171
|
+
config[:password], config[:database])
|
172
|
+
rescue => ex
|
173
|
+
if ex.errno == 1049 # database does not exist.
|
174
|
+
$log.info "Database '#{config[:database]}' not found!"
|
175
|
+
MysqlBackend.create_db(config[:database], config[:user], config[:password])
|
176
|
+
retry
|
177
|
+
end
|
178
|
+
raise
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Create the database.
|
183
|
+
#
|
184
|
+
def self.create_db(database, user = nil, password = nil)
|
185
|
+
$log.info "Creating database '#{database}'."
|
186
|
+
`mysqladmin -f --user=#{user} --password=#{password} create #{database}`
|
187
|
+
end
|
188
|
+
|
189
|
+
# Drop the database.
|
190
|
+
#
|
191
|
+
def self.drop_db(database, user = nil, password = nil)
|
192
|
+
$log.info "Dropping database '#{database}'."
|
193
|
+
`mysqladmin -f --user=#{user} --password=#{password} drop #{database}`
|
194
|
+
end
|
195
|
+
|
196
|
+
# Execute an SQL query and return the result
|
197
|
+
#
|
198
|
+
def query(sql)
|
199
|
+
$log.debug sql if $DBG
|
200
|
+
return @conn.query(sql)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Execute an SQL query, no result returned.
|
204
|
+
#
|
205
|
+
def exec(sql)
|
206
|
+
$log.debug sql if $DBG
|
207
|
+
@conn.query(sql)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Execute an SQL query and return the result. Wrapped in a rescue
|
211
|
+
# block.
|
212
|
+
#
|
213
|
+
def safe_query(sql)
|
214
|
+
$log.debug sql if $DBG
|
215
|
+
begin
|
216
|
+
return @conn.query(sql)
|
217
|
+
rescue => ex
|
218
|
+
$log.error "DB error #{ex}, [#{sql}]"
|
219
|
+
$log.error ex.backtrace
|
220
|
+
return nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Execute an SQL query, no result returned. Wrapped in a rescue
|
225
|
+
# block.
|
226
|
+
#
|
227
|
+
def safe_exec(sql)
|
228
|
+
$log.debug sql if $DBG
|
229
|
+
begin
|
230
|
+
@conn.query(sql)
|
231
|
+
rescue => ex
|
232
|
+
$log.error "DB error #{ex}, [#{sql}]"
|
233
|
+
$log.error ex.backtrace
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Check if it is a valid resultset.
|
238
|
+
#
|
239
|
+
def valid?(res)
|
240
|
+
return !(res.nil? or 0 == res.num_rows)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Start a new transaction.
|
244
|
+
#
|
245
|
+
def start
|
246
|
+
# no transaction support
|
247
|
+
end
|
248
|
+
|
249
|
+
# Commit a transaction.
|
250
|
+
#
|
251
|
+
def commit
|
252
|
+
# no transaction support
|
253
|
+
end
|
254
|
+
|
255
|
+
# Rollback transaction.
|
256
|
+
#
|
257
|
+
def rollback
|
258
|
+
# no transaction support
|
259
|
+
end
|
260
|
+
|
261
|
+
# Create the managed object table. The properties of the
|
262
|
+
# object are mapped to the table columns. Additional sql relations
|
263
|
+
# and constrains are created (indicices, sequences, etc).
|
264
|
+
#
|
265
|
+
def create_table(klass)
|
266
|
+
fields = []
|
267
|
+
|
268
|
+
klass.__props.each do |p|
|
269
|
+
klass.sql_index(p.symbol) if p.meta[:sql_index]
|
270
|
+
|
271
|
+
field = "#{p.symbol}"
|
272
|
+
|
273
|
+
if p.meta and p.meta[:sql]
|
274
|
+
field << " #{p.meta[:sql]}"
|
275
|
+
else
|
276
|
+
field << " #{TYPEMAP[p.klass]}"
|
277
|
+
end
|
278
|
+
|
279
|
+
fields << field
|
280
|
+
end
|
281
|
+
|
282
|
+
sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
|
283
|
+
|
284
|
+
# Create table constrains
|
285
|
+
|
286
|
+
if klass.__meta and constrains = klass.__meta[:sql_constrain]
|
287
|
+
sql << ", #{constrains.join(', ')}"
|
288
|
+
end
|
289
|
+
|
290
|
+
sql << ');'
|
291
|
+
|
292
|
+
begin
|
293
|
+
exec(sql)
|
294
|
+
$log.info "Created table '#{klass::DBTABLE}'."
|
295
|
+
rescue => ex
|
296
|
+
if ex.errno == 1050 # table already exists.
|
297
|
+
$log.debug "Table already exists" if $DBG
|
298
|
+
else
|
299
|
+
raise
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Create indices
|
304
|
+
|
305
|
+
if klass.__meta
|
306
|
+
for data in klass.__meta[:sql_index]
|
307
|
+
idx, options = *data
|
308
|
+
idx = idx.to_s
|
309
|
+
pre_sql, post_sql = options[:pre], options[:post]
|
310
|
+
idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
|
311
|
+
exec "CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx})"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Deserialize one row of the resultset.
|
317
|
+
#
|
318
|
+
def deserialize_one(res, klass)
|
319
|
+
return nil unless valid?(res)
|
320
|
+
|
321
|
+
# gmosx: Managed objects should have no params constructor.
|
322
|
+
row = res.fetch_row()
|
323
|
+
entity = klass.new()
|
324
|
+
entity.og_deserialize(row)
|
325
|
+
|
326
|
+
# get_join_fields(res, 0, entity, join_fields) if join_fields
|
327
|
+
|
328
|
+
return entity
|
329
|
+
end
|
330
|
+
|
331
|
+
# Deserialize all rows of the resultset.
|
332
|
+
#
|
333
|
+
def deserialize_all(res, klass)
|
334
|
+
return nil unless valid?(res)
|
335
|
+
|
336
|
+
entities = []
|
337
|
+
|
338
|
+
for tuple in (0...res.num_rows)
|
339
|
+
row = res.fetch_row()
|
340
|
+
|
341
|
+
entity = klass.new()
|
342
|
+
entity.og_deserialize(row)
|
343
|
+
|
344
|
+
# get_join_fields(res, tuple, entity, join_fields) if join_fields
|
345
|
+
|
346
|
+
entities << entity
|
347
|
+
end
|
348
|
+
|
349
|
+
return entities
|
350
|
+
end
|
351
|
+
|
352
|
+
# Return a single integer value from the resultset.
|
353
|
+
#
|
354
|
+
def get_int(res, idx = 0)
|
355
|
+
return res.fetch_row[idx].to_i
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end # module
|