Module: N::MysqlBackend

Defined in:
lib/n/db/mysql.rb

Overview

MysqlBackend

Implement the Db backend using the MySQL RDBMS.

Constant Summary collapse

TYPEMAP =

map between Ruby and SQL types

{
	Integer => "integer",
	Fixnum => "integer",
	Float => "float",
	String => "text",
	Time => "timestamp",
	Date => "date",
	TrueClass => "boolean",
	Array => "bytea",
	Hash => "bytea"
}

Instance Method Summary collapse

Instance Method Details

#calc_fields(rows, klass) ⇒ Object

Calculate the fields map for the given class



195
196
197
198
199
200
201
202
# File 'lib/n/db/mysql.rb', line 195

def calc_fields(rows, klass)
	# gmosx SOS, FIXME, INVESTIGATE: no need for second safe hash ????
	fields = $db.fields[klass] = {}
	for field in rows.fields
		fields[field] = rows.fieldnum(field)
	end
	N::Managed.eval_db_read_row(klass)
end

#closeObject

Close the connection to the database



111
112
113
# File 'lib/n/db/mysql.rb', line 111

def close()
	@rdb.close
end

#create_schemaObject

Create the sequence that generates the unified space id oids. You MUST call this method on newly created databases.



119
120
121
# File 'lib/n/db/mysql.rb', line 119

def create_schema()
	@rdb.query("CREATE SEQUENCE oids_seq")
end

#create_table(klass) ⇒ Object

Create a table for an entity.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/n/db/mysql.rb', line 145

def create_table(klass)
	fields = []
	klass.__props.each { |p|
		field = "#{p.symbol}"
		if p.sql_type
			field << " #{p.sql_type}"
		else
			field << " #{TYPEMAP[p.klass]}"
		end
		field << " #{p.sql}" if p.sql
	
		field << " NOT NULL AUTO_INCREMENT" if p.symbol == :oid	
		fields << field 
	}
	
	sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
	
	# Create table constrains
	
	if klass.__meta and constrains = klass.__meta[:sql_constrain]
		sql << ", #{constrains.join(', ')}"
	end
	
	sql << ");"
	safe_query(sql)

	$log.info "Created table #{klass::DBTABLE}! :: #{sql}"

	# Create indices
		if klass.__meta
     for data in klass.__meta[:sql_index]
			sql = ""
       idx = data[0]
       idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
       sql << " CREATE"
       sql << " UNIQUE" if data[1]
       sql << " INDEX #{klass::DBTABLE}_#{idxname}_idx #{data[3]} ON #{klass::DBTABLE} (#{idx});"
 			safe_query(sql)
    end
   end
end

#deserialize_all(rows, klass, join_fields = nil) ⇒ Object

If the connection is in deserialize mode, deserialize all rows.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/n/db/mysql.rb', line 238

def deserialize_all(rows, klass, join_fields = nil)	
	return nil unless rows

	calc_fields(rows, klass) unless $db.fields[klass]
	
	if @deserialize		
		entities = []
	
		for tuple in (0...rows.num_tuples)
			entity = klass.new()
			entity.__db_read_row(rows, tuple)
			
			get_join_fields(rows, tuple, entity, join_fields) if join_fields
			
			entities << entity
		end
		
		rows.clear()
		return entities
	end
	
	return rows
end

#deserialize_one(rows, klass, join_fields = nil) ⇒ Object

If the connection is in deserialize mode, deserialize one row.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/n/db/mysql.rb', line 216

def deserialize_one(rows, klass, join_fields = nil)	
	return nil unless rows
	
	calc_fields(rows, klass) unless $db.fields[klass]
	
	if @deserialize
		# gmosx: Enities should have no params constructor SOS.
		#
		entity = klass.new()
		entity.__db_read_row(rows, 0)
		
		get_join_fields(rows, 0, entity, join_fields) if join_fields
		
		rows.clear()
		return entity
	end

	return rows[0]
end

#drop_schemaObject

Drop the oid sequence



125
126
127
# File 'lib/n/db/mysql.rb', line 125

def drop_schema()
	@rdb.query("DROP SEQUENCE oids_seq")
end

#drop_table(klass) ⇒ Object

Drop the entity table



189
190
191
# File 'lib/n/db/mysql.rb', line 189

def drop_table(klass)
	safe_query("DROP TABLE #{klass::DBTABLE}")
end

#execute_statementObject Also known as: xstatement

NOT IMPLEMENTED



138
139
140
# File 'lib/n/db/mysql.rb', line 138

def execute_statement()
	self.select("EXECUTE")
end

#get_join_fields(rows, tuple, entity, join_fields) ⇒ Object

Grab the join fields returned by an sql join query, and attach them to the entity.



207
208
209
210
211
212
# File 'lib/n/db/mysql.rb', line 207

def get_join_fields(rows, tuple, entity, join_fields)
	entity.join_fields = {}
	for f in join_fields
		entity.join_fields[f] = rows.getvalue(tuple, $db.fields[entity.class][f.to_s])
	end
end

#initialize(config) ⇒ Object

Initialize a connection to the database



105
106
107
# File 'lib/n/db/mysql.rb', line 105

def initialize(config)
	@rdb = Mysql.connect(config[:address], config[:user], config[:password], config[:database])
end

#next_oid(klass) ⇒ Object

Get the next oid in the sequence for this klass



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/n/db/mysql.rb', line 312

def next_oid(klass)
	retries = 0
	begin
		res = @rdb.insert_id()
		res ? oid = res + 1 : oid = 1
		return oid
	rescue => ex
		if ex.errno == 1146 # table does not exist
			$log.info "next_oid: #{ex}"
			# table does not exist, create it!
			create_table(klass)
			# gmosx: only allow ONE retry to avoid loops here!
			retries += 1 
			retry if retries <= 1
		else
			$log.error "DB Error (next_oid): #{ex} (#{ex.errno})"
			$log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
			return nil
		end
	end
end

#prepare_statementObject Also known as: pstatement

NOT IMPLEMENTED



131
132
133
# File 'lib/n/db/mysql.rb', line 131

def prepare_statement()
	@rdb.query("PREPARE")
end

#retry_query(sql, klass = nil) ⇒ Object

Execute an sql query. If the entity table is missing, create it and retry.



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/n/db/mysql.rb', line 266

def retry_query(sql, klass = nil)
	$log.debug sql if $DBG
	retries = 0
	begin
		rows = @rdb.query(sql)
		if rows && (rows.num_tuples > 0) 
			return rows
		else
			return nil
		end
	rescue => ex
		if ex.errno == 1146 # table does not exist
			$log.info "retry_query: #{ex}"
			# table does not exist, create it!
			create_table(klass)
			# gmosx: only allow ONE retry to avoid loops here!
			retries += 1 
			retry if retries <= 1
		else
			$log.error "DB Error: #{ex} (#{ex.errno}), [#{sql}]"
			$log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
			return nil
		end
	end
end

#safe_query(sql) ⇒ Object

Execute an sql query and catch the errors.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/n/db/mysql.rb', line 294

def safe_query(sql)	
	$log.debug sql if $DBG
	begin
		rows = @rdb.query(sql)
		if rows #&& (rows.num_tuples > 0) 
			return rows
		else
			return nil
		end
	rescue => ex
			$log.error "DB Error (safe_query): #{ex} (#{ex.errno}), [#{sql}]"
			$log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
		return nil
	end
end