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.
- data/CHANGELOG +31 -1
- data/MIT-LICENSE +1 -1
- data/README +9 -1
- data/example.rb +23 -15
- data/lib/data_mapper.rb +5 -0
- data/lib/data_mapper/adapters/abstract_adapter.rb +9 -207
- data/lib/data_mapper/adapters/mysql_adapter.rb +132 -108
- data/lib/data_mapper/adapters/postgresql_adapter.rb +242 -0
- data/lib/data_mapper/adapters/sql/coersion.rb +74 -0
- data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +140 -0
- data/lib/data_mapper/adapters/sql/commands/conditions.rb +161 -0
- data/lib/data_mapper/adapters/sql/commands/delete_command.rb +113 -0
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +296 -0
- data/lib/data_mapper/adapters/sql/commands/save_command.rb +141 -0
- data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +33 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +91 -0
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +30 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +143 -0
- data/lib/data_mapper/adapters/sql/quoting.rb +38 -0
- data/lib/data_mapper/adapters/sql_adapter.rb +163 -0
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +155 -116
- data/lib/data_mapper/associations.rb +2 -0
- data/lib/data_mapper/associations/advanced_has_many_association.rb +55 -0
- data/lib/data_mapper/associations/belongs_to_association.rb +2 -2
- data/lib/data_mapper/associations/has_many_association.rb +3 -3
- data/lib/data_mapper/associations/has_one_association.rb +2 -2
- data/lib/data_mapper/base.rb +30 -11
- data/lib/data_mapper/callbacks.rb +4 -1
- data/lib/data_mapper/database.rb +8 -41
- data/lib/data_mapper/identity_map.rb +23 -3
- data/lib/data_mapper/session.rb +34 -186
- data/lib/data_mapper/{extensions → support}/active_record_impersonation.rb +16 -12
- data/lib/data_mapper/support/blank.rb +35 -0
- data/lib/data_mapper/support/connection_pool.rb +2 -1
- data/lib/data_mapper/support/string.rb +27 -0
- data/lib/data_mapper/support/struct.rb +26 -0
- data/lib/data_mapper/validations/unique_validator.rb +1 -3
- data/lib/data_mapper/validations/validation_helper.rb +1 -1
- data/performance.rb +24 -7
- data/profile_data_mapper.rb +24 -2
- data/rakefile.rb +2 -2
- data/spec/basic_finder.rb +2 -2
- data/spec/belongs_to.rb +1 -1
- data/spec/delete_command_spec.rb +9 -0
- data/spec/fixtures/zoos.yaml +4 -0
- data/spec/has_many.rb +1 -1
- data/spec/load_command_spec.rb +44 -0
- data/spec/models/zoo.rb +2 -0
- data/spec/save_command_spec.rb +13 -0
- data/spec/spec_helper.rb +10 -1
- data/spec/support/string_spec.rb +7 -0
- data/spec/validates_confirmation_of.rb +1 -1
- data/spec/validates_format_of.rb +1 -1
- data/spec/validates_length_of.rb +1 -1
- data/spec/validations.rb +1 -1
- metadata +23 -20
- data/lib/data_mapper/extensions/callback_helpers.rb +0 -35
- data/lib/data_mapper/loaded_set.rb +0 -45
- data/lib/data_mapper/mappings/column.rb +0 -78
- data/lib/data_mapper/mappings/schema.rb +0 -28
- data/lib/data_mapper/mappings/table.rb +0 -99
- data/lib/data_mapper/queries/conditions.rb +0 -141
- data/lib/data_mapper/queries/connection.rb +0 -34
- data/lib/data_mapper/queries/create_table_statement.rb +0 -38
- data/lib/data_mapper/queries/delete_statement.rb +0 -17
- data/lib/data_mapper/queries/drop_table_statement.rb +0 -17
- data/lib/data_mapper/queries/insert_statement.rb +0 -29
- data/lib/data_mapper/queries/reader.rb +0 -42
- data/lib/data_mapper/queries/result.rb +0 -19
- data/lib/data_mapper/queries/select_statement.rb +0 -103
- data/lib/data_mapper/queries/table_exists_statement.rb +0 -17
- data/lib/data_mapper/queries/truncate_table_statement.rb +0 -17
- 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
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
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'data_mapper'
|
3
|
+
require 'lib/data_mapper'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
11
|
+
|
12
|
+
def delete(instance_or_klass, options = nil)
|
55
13
|
raise NotImplementedError.new
|
56
14
|
end
|
57
|
-
|
58
|
-
def
|
15
|
+
|
16
|
+
def save(session, instance)
|
59
17
|
raise NotImplementedError.new
|
60
18
|
end
|
61
19
|
|
62
|
-
|
63
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
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/
|
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 <
|
19
|
+
class MysqlAdapter < SqlAdapter
|
20
20
|
|
21
21
|
def initialize(configuration)
|
22
22
|
super
|
23
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
43
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
name
|
51
|
-
|
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
|
-
|
88
|
+
def query(*args)
|
82
89
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
93
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
102
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
138
|
+
end
|
113
139
|
|
114
|
-
class
|
115
|
-
|
116
|
-
include Enumerable
|
140
|
+
class SaveCommand
|
117
141
|
|
118
|
-
def
|
119
|
-
@
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
128
|
-
|
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
|
132
|
-
@
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
149
|
-
|
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
|
155
|
-
@
|
156
|
-
|
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
|
161
|
-
|
180
|
+
def fetch_one(reader)
|
181
|
+
fetch_all(reader).first
|
162
182
|
end
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|