datamapper 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/CHANGELOG +31 -1
  2. data/MIT-LICENSE +1 -1
  3. data/README +9 -1
  4. data/example.rb +23 -15
  5. data/lib/data_mapper.rb +5 -0
  6. data/lib/data_mapper/adapters/abstract_adapter.rb +9 -207
  7. data/lib/data_mapper/adapters/mysql_adapter.rb +132 -108
  8. data/lib/data_mapper/adapters/postgresql_adapter.rb +242 -0
  9. data/lib/data_mapper/adapters/sql/coersion.rb +74 -0
  10. data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +140 -0
  11. data/lib/data_mapper/adapters/sql/commands/conditions.rb +161 -0
  12. data/lib/data_mapper/adapters/sql/commands/delete_command.rb +113 -0
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +296 -0
  14. data/lib/data_mapper/adapters/sql/commands/save_command.rb +141 -0
  15. data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +33 -0
  16. data/lib/data_mapper/adapters/sql/mappings/column.rb +91 -0
  17. data/lib/data_mapper/adapters/sql/mappings/schema.rb +30 -0
  18. data/lib/data_mapper/adapters/sql/mappings/table.rb +143 -0
  19. data/lib/data_mapper/adapters/sql/quoting.rb +38 -0
  20. data/lib/data_mapper/adapters/sql_adapter.rb +163 -0
  21. data/lib/data_mapper/adapters/sqlite3_adapter.rb +155 -116
  22. data/lib/data_mapper/associations.rb +2 -0
  23. data/lib/data_mapper/associations/advanced_has_many_association.rb +55 -0
  24. data/lib/data_mapper/associations/belongs_to_association.rb +2 -2
  25. data/lib/data_mapper/associations/has_many_association.rb +3 -3
  26. data/lib/data_mapper/associations/has_one_association.rb +2 -2
  27. data/lib/data_mapper/base.rb +30 -11
  28. data/lib/data_mapper/callbacks.rb +4 -1
  29. data/lib/data_mapper/database.rb +8 -41
  30. data/lib/data_mapper/identity_map.rb +23 -3
  31. data/lib/data_mapper/session.rb +34 -186
  32. data/lib/data_mapper/{extensions → support}/active_record_impersonation.rb +16 -12
  33. data/lib/data_mapper/support/blank.rb +35 -0
  34. data/lib/data_mapper/support/connection_pool.rb +2 -1
  35. data/lib/data_mapper/support/string.rb +27 -0
  36. data/lib/data_mapper/support/struct.rb +26 -0
  37. data/lib/data_mapper/validations/unique_validator.rb +1 -3
  38. data/lib/data_mapper/validations/validation_helper.rb +1 -1
  39. data/performance.rb +24 -7
  40. data/profile_data_mapper.rb +24 -2
  41. data/rakefile.rb +2 -2
  42. data/spec/basic_finder.rb +2 -2
  43. data/spec/belongs_to.rb +1 -1
  44. data/spec/delete_command_spec.rb +9 -0
  45. data/spec/fixtures/zoos.yaml +4 -0
  46. data/spec/has_many.rb +1 -1
  47. data/spec/load_command_spec.rb +44 -0
  48. data/spec/models/zoo.rb +2 -0
  49. data/spec/save_command_spec.rb +13 -0
  50. data/spec/spec_helper.rb +10 -1
  51. data/spec/support/string_spec.rb +7 -0
  52. data/spec/validates_confirmation_of.rb +1 -1
  53. data/spec/validates_format_of.rb +1 -1
  54. data/spec/validates_length_of.rb +1 -1
  55. data/spec/validations.rb +1 -1
  56. metadata +23 -20
  57. data/lib/data_mapper/extensions/callback_helpers.rb +0 -35
  58. data/lib/data_mapper/loaded_set.rb +0 -45
  59. data/lib/data_mapper/mappings/column.rb +0 -78
  60. data/lib/data_mapper/mappings/schema.rb +0 -28
  61. data/lib/data_mapper/mappings/table.rb +0 -99
  62. data/lib/data_mapper/queries/conditions.rb +0 -141
  63. data/lib/data_mapper/queries/connection.rb +0 -34
  64. data/lib/data_mapper/queries/create_table_statement.rb +0 -38
  65. data/lib/data_mapper/queries/delete_statement.rb +0 -17
  66. data/lib/data_mapper/queries/drop_table_statement.rb +0 -17
  67. data/lib/data_mapper/queries/insert_statement.rb +0 -29
  68. data/lib/data_mapper/queries/reader.rb +0 -42
  69. data/lib/data_mapper/queries/result.rb +0 -19
  70. data/lib/data_mapper/queries/select_statement.rb +0 -103
  71. data/lib/data_mapper/queries/table_exists_statement.rb +0 -17
  72. data/lib/data_mapper/queries/truncate_table_statement.rb +0 -17
  73. data/lib/data_mapper/queries/update_statement.rb +0 -25
data/CHANGELOG CHANGED
@@ -1,2 +1,32 @@
1
1
  -- 0.1.0
2
- * Initial Public Release
2
+ * Initial Public Release
3
+
4
+ -- 0.1.1
5
+ * Removed /lib/data_mapper/extensions
6
+ * Moved ActiveRecordImpersonation into DataMapper::Support module
7
+ * Moved CallbackHelper methods into DataMapper::Base class
8
+ * Moved ValidationHelper into DataMapper::Validations module
9
+ * Removed LoadedSet since it's not necessary for it to reference the Database, so it's nothing more than an array now; Replaced with Array
10
+ * Modified data_mapper.rb to load DataMapper::Support::Enumerable
11
+ * Modified example.rb and performance.rb to require 'lib/data_mapper' instead of modifying $LOADPATH
12
+ * Created SqlAdapter base-class
13
+ * Refactored MysqlAdapter to use SqlAdapter superclass
14
+ * Refactored Sqlite3Adapter to use SqlAdapter superclass
15
+ * Moved /lib/data_mapper/queries to /lib/data_mapper/adapters/sql/queries
16
+ * Moved Connection, Result and Reader classes along with Coersion and Quoting modules to DataMapper::Adapters::Sql module
17
+ * Moved DataMapper::Adapters::Sql::Queries to ::Commands
18
+ * Moved Mappings to SqlAdapter
19
+ * Added monolithic DeleteCommand
20
+ * Added monolithic SaveCommand
21
+ * Added TableExistsCommand
22
+ * Moved save/delete logic out of Session
23
+ * Added create-table functionality to SaveCommand
24
+ * Cleaned up Session; #find no longer supported, use #all or #first
25
+ * Moved object materialization into LoadCommand
26
+ * Migrated Sqlite3Adapter::Commands
27
+ * Added Session#query support back in
28
+ * Removed Connection/Reader/Result classes
29
+ * Set DataMapper::Base#key on load to avoid double-hit against Schema
30
+ * Added DataMapper::Support::Struct for increased Session#query performance
31
+ * Added AdvancedHasManyAssociation (preview status)
32
+ * Added benchmarks comparing ActiveRecord::Base::find_by_sql with Session#query
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Samuel Smoot
1
+ Copyright (c) 2007 Sam Smoot
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README CHANGED
@@ -1 +1,9 @@
1
- Start out with example.rb and go from there if you want to try it out live.
1
+ Start out with example.rb and go from there if you want to try it out live.
2
+
3
+
4
+ == Conventions
5
+
6
+ Merb's coding conventions are a fantastic start: (http://merb.devjavu.com/#ContributingtoMerb)
7
+
8
+ DataMapper could use some documentation help. If you'd like to contribute, the example you'll
9
+ find on the above link is top-notch.
data/example.rb CHANGED
@@ -1,25 +1,33 @@
1
- $LOAD_PATH.unshift('lib')
1
+ #!/usr/bin/env ruby
2
2
 
3
- require 'data_mapper'
3
+ require 'lib/data_mapper'
4
4
 
5
- if ENV['ADAPTER'] == 'sqlite3'
6
- DataMapper::Database.setup do
7
- adapter 'sqlite3'
8
- database 'data_mapper_1.db'
5
+ ENV['ADAPTER'] ||= 'mysql'
6
+
7
+ DataMapper::Database.setup do
8
+ adapter ENV['ADAPTER']
9
+
10
+ unless ENV['LOGGER'] == 'false'
9
11
  log_stream 'example.log'
10
12
  log_level Logger::DEBUG
11
13
  end
12
- else
13
- DataMapper::Database.setup do
14
- adapter 'mysql'
15
- host 'localhost'
16
- username 'root'
17
- database 'data_mapper_1'
18
- log_stream 'example.log'
19
- log_level Logger::DEBUG
14
+
15
+ database_name = 'data_mapper_1'
16
+
17
+ case ENV['ADAPTER']
18
+ when 'postgresql' then
19
+ username 'postgres'
20
+ when 'mysql' then
21
+ username 'root'
22
+ when 'sqlite3' then
23
+ database_name << '.db'
20
24
  end
25
+
26
+ database database_name
21
27
  end
22
28
 
23
29
  Dir[File.dirname(__FILE__) + '/spec/models/*.rb'].each do |path|
24
30
  load path
25
- end
31
+ end
32
+
33
+ # p Zoo.all
data/lib/data_mapper.rb CHANGED
@@ -3,10 +3,15 @@
3
3
  $LOAD_PATH.unshift(File.dirname(__FILE__))
4
4
 
5
5
  # Require the basics...
6
+ require 'set'
7
+ require 'fastthread'
8
+ require 'data_mapper/support/blank'
9
+ require 'data_mapper/support/enumerable'
6
10
  require 'data_mapper/support/symbol'
7
11
  require 'data_mapper/support/string'
8
12
  require 'data_mapper/support/proc'
9
13
  require 'data_mapper/support/inflector'
14
+ require 'data_mapper/support/struct'
10
15
  require 'data_mapper/database'
11
16
  require 'data_mapper/base'
12
17
 
@@ -1,48 +1,6 @@
1
- require 'data_mapper/queries/select_statement'
2
- require 'data_mapper/queries/insert_statement'
3
- require 'data_mapper/queries/update_statement'
4
- require 'data_mapper/queries/delete_statement'
5
- require 'data_mapper/queries/truncate_table_statement'
6
- require 'data_mapper/queries/create_table_statement'
7
- require 'data_mapper/queries/drop_table_statement'
8
- require 'data_mapper/queries/table_exists_statement'
9
- require 'data_mapper/queries/connection'
10
-
11
1
  module DataMapper
12
-
13
- # An Adapter is really a Factory for three types of object,
14
- # so they can be selectively sub-classed where needed.
15
- #
16
- # The first type is a Query. The Query is an object describing
17
- # the database-specific operations we wish to perform, in an
18
- # abstract manner. For example: While most if not all databases
19
- # support a mechanism for limiting the size of results returned,
20
- # some use a "LIMIT" keyword, while others use a "TOP" keyword.
21
- # We can set a SelectStatement#limit field then, and allow
22
- # the adapter to override the underlying SQL generated.
23
- # Refer to DataMapper::Queries.
24
- #
25
- # The second type provided by the Adapter is a DataMapper::Connection.
26
- # This allows us to execute queries and return results in a clear and
27
- # uniform manner we can use throughout the DataMapper.
28
- #
29
- # The final type provided is a DataMapper::Transaction.
30
- # Transactions are duck-typed Connections that span multiple queries.
31
- #
32
- # Note: It is assumed that the Adapter implements it's own
33
- # ConnectionPool if any since some libraries implement their own at
34
- # a low-level, and it wouldn't make sense to pay a performance
35
- # cost twice by implementing a secondary pool in the DataMapper itself.
36
- # If the library being adapted does not provide such functionality,
37
- # DataMapper::Support::ConnectionPool can be used.
38
2
  module Adapters
39
3
 
40
- # You must inherit from the Abstract::Adapter, and implement the
41
- # required methods to adapt a database library for use with the DataMapper.
42
- #
43
- # NOTE: By inheriting from AbstractAdapter, you get a copy of all the
44
- # standard sub-modules (Quoting, Coersion and Queries) in your own Adapter.
45
- # You can extend and overwrite these copies without affecting the originals.
46
4
  class AbstractAdapter
47
5
 
48
6
  # Instantiate an Adapter by passing it a DataMapper::Database
@@ -50,179 +8,23 @@ module DataMapper
50
8
  def initialize(configuration)
51
9
  @configuration = configuration
52
10
  end
53
-
54
- def connection(&block)
11
+
12
+ def delete(instance_or_klass, options = nil)
55
13
  raise NotImplementedError.new
56
14
  end
57
-
58
- def transaction(&block)
15
+
16
+ def save(session, instance)
59
17
  raise NotImplementedError.new
60
18
  end
61
19
 
62
- # This callback copies and sub-classes modules and classes
63
- # in the AbstractAdapter to the inherited class so you don't
64
- # have to copy and paste large blocks of code from the
65
- # AbstractAdapter.
66
- #
67
- # Basically, when inheriting from the AbstractAdapter, you
68
- # aren't just inheriting a single class, you're inheriting
69
- # a whole graph of Types. For convenience.
70
- def self.inherited(base)
71
-
72
- quoting = base.const_set('Quoting', Module.new)
73
- quoting.send(:include, Quoting)
74
-
75
- coersion = base.const_set('Coersion', Module.new)
76
- coersion.send(:include, Coersion)
77
-
78
- queries = base.const_set('Queries', Module.new)
79
-
80
- Queries.constants.each do |name|
81
- queries.const_set(name, Class.new(Queries.const_get(name)))
82
- end
83
-
84
- base.const_set('TYPES', TYPES.dup)
85
-
86
- base.const_set('SYNTAX', SYNTAX.dup)
20
+ def load(session, klass, options)
21
+ raise NotImplementedError.new
87
22
  end
88
23
 
89
- TYPES = {
90
- :integer => 'int'.freeze,
91
- :string => 'varchar'.freeze,
92
- :text => 'text'.freeze,
93
- :class => 'varchar'.freeze
94
- }
95
-
96
- SYNTAX = {
97
- :auto_increment => 'auto_increment'.freeze
98
- }
99
-
100
- # Quoting is a mixin that extends your DataMapper::Database singleton-class
101
- # to allow for object-name and value quoting to be exposed to the queries.
102
- #
103
- # DESIGN: Is there any need for this outside of the query objects? Should
104
- # we just include it in our query object subclasses and not rely on a Quoting
105
- # mixin being part of the "standard" Adapter interface?
106
- module Quoting
107
-
108
- def quote_table_name(name)
109
- name.ensure_wrapped_with('"')
110
- end
111
-
112
- def quote_column_name(name)
113
- name.ensure_wrapped_with('"')
114
- end
24
+ def log
25
+ @configuration.log
26
+ end
115
27
 
116
- def quote_value(value)
117
- return 'NULL' if value.nil?
118
-
119
- case value
120
- when Numeric then value.to_s
121
- when String then "'#{value.gsub("'", "''")}'"
122
- when Class then "'#{value.name}'"
123
- when Date then "'#{value.to_s}'"
124
- when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
125
- when TrueClass, FalseClass then value.to_s.upcase
126
- else raise "Don't know how to quote #{value.inspect}"
127
- end
128
- end
129
-
130
- end # module Quoting
131
-
132
- # Coersion is a mixin that allows for coercing database values to Ruby Types.
133
- #
134
- # DESIGN: Probably should handle the opposite scenario here too. I believe that's
135
- # currently in DataMapper::Database, which is obviously not a very good spot for
136
- # it.
137
- module Coersion
138
-
139
- def type_cast_value(type, raw_value)
140
- return nil if raw_value.nil?
141
-
142
- case type
143
- when :class then Kernel::const_get(raw_value)
144
- when :string, :text then
145
- return nil if raw_value.nil?
146
- value_as_string = raw_value.to_s.strip
147
- return nil if value_as_string.empty?
148
- value_as_string
149
- when :integer then
150
- return nil if raw_value.nil? || (raw_value.kind_of?(String) && raw_value.empty?)
151
- begin
152
- Integer(raw_value)
153
- rescue ArgumentError
154
- nil
155
- end
156
- else
157
- if respond_to?("type_cast_#{type}")
158
- send("type_cast_#{type}", raw_value)
159
- else
160
- raise "Don't know how to type-cast #{{ type => raw_value }.inspect }"
161
- end
162
- end
163
- end
164
-
165
- end # module Coersion
166
-
167
- # You define your custom queries in a sub-module called Queries.
168
- # If you don't need to redefine any of the default functionality/syntax,
169
- # you can just create constants that point to the standard queries:
170
- #
171
- # SelectStatement = DataMapper::Queries::SelectStatement
172
- #
173
- # It's just as easy to turn that into a sub-class however:
174
- #
175
- # class SelectStatement < DataMapper::Queries::SelectStatement
176
- # end
177
- #
178
- # You sub-class and edit instead of overwrite because you want to
179
- # make sure your changes only affect this database adapter and avoid
180
- # introducing incompatibilities into other adapters.
181
- module Queries
182
-
183
- # Your Connection class is one of two that will be almost completely custom.
184
- # Refer to DataMapper::Queries::Connection for the required interface.
185
- class Connection < DataMapper::Queries::Connection
186
- end
187
-
188
- # Reader is the other Connection related class that will be almost completely custom.
189
- # The idea with the Reader is to avoid creating a large Array of Hash objects to
190
- # represent rows since the hashes will be discarded almost immediately. Create only
191
- # what you need. So the reader creates a single Hash to associate columns with their
192
- # ordinals in the result set, then indexing the Reader for each row results in looking
193
- # up the column index, then the value for that index in the current row array.
194
- class Reader < DataMapper::Queries::Reader
195
- end
196
-
197
- class Result < DataMapper::Queries::Result
198
- end
199
-
200
- class DeleteStatement < DataMapper::Queries::DeleteStatement
201
- end
202
-
203
- class InsertStatement < DataMapper::Queries::InsertStatement
204
- end
205
-
206
- class SelectStatement < DataMapper::Queries::SelectStatement
207
- end
208
-
209
- class TruncateTableStatement < DataMapper::Queries::TruncateTableStatement
210
- end
211
-
212
- class UpdateStatement < DataMapper::Queries::UpdateStatement
213
- end
214
-
215
- class CreateTableStatement < DataMapper::Queries::CreateTableStatement
216
- end
217
-
218
- class DropTableStatement < DataMapper::Queries::DropTableStatement
219
- end
220
-
221
- class TableExistsStatement < DataMapper::Queries::TableExistsStatement
222
- end
223
-
224
- end # module Queries
225
-
226
28
  end # class AbstractAdapter
227
29
 
228
30
  end # module Adapters
@@ -1,4 +1,4 @@
1
- require 'data_mapper/adapters/abstract_adapter'
1
+ require 'data_mapper/adapters/sql_adapter'
2
2
  require 'data_mapper/support/connection_pool'
3
3
 
4
4
  begin
@@ -16,155 +16,179 @@ end
16
16
  module DataMapper
17
17
  module Adapters
18
18
 
19
- class MysqlAdapter < AbstractAdapter
19
+ class MysqlAdapter < SqlAdapter
20
20
 
21
21
  def initialize(configuration)
22
22
  super
23
- @connections = Support::ConnectionPool.new { Queries::Connection.new(@configuration) }
23
+
24
+ create_connection = lambda do
25
+ Mysql.new(configuration.host, configuration.username, configuration.password, configuration.database)
26
+ end
27
+
28
+ # Initialize the connection pool.
29
+ if single_threaded?
30
+ @connection_factory = create_connection
31
+ @active_connection = create_connection[]
32
+ else
33
+ @connections = Support::ConnectionPool.new(&create_connection)
34
+ end
24
35
  end
25
36
 
37
+ # Yields an available connection. Flushes the connection-pool if
38
+ # the connection returns an error.
26
39
  def connection
27
- raise ArgumentError.new('MysqlAdapter#connection requires a block-parameter') unless block_given?
28
- begin
29
- @connections.hold { |connection| yield connection }
30
- rescue Mysql::Error => me
31
-
32
- @configuration.log.fatal(me)
33
-
34
- @connections.available_connections.each do |sock|
40
+
41
+ if single_threaded?
42
+ begin
43
+ # BUG: Single_threaded mode totally breaks shit right now. No real idea why just from
44
+ # eyeballing this. Probably should move this into the SqlAdapter anyways and just
45
+ # force derived adapters to implement a #create_connection() and #close_connection(conn) methods.
46
+ yield(@active_connection)
47
+ rescue Mysql::Error => me
48
+ @configuration.log.fatal(me)
49
+
35
50
  begin
36
- sock.close
51
+ @active_connection.close
37
52
  rescue => se
38
53
  @configuration.log.error(se)
39
54
  end
55
+
56
+ @active_connection = @connection_factory[]
40
57
  end
58
+ else
59
+ begin
60
+ @connections.hold { |dbh| yield(dbh) }
61
+ rescue Mysql::Error => me
41
62
 
42
- @connections.available_connections.clear
43
- raise me
63
+ @configuration.log.fatal(me)
64
+
65
+ @connections.available_connections.each do |sock|
66
+ begin
67
+ sock.close
68
+ rescue => se
69
+ @configuration.log.error(se)
70
+ end
71
+ end
72
+
73
+ @connections.available_connections.clear
74
+ raise me
75
+ end
44
76
  end
45
77
  end
46
78
 
47
- module Quoting
48
-
49
- def quote_table_name(name)
50
- name.ensure_wrapped_with('`')
51
- end
52
-
53
- def quote_column_name(name)
54
- name.ensure_wrapped_with('`')
79
+ def execute(*args)
80
+ connection do |db|
81
+ reader = db.query(escape_sql(*args))
82
+ result = yield(reader, reader.fetch_fields.map { |field| field.name })
83
+ reader.free
84
+ result
55
85
  end
56
-
57
- end # module Quoting
58
-
59
- module Coersion
60
-
61
- def type_cast_boolean(value)
62
- case value
63
- when TrueClass, FalseClass then value
64
- when "1", "true", "TRUE" then true
65
- when "0", nil then false
66
- else "Can't type-cast #{value.inspect} to a boolean"
67
- end
68
- end
69
-
70
- def type_cast_datetime(value)
71
- case value
72
- when DateTime then value
73
- when Date then DateTime.new(value)
74
- when String then DateTime::parse(value)
75
- else "Can't type-cast #{value.inspect} to a datetime"
76
- end
77
- end
78
-
79
- end # module Coersion
86
+ end
80
87
 
81
- module Queries
88
+ def query(*args)
82
89
 
83
- class Connection
84
-
85
- def initialize(database)
86
- @database = database
87
- @dbh = Mysql.new(database.host, database.username, database.password, database.database)
88
- database.log.debug("Initializing Connection for Database[#{database.name}]")
89
- super(database.log)
90
- end
90
+ execute(*args) do |reader, fields|
91
+ struct = Support::Struct::define(fields)
92
+
93
+ results = []
91
94
 
92
- def execute(statement)
93
- send_query(statement)
94
- Result.new(@dbh.affected_rows, @dbh.insert_id)
95
+ reader.each do |row|
96
+ results << struct.new(row)
95
97
  end
98
+ results
99
+
100
+ end
101
+ end
96
102
 
97
- def query(statement)
98
- Reader.new(send_query(statement))
99
- end
103
+ TABLE_QUOTING_CHARACTER = '`'.freeze
104
+ COLUMN_QUOTING_CHARACTER = '`'.freeze
105
+
106
+ TRUE_ALIASES.unshift('1'.freeze)
107
+ FALSE_ALIASES.unshift('0'.freeze)
100
108
 
101
- def close
102
- @dbh.close
109
+ module Commands
110
+
111
+ class TableExistsCommand
112
+ def call
113
+ sql = to_sql
114
+ @adapter.log.debug(sql)
115
+ reader = @adapter.connection { |db| db.query(sql) }
116
+ result = reader.num_rows > 0
117
+ reader.free
118
+ result
119
+ end
120
+ end
121
+
122
+ class DeleteCommand
123
+
124
+ def execute(sql)
125
+ @adapter.connection do |db|
126
+ @adapter.log.debug(sql)
127
+ db.query(sql)
128
+ db.affected_rows > 0
129
+ end
103
130
  end
104
131
 
105
- private
106
- def send_query(statement)
107
- sql = statement.respond_to?(:to_sql) ? statement.to_sql : statement
108
- log.debug("Database[#{@database.name}] => #{sql}")
109
- @dbh.query(sql)
132
+ def execute_drop(sql)
133
+ @adapter.log.debug(sql)
134
+ @adapter.connection { |db| db.query(sql) }
135
+ true
110
136
  end
111
137
 
112
- end # class Connection
138
+ end
113
139
 
114
- class Reader
115
-
116
- include Enumerable
140
+ class SaveCommand
117
141
 
118
- def initialize(results)
119
- @results = results
120
- @columns = {}
121
- results.fetch_fields.each_with_index do |field, index|
122
- @columns[field.name] = index
142
+ def execute_insert(sql)
143
+ @adapter.connection do |db|
144
+ @adapter.log.debug(sql)
145
+ db.query(sql)
146
+ db.insert_id
123
147
  end
124
- @current_row_index = 0
125
148
  end
126
149
 
127
- def eof?
128
- records_affected <= @current_row_index
150
+ def execute_update(sql)
151
+ @adapter.connection do |db|
152
+ @adapter.log.debug(sql)
153
+ db.query(sql)
154
+ db.affected_rows > 0
155
+ end
129
156
  end
130
157
 
131
- def records_affected
132
- @results.num_rows
158
+ def execute_create_table(sql)
159
+ @adapter.log.debug(sql)
160
+ @adapter.connection { |db| db.query(sql) }
161
+ true
133
162
  end
134
163
 
135
- def next
136
- @current_row_index += 1
137
- @current_row = @results.fetch_row
138
- self
139
- end
140
-
141
- def each
142
- @results.each do |row|
143
- @current_row = row
144
- yield self
145
- end
164
+ end
165
+
166
+ class LoadCommand
167
+ def eof?(reader)
168
+ reader.num_rows == 0
146
169
  end
147
-
148
- def [](column)
149
- index = @columns[column]
150
- return nil if index.nil?
151
- @current_row[index]
170
+
171
+ def close_reader(reader)
172
+ reader.free
152
173
  end
153
174
 
154
- def each_pair
155
- @columns.each_pair do |column_name, index|
156
- yield(column_name, @current_row[index])
157
- end
175
+ def execute(sql)
176
+ @adapter.log.debug(sql)
177
+ @adapter.connection { |db| db.query(to_sql) }
158
178
  end
159
179
 
160
- def close
161
- @results.free
180
+ def fetch_one(reader)
181
+ fetch_all(reader).first
162
182
  end
163
-
164
- end # class Reader
165
-
166
- end # module Queries
183
+
184
+ def fetch_all(reader)
185
+ load_instances(reader.fetch_fields.map { |field| field.name }, reader)
186
+ end
187
+
188
+ end
167
189
 
190
+ end
191
+
168
192
  end # class MysqlAdapter
169
193
 
170
194
  end # module Adapters