dohmysql 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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