dohmysql 0.1.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 (46) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/bin/makedb +28 -0
  3. data/lib/doh/mysql/abstract_row.rb +80 -0
  4. data/lib/doh/mysql/activate.rb +31 -0
  5. data/lib/doh/mysql/cache_connector.rb +54 -0
  6. data/lib/doh/mysql/connector_instance.rb +79 -0
  7. data/lib/doh/mysql/connector_util.rb +27 -0
  8. data/lib/doh/mysql/convert.rb +18 -0
  9. data/lib/doh/mysql/current_date.rb +22 -0
  10. data/lib/doh/mysql/database_creator.rb +101 -0
  11. data/lib/doh/mysql/db_date.rb +28 -0
  12. data/lib/doh/mysql/default_type_guesser.rb +37 -0
  13. data/lib/doh/mysql/error.rb +7 -0
  14. data/lib/doh/mysql/handle.rb +218 -0
  15. data/lib/doh/mysql/hash_row.rb +13 -0
  16. data/lib/doh/mysql/load_sql.rb +26 -0
  17. data/lib/doh/mysql/metadata_util.rb +73 -0
  18. data/lib/doh/mysql/parse.rb +36 -0
  19. data/lib/doh/mysql/raw_row_builder.rb +15 -0
  20. data/lib/doh/mysql/readonly_row.rb +26 -0
  21. data/lib/doh/mysql/require_dbtypes.rb +8 -0
  22. data/lib/doh/mysql/smart_row.rb +156 -0
  23. data/lib/doh/mysql/to_sql.rb +65 -0
  24. data/lib/doh/mysql/typed_row_builder.rb +28 -0
  25. data/lib/doh/mysql/types.rb +33 -0
  26. data/lib/doh/mysql/unquoted.rb +17 -0
  27. data/lib/doh/mysql/version.rb +102 -0
  28. data/lib/doh/mysql/virtual.rb +17 -0
  29. data/lib/doh/mysql/writable_row.rb +59 -0
  30. data/lib/doh/mysql.rb +7 -0
  31. data/test/cache_connector.dt.rb +41 -0
  32. data/test/connector.yml +4 -0
  33. data/test/connector.yml.tmpl +4 -0
  34. data/test/connector_instance.dt.rb +32 -0
  35. data/test/convert.dt.rb +45 -0
  36. data/test/db_unit_test.rb +10 -0
  37. data/test/handle.dt.rb +112 -0
  38. data/test/metadata_util.dt.rb +53 -0
  39. data/test/parse.dt.rb +39 -0
  40. data/test/readonly_row.dt.rb +85 -0
  41. data/test/smart_row.dt.rb +21 -0
  42. data/test/to_sql.dt.rb +19 -0
  43. data/test/types.dt.rb +32 -0
  44. data/test/unquoted.dt.rb +16 -0
  45. data/test/writable_row.dt.rb +21 -0
  46. metadata +118 -0
@@ -0,0 +1,218 @@
1
+ require 'doh/array_to_hash'
2
+ require 'doh/log/stub'
3
+ require 'doh/mysql/error'
4
+ require 'doh/mysql/typed_row_builder'
5
+ require 'doh/mysql/writable_row'
6
+ require 'doh/mysql/hash_row'
7
+ require 'doh/mysql/smart_row'
8
+
9
+ module DohDb
10
+
11
+ class Handle
12
+ def initialize(mysqlh, row_builder = nil)
13
+ @mysqlh = mysqlh
14
+ @row_builder = row_builder || TypedRowBuilder.new
15
+ end
16
+
17
+ def close
18
+ unless closed?
19
+ dohlog.info("closing raw mysql handle: #@mysqlh")
20
+ @mysqlh.close
21
+ @mysqlh = nil
22
+ end
23
+ end
24
+
25
+ def closed?
26
+ @mysqlh.nil?
27
+ end
28
+
29
+ def query(statement)
30
+ generic_query(statement)
31
+ retval = @mysqlh.affected_rows
32
+ dohlog.info("affected #{retval} rows")
33
+ retval
34
+ end
35
+
36
+ def update(statement)
37
+ generic_query(statement)
38
+ retval = @mysqlh.affected_rows
39
+ dohlog.info("updated #{retval} rows")
40
+ retval
41
+ end
42
+
43
+ def update_row(statement)
44
+ retval = update(statement)
45
+ raise UnexpectedQueryResult, "updated #{retval} rows; expected 1" unless retval == 1
46
+ retval
47
+ end
48
+
49
+ def update_hash(hash, table, primary_key_value, primary_key_name)
50
+ items = hash.keys.collect {|key| key + ' = ' + hash[key].to_sql}
51
+ query("UPDATE #{table} SET #{items.join(', ')} WHERE #{primary_key_name} = #{primary_key_value.to_sql}")
52
+ end
53
+
54
+ def insert(statement)
55
+ generic_query(statement)
56
+ retval = @mysqlh.insert_id
57
+ dohlog.info("insert_id was #{retval}")
58
+ retval
59
+ end
60
+
61
+ def insert_hash(hash, table, ignore = nil)
62
+ if ignore then keyword = 'INSERT IGNORE' else keyword = 'INSERT' end
63
+ insert_hash_helper(hash, table, keyword)
64
+ end
65
+
66
+ def replace_hash(hash, table)
67
+ insert_hash_helper(hash, table, 'REPLACE')
68
+ end
69
+
70
+ # The most generic form of select.
71
+ # It calls to_s on the statement object to facilitate the use of sql builder objects.
72
+ def select(statement, row_builder = nil)
73
+ result_set = generic_query(statement)
74
+ dohlog.info("selected #{result_set.num_rows} rows")
75
+ rows = get_row_builder(row_builder).build_rows(result_set)
76
+ result_set.free
77
+ rows
78
+ end
79
+
80
+ # Simple convenience wrapper around the generic select call.
81
+ # Throws an exception unless the result set is a single row.
82
+ # Returns the row selected.
83
+ def select_row(statement, row_builder = nil)
84
+ rows = select(statement, row_builder)
85
+ raise UnexpectedQueryResult, "selected #{rows.size} rows; expected 1" unless rows.size == 1
86
+ rows[0]
87
+ end
88
+
89
+ # Simple convenience wrapper around the generic select call.
90
+ # Throws an exception unless the result set is empty or a single row.
91
+ # Returns nil if the result set is empty, or the row selected.
92
+ def select_optional_row(statement, row_builder = nil)
93
+ rows = select(statement, row_builder)
94
+ raise UnexpectedQueryResult, "selected #{rows.size} rows; expected 0 or 1" if rows.size > 1
95
+ if rows.empty? then nil else rows[0] end
96
+ end
97
+
98
+ # Simple convenience wrapper around select_row.
99
+ # Returns the first (and typically, the only) field from the selected row.
100
+ def select_field(statement, row_builder = nil)
101
+ select_row(statement, row_builder).at(0)
102
+ end
103
+
104
+ # Simple convenience wrapper around select_optional_row.
105
+ # Returns the first (and typically, the only) field from the selected row, if any, or nil.
106
+ def select_optional_field(statement, row_builder = nil)
107
+ row = select_optional_row(statement, row_builder)
108
+ row && row.at(0)
109
+ end
110
+
111
+ # Rows in the result set must have 2 or more fields.
112
+ # If there are 2 fields, returns a hash where each key is the first field in the result set, and the value is the second field.
113
+ # If there are more than 2 fields, returns a hash where each key is the first field in the result set,
114
+ # and the value is the row itself, as a Hash, and without the field used as a key.
115
+ def select_transpose(statement, row_builder = nil)
116
+ rows = select(statement, row_builder)
117
+ return {} if rows.empty?
118
+ field_count = rows.first.size
119
+ if field_count < 2
120
+ raise UnexpectedQueryResult, "must select at least 2 fields in order to transpose"
121
+ elsif field_count == 2
122
+ Doh::array_to_hash(rows) { |row| [row.at(0), row.at(1)] }
123
+ else
124
+ key_field = rows.first.keys.first
125
+ Doh::array_to_hash(rows) do |row|
126
+ value = row.to_h
127
+ value.delete(key_field)
128
+ [row.at(0), value]
129
+ end
130
+ end
131
+ end
132
+
133
+ # Returns an array of arrays, where the individual arrays contain just the values from each database row -- they lack field names.
134
+ def select_values(statement, row_builder = nil)
135
+ select(statement, row_builder).collect { |row| row.values }
136
+ end
137
+
138
+ # Returns an array of the first (and typically, the only) field of every row in the result set.
139
+ def select_list(statement, row_builder = nil)
140
+ select(statement, row_builder).collect { |row| row.at(0) }
141
+ end
142
+
143
+ def multi_select(statement_infos, dflt_row_builder = nil)
144
+ statements = []
145
+ row_builders = []
146
+ statement_infos.each do |info|
147
+ if info.is_a?(Array)
148
+ statements.push(info.first)
149
+ row_builders.push(info.last)
150
+ else
151
+ statements.push(info)
152
+ row_builders.push(nil)
153
+ end
154
+ end
155
+ sqlstr = statements.join(';')
156
+
157
+ dohlog.info(sqlstr)
158
+ @mysqlh.query(sqlstr)
159
+ retval = []
160
+ while true
161
+ custom_builder = row_builders.shift
162
+ if @mysqlh.field_count > 0
163
+ result_set = @mysqlh.store_result
164
+ dohlog.info("selected #{result_set.num_rows} rows")
165
+ rows = get_row_builder(custom_builder || dflt_row_builder).build_rows(result_set)
166
+ result_set.free
167
+ retval.push(rows)
168
+ end
169
+ break unless @mysqlh.next_result
170
+ end
171
+ retval
172
+ rescue Exception => excpt
173
+ dohlog.error("caught exception during query: #{sqlstr}", excpt)
174
+ raise
175
+ end
176
+
177
+ private
178
+ def generic_query(statement)
179
+ sqlstr = statement.to_s
180
+ dohlog.info(sqlstr)
181
+ @mysqlh.query(sqlstr)
182
+ @mysqlh.store_result if @mysqlh.field_count > 0
183
+ rescue Exception => excpt
184
+ dohlog.error("caught exception during query: #{sqlstr}", excpt)
185
+ raise
186
+ end
187
+
188
+ def insert_hash_helper(hash, table, keyword)
189
+ names = []
190
+ values = []
191
+ hash.each_pair do |key, value|
192
+ names.push(key)
193
+ values.push(value.to_sql)
194
+ end
195
+
196
+ insert("#{keyword} INTO #{table} (#{names.join(',')}) VALUES (#{values.join(',')})")
197
+ end
198
+
199
+ def get_row_builder(row_builder = nil)
200
+ if row_builder.nil?
201
+ @row_builder
202
+ elsif row_builder == :read
203
+ TypedRowBuilder.new(ReadOnlyRow)
204
+ elsif row_builder == :hash
205
+ TypedRowBuilder.new(HashRow)
206
+ elsif row_builder == :write
207
+ TypedRowBuilder.new(WritableRow)
208
+ elsif row_builder == :smart
209
+ TypedRowBuilder.new(SmartRow)
210
+ elsif row_builder.respond_to?('build_rows')
211
+ row_builder
212
+ else
213
+ TypedRowBuilder.new(row_builder)
214
+ end
215
+ end
216
+ end
217
+
218
+ end
@@ -0,0 +1,13 @@
1
+ module DohDb
2
+
3
+ class HashRow
4
+ def self.new(keys, values)
5
+ hash = {}
6
+ keys.each_with_index do |key, index|
7
+ hash[key] = values.at(index)
8
+ end
9
+ hash
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'doh/mysql/connector_instance'
2
+
3
+ module DohDb
4
+
5
+ def self.mysql_arg(value, option_specifier)
6
+ return '' if value.to_s.strip.empty?
7
+ ' -' + option_specifier + value
8
+ end
9
+
10
+ def self.load_sql(filenames, host, username, password, database)
11
+ mysqlcmd = 'mysql' + mysql_arg(host, 'h') + mysql_arg(username, 'u') + mysql_arg(password, 'p') + ' ' + database
12
+ io = IO::popen(mysqlcmd, 'r+')
13
+ dohlog.debug("loading sql file: " + filenames.first) if filenames.size == 1
14
+ filenames.each do |elem|
15
+ open(elem) {|file| io << file.read}
16
+ end
17
+ io.close
18
+ end
19
+
20
+ def self.load_sql_connector(filenames, connector = nil, alternate_database = nil)
21
+ connector ||= DohDb::connector_instance
22
+ load_sql(filenames, connector.host, connector.username, connector.password, alternate_database || connector.database)
23
+ end
24
+
25
+ end
26
+
@@ -0,0 +1,73 @@
1
+ require 'doh/mysql'
2
+ require 'doh/mysql/to_sql'
3
+
4
+ module DohDb
5
+
6
+ @@cached_column_info = {}
7
+ def self.column_info(table, database = nil)
8
+ database ||= DohDb::connector_instance.database
9
+ lookup_str = database + '.' + table
10
+ return @@cached_column_info[lookup_str] if @@cached_column_info[lookup_str]
11
+ stmt = "SELECT column_name, is_nullable, data_type, character_maximum_length, numeric_scale, column_type FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=#{database.to_sql} AND TABLE_NAME=#{table.to_sql}"
12
+ @@cached_column_info[lookup_str] = DohDb::select_transpose(stmt)
13
+ end
14
+
15
+ def self.field_character_size(table, field, database = nil)
16
+ column_info(table, database).fetch(field, {}).fetch('character_maximum_length')
17
+ end
18
+
19
+ def self.chop_character_fields!(table, row)
20
+ column_info(table).each do |field, attribs|
21
+ maxlen = attribs['character_maximum_length']
22
+ if maxlen && row[field].to_s.size > maxlen
23
+ row[field] = row[field].to_s[0, maxlen]
24
+ end
25
+ end
26
+ row
27
+ end
28
+
29
+ def self.chop_character_fields(table, row)
30
+ chop_character_fields!(table, row.dup)
31
+ end
32
+
33
+ def self.field_exist?(table, field, database = nil)
34
+ column_info(table, database).key?(field)
35
+ end
36
+
37
+ @@primary_keys = {}
38
+ def self.find_primary_key(table, database = nil)
39
+ if table.index('.')
40
+ database = table.before('.')
41
+ table = table.after('.')
42
+ else
43
+ database ||= DohDb::connector_instance.database
44
+ end
45
+
46
+ dbhash = @@primary_keys[database]
47
+ if dbhash.nil?
48
+ dbhash = DohDb::select_transpose("SELECT table_name, column_name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=#{database.to_sql} AND ORDINAL_POSITION=1")
49
+ raise "no information found for database #{database}" if dbhash.empty?
50
+ @@primary_keys[database] = dbhash
51
+ end
52
+
53
+ retval = dbhash[table]
54
+ raise "attempting to find_primary_key for table that doesn't exist: #{database}.#{table}" if retval.nil?
55
+ retval
56
+ end
57
+
58
+ def self.table_exist?(table, database = nil)
59
+ !find_primary_key(table, database).nil?
60
+ end
61
+
62
+ def self.field_list(table, database = nil)
63
+ column_info(table, database).keys
64
+ end
65
+
66
+ @@tables_by_database = {}
67
+ def self.all_tables(database = nil)
68
+ database ||= DohDb::connector_instance.database
69
+ @@tables_by_database[database] ||
70
+ @@tables_by_database[database] ||= DohDb::select_list("SELECT table_name FROM information_schema.tables WHERE table_schema = '#{database}'")
71
+ end
72
+
73
+ end
@@ -0,0 +1,36 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+
4
+ module DohDb
5
+
6
+ def self.parse_bool(str)
7
+ if str == '0'
8
+ false
9
+ elsif str == '1'
10
+ true
11
+ else
12
+ raise ArgumentError.new("unexpected value: " + str)
13
+ end
14
+ end
15
+
16
+ def self.parse_datetime(str)
17
+ raise ArgumentError.new("unexpected value: " + str) unless str.size == 19
18
+ return nil if str == '0000-00-00 00:00:00'
19
+ DateTime.parse(str)
20
+ end
21
+
22
+ def self.parse_date(str)
23
+ raise ArgumentError.new("unexpected value: " + str) unless str.size == 10
24
+ return nil if str == '0000-00-00'
25
+ Date.new(str[0..3].to_i, str[5..6].to_i, str[8..9].to_i)
26
+ end
27
+
28
+ def self.parse_decimal(str)
29
+ BigDecimal(str)
30
+ end
31
+
32
+ def self.parse_int(str)
33
+ str.to_i
34
+ end
35
+
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'mysql'
2
+ require 'doh/mysql/readonly_row'
3
+
4
+ module DohDb
5
+
6
+ class RawRowBuilder
7
+ def build_rows(result_set)
8
+ field_names = result_set.fetch_fields.collect {|elem| elem.name}
9
+ retval = []
10
+ result_set.each {|elem| retval.push(DohDb::ReadOnlyRow.new(field_names, elem))}
11
+ retval
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,26 @@
1
+ require 'doh/mysql/abstract_row'
2
+
3
+ module DohDb
4
+
5
+ class ReadOnlyRow < AbstractRow
6
+ # can accept 2 arguments: keys array, values array
7
+ # or 1 argument: a hash
8
+ def initialize(*args)
9
+ keys, values = parse_initialize_args(*args)
10
+ @keys = keys.freeze
11
+ @values = values.freeze
12
+ freeze
13
+ end
14
+
15
+ def method_missing(sym, *ignore)
16
+ key = sym.to_s
17
+ index = @keys.index(key)
18
+ if index
19
+ @values.at(index)
20
+ else
21
+ raise RuntimeError.new("unknown field: " + key)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,8 @@
1
+ module DohDb
2
+
3
+ def self.require_dbtypes
4
+ lib_dbtypes_file = File.join(Doh::root, 'lib/dbtypes.rb')
5
+ require(lib_dbtypes_file) if File.exist?(lib_dbtypes_file)
6
+ end
7
+
8
+ end
@@ -0,0 +1,156 @@
1
+ require 'doh/mysql/writable_row'
2
+ require 'doh/to_display'
3
+ require 'doh/mysql/virtual'
4
+
5
+ module DohDb
6
+
7
+ class RowDisplayProxy
8
+ def initialize(row)
9
+ @row = row
10
+ end
11
+
12
+ def method_missing(sym, *ignore_args)
13
+ @row.send(sym.to_s).to_display
14
+ end
15
+ end
16
+
17
+ class AbstractSmartRow < AbstractRow
18
+ attr_accessor :table
19
+ attr_reader :changed_keys
20
+
21
+ def initialize(keys, values)
22
+ @keys = keys.dup
23
+ @values = values.dup
24
+ @changed_keys = Set.new
25
+ @cached_virtuals = {}
26
+ end
27
+
28
+ def initialize_copy(orig)
29
+ @keys = @keys.dup
30
+ @values = @values.dup
31
+ @changed_keys = @changed_keys.dup
32
+ @cached_virtuals = @cached_virtuals.dup
33
+ @display_proxy = nil
34
+ end
35
+
36
+ def display(key = nil)
37
+ if key.nil?
38
+ @display_proxy ||= RowDisplayProxy.new(self)
39
+ else
40
+ get(key).to_display
41
+ end
42
+ end
43
+
44
+ def set(key, value, flag_changed = true)
45
+ index = @keys.index(key)
46
+ if index
47
+ if @values[index] != value
48
+ @values[index] = value
49
+ @changed_keys.add(key)
50
+ end
51
+ else
52
+ @keys.push(key)
53
+ @values.push(value)
54
+ @changed_keys.add(key)
55
+ end
56
+ value
57
+ end
58
+ alias []= set
59
+
60
+ def clear_changed_keys
61
+ @changed_keys.clear
62
+ end
63
+
64
+ def method_missing(sym, *args)
65
+ name = sym.to_s
66
+ if name.end_with?('=')
67
+ guess_missing_set(name[0..-2], args.first)
68
+ else
69
+ guess_missing_get(name)
70
+ end
71
+ end
72
+
73
+ def merge!(hash)
74
+ hash.each_pair { |key, value| set(key, value) }
75
+ end
76
+
77
+ def delete(key)
78
+ index = @keys.index(key)
79
+ return unless index
80
+ @keys.delete_at(index)
81
+ @values.delete_at(index)
82
+ end
83
+
84
+ def record_id=(value)
85
+ set(primary_key, value)
86
+ end
87
+
88
+ def db_insert
89
+ newid = DohDb::insert_hash(self, @table)
90
+ if newid != 0
91
+ set(primary_key, newid, false)
92
+ end
93
+ newid
94
+ end
95
+
96
+ def db_update
97
+ return if @changed_keys.empty?
98
+ before_db_update
99
+ builder = BuildSQL::SimpleUpdateRow.new(@table, nil, get(primary_key), primary_key)
100
+ @changed_keys.each {|key| builder.setc(key, get(key).to_sql)}
101
+ DohDb::query(builder)
102
+ after_db_update
103
+ @changed_keys.clear
104
+ end
105
+
106
+ protected
107
+ def primary_key
108
+ DohDb::find_primary_key(@table)
109
+ end
110
+
111
+ def before_db_update
112
+ end
113
+
114
+ def after_db_update
115
+ end
116
+
117
+ def guess_missing_get(key)
118
+ return get(key) if key?(key)
119
+ retval = get_virtual_field(key)
120
+ raise "unknown field: #{key}" unless retval
121
+ retval
122
+ end
123
+
124
+ def guess_missing_set(key, value)
125
+ set(key, value)
126
+ end
127
+
128
+ def get_virtual_field(name)
129
+ cached = @cached_virtuals[name]
130
+ return cached if cached
131
+ if idvalue = get(name + '_id')
132
+ @cached_virtuals[name] = DohDb::LinkedRow.build(name, idvalue.to_i)
133
+ else
134
+ nil
135
+ end
136
+ end
137
+ end
138
+
139
+ class SmartRow < AbstractSmartRow
140
+ def initialize(*args)
141
+ parsed_args = parse_initialize_args(*args)
142
+ if parsed_args.size == 3
143
+ @table = parsed_args.pop
144
+ end
145
+ super(*parsed_args)
146
+ end
147
+ end
148
+
149
+ class CustomSmartRow < AbstractSmartRow
150
+ def initialize(*args)
151
+ super(*parse_initialize_args(*args))
152
+ @table = self.class.default_table
153
+ end
154
+ end
155
+
156
+ end
@@ -0,0 +1,65 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+ begin
4
+ require 'mysql'
5
+ $mysql_loaded = true
6
+ rescue
7
+ $mysql_loaded = false
8
+ end
9
+
10
+ class Object
11
+ def to_sql
12
+ if $mysql_loaded
13
+ str = Mysql.escape_string(to_s)
14
+ else
15
+ str = non_mysql_escape_string
16
+ end
17
+ '"' + str + '"'
18
+ end
19
+ private
20
+ def non_mysql_escape_string
21
+ str.gsub('\\', '\\\\').gsub('\'', '\\\'').gsub('"', '\\"')
22
+ end
23
+ end
24
+
25
+ class NilClass
26
+ def to_sql
27
+ 'NULL'
28
+ end
29
+ end
30
+
31
+ class Numeric
32
+ def to_sql
33
+ to_s
34
+ end
35
+ end
36
+
37
+ class DateTime
38
+ def to_sql
39
+ '"' + strftime('%Y-%m-%d %H:%M:%S') + '"'
40
+ end
41
+ end
42
+
43
+ class TrueClass
44
+ def to_sql
45
+ '1'
46
+ end
47
+ end
48
+
49
+ class FalseClass
50
+ def to_sql
51
+ '0'
52
+ end
53
+ end
54
+
55
+ class BigDecimal
56
+ def to_sql
57
+ to_s('F')
58
+ end
59
+ end
60
+
61
+ class Array
62
+ def to_sql
63
+ '(' + collect { |elem| elem.to_sql }.join(',') + ')'
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ require 'doh/mysql/readonly_row'
2
+ require 'doh/mysql/default_type_guesser'
3
+
4
+ module DohDb
5
+
6
+ class TypedRowBuilder
7
+ def initialize(row_klass = nil, guesser = nil)
8
+ @row_klass = row_klass || ReadOnlyRow
9
+ @guesser = guesser || DefaultTypeGuesser
10
+ end
11
+
12
+ def build_rows(result_set)
13
+ meta_info = result_set.fetch_fields
14
+ field_names = meta_info.collect {|elem| elem.name}
15
+
16
+ retval = []
17
+ result_set.each do |row|
18
+ typed_values = []
19
+ row.each_with_index do |field, index|
20
+ typed_values[index] = @guesser.guess_type(field, meta_info[index])
21
+ end
22
+ retval.push(@row_klass.new(field_names, typed_values))
23
+ end
24
+ retval
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,33 @@
1
+ module DohDb
2
+
3
+ @@column_types = {}
4
+ def self.register_column_type(database, table, column, klass)
5
+ database = '__global__' # until database support is available
6
+ @@column_types[database] ||= {}
7
+ table = '__global__' if table.nil?
8
+ @@column_types[database][table] ||= {}
9
+ @@column_types[database][table][column] = klass
10
+ end
11
+
12
+ def self.find_column_type(database, table, column)
13
+ database = '__global__' # until database support is available
14
+ db_hash = @@column_types[database]; return nil unless db_hash
15
+ table = '__global__' if table.nil?
16
+ table_hash = db_hash[table]; return nil unless table_hash
17
+ table_hash[column]
18
+ end
19
+
20
+ def self.link_database_types(dest_db, source_db)
21
+ @@column_types[dest_db] = @@column_types[source_db]
22
+ end
23
+
24
+ @@row_types = {}
25
+ def self.register_row_type(table, klass)
26
+ @@row_types[table] = klass
27
+ end
28
+
29
+ def self.find_row_type(table)
30
+ @@row_types[table]
31
+ end
32
+
33
+ end