datamapper 0.1.0 → 0.1.1

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 (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