activerecord_authorails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3043 -0
- data/README +360 -0
- data/RUNNING_UNIT_TESTS +64 -0
- data/Rakefile +226 -0
- data/examples/associations.png +0 -0
- data/examples/associations.rb +87 -0
- data/examples/shared_setup.rb +15 -0
- data/examples/validation.rb +85 -0
- data/install.rb +30 -0
- data/lib/active_record.rb +85 -0
- data/lib/active_record/acts/list.rb +244 -0
- data/lib/active_record/acts/nested_set.rb +211 -0
- data/lib/active_record/acts/tree.rb +89 -0
- data/lib/active_record/aggregations.rb +191 -0
- data/lib/active_record/associations.rb +1637 -0
- data/lib/active_record/associations/association_collection.rb +190 -0
- data/lib/active_record/associations/association_proxy.rb +158 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
- data/lib/active_record/associations/has_many_association.rb +210 -0
- data/lib/active_record/associations/has_many_through_association.rb +247 -0
- data/lib/active_record/associations/has_one_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +2164 -0
- data/lib/active_record/calculations.rb +270 -0
- data/lib/active_record/callbacks.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
- data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
- data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
- data/lib/active_record/deprecated_associations.rb +104 -0
- data/lib/active_record/deprecated_finders.rb +44 -0
- data/lib/active_record/fixtures.rb +628 -0
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +394 -0
- data/lib/active_record/observer.rb +178 -0
- data/lib/active_record/query_cache.rb +64 -0
- data/lib/active_record/reflection.rb +222 -0
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +149 -0
- data/lib/active_record/timestamp.rb +51 -0
- data/lib/active_record/transactions.rb +136 -0
- data/lib/active_record/validations.rb +843 -0
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +1214 -0
- data/lib/active_record/vendor/simple.rb +693 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
- data/lib/active_record/wrappings.rb +58 -0
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +59 -0
- data/test/abstract_unit.rb +77 -0
- data/test/active_schema_test_mysql.rb +31 -0
- data/test/adapter_test.rb +87 -0
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +95 -0
- data/test/all.sh +8 -0
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/callbacks_test.rb +126 -0
- data/test/associations/cascaded_eager_loading_test.rb +138 -0
- data/test/associations/eager_test.rb +393 -0
- data/test/associations/extension_test.rb +42 -0
- data/test/associations/join_model_test.rb +497 -0
- data/test/associations_test.rb +1809 -0
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +1586 -0
- data/test/binary_test.rb +37 -0
- data/test/calculations_test.rb +219 -0
- data/test/callbacks_test.rb +377 -0
- data/test/class_inheritable_attributes_test.rb +32 -0
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +23 -0
- data/test/connections/native_sqlite/connection.rb +34 -0
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sqlserver/connection.rb +23 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_sqlite.rb +64 -0
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +60 -0
- data/test/deprecated_associations_test.rb +396 -0
- data/test/deprecated_finder_test.rb +151 -0
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +504 -0
- data/test/fixtures/accounts.yml +28 -0
- data/test/fixtures/author.rb +99 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/auto_id.rb +4 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +20 -0
- data/test/fixtures/column_name.rb +3 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +107 -0
- data/test/fixtures/company_in_module.rb +59 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/course.rb +3 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +55 -0
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +32 -0
- data/test/fixtures/db_definitions/db2.sql +231 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
- data/test/fixtures/db_definitions/firebird.sql +304 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
- data/test/fixtures/db_definitions/frontbase.sql +268 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
- data/test/fixtures/db_definitions/mysql.sql +234 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +302 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
- data/test/fixtures/db_definitions/oracle.sql +325 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
- data/test/fixtures/db_definitions/postgresql.sql +263 -0
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +5 -0
- data/test/fixtures/db_definitions/schema.rb +60 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
- data/test/fixtures/db_definitions/sqlite.sql +215 -0
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
- data/test/fixtures/db_definitions/sqlserver.sql +243 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
- data/test/fixtures/db_definitions/sybase.sql +218 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/default.rb +2 -0
- data/test/fixtures/developer.rb +52 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/developers_projects/david_action_controller +3 -0
- data/test/fixtures/developers_projects/david_active_record +3 -0
- data/test/fixtures/developers_projects/jamis_active_record +2 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrant.rb +3 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/joke.rb +6 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixin.rb +63 -0
- data/test/fixtures/mixins.yml +127 -0
- data/test/fixtures/movie.rb +5 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/post.rb +58 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +37 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +6 -0
- data/test/fixtures/subscribers/first +2 -0
- data/test/fixtures/subscribers/second +2 -0
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +6 -0
- data/test/fixtures/taggings.yml +18 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +25 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +401 -0
- data/test/inheritance_test.rb +205 -0
- data/test/lifecycle_test.rb +137 -0
- data/test/locking_test.rb +190 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +768 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +196 -0
- data/test/mixin_test.rb +550 -0
- data/test/modules_test.rb +34 -0
- data/test/multiple_db_test.rb +60 -0
- data/test/pk_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +159 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +96 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +230 -0
- data/test/unconnected_test.rb +32 -0
- data/test/validations_test.rb +1097 -0
- data/test/xml_serialization_test.rb +125 -0
- metadata +365 -0
@@ -0,0 +1,414 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module MysqlCompat #:nodoc:
|
5
|
+
# add all_hashes method to standard mysql-c bindings or pure ruby version
|
6
|
+
def self.define_all_hashes_method!
|
7
|
+
raise 'Mysql not loaded' unless defined?(::Mysql)
|
8
|
+
|
9
|
+
target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
|
10
|
+
return if target.instance_methods.include?('all_hashes')
|
11
|
+
|
12
|
+
# Ruby driver has a version string and returns null values in each_hash
|
13
|
+
# C driver >= 2.7 returns null values in each_hash
|
14
|
+
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
|
15
|
+
target.class_eval <<-'end_eval'
|
16
|
+
def all_hashes
|
17
|
+
rows = []
|
18
|
+
each_hash { |row| rows << row }
|
19
|
+
rows
|
20
|
+
end
|
21
|
+
end_eval
|
22
|
+
|
23
|
+
# adapters before 2.7 don't have a version constant
|
24
|
+
# and don't return null values in each_hash
|
25
|
+
else
|
26
|
+
target.class_eval <<-'end_eval'
|
27
|
+
def all_hashes
|
28
|
+
rows = []
|
29
|
+
all_fields = fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
|
30
|
+
each_hash { |row| rows << all_fields.dup.update(row) }
|
31
|
+
rows
|
32
|
+
end
|
33
|
+
end_eval
|
34
|
+
end
|
35
|
+
|
36
|
+
unless target.instance_methods.include?('all_hashes')
|
37
|
+
raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ActiveRecord
|
43
|
+
class Base
|
44
|
+
def self.require_mysql
|
45
|
+
# Include the MySQL driver if one hasn't already been loaded
|
46
|
+
unless defined? Mysql
|
47
|
+
begin
|
48
|
+
require_library_or_gem 'mysql'
|
49
|
+
rescue LoadError => cannot_require_mysql
|
50
|
+
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
51
|
+
begin
|
52
|
+
require 'active_record/vendor/mysql'
|
53
|
+
rescue LoadError
|
54
|
+
raise cannot_require_mysql
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Define Mysql::Result.all_hashes
|
60
|
+
MysqlCompat.define_all_hashes_method!
|
61
|
+
end
|
62
|
+
|
63
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
64
|
+
def self.mysql_connection(config) # :nodoc:
|
65
|
+
config = config.symbolize_keys
|
66
|
+
host = config[:host]
|
67
|
+
port = config[:port]
|
68
|
+
socket = config[:socket]
|
69
|
+
username = config[:username] ? config[:username].to_s : 'root'
|
70
|
+
password = config[:password].to_s
|
71
|
+
|
72
|
+
if config.has_key?(:database)
|
73
|
+
database = config[:database]
|
74
|
+
else
|
75
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
76
|
+
end
|
77
|
+
|
78
|
+
require_mysql
|
79
|
+
mysql = Mysql.init
|
80
|
+
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]
|
81
|
+
|
82
|
+
ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module ConnectionAdapters
|
87
|
+
class MysqlColumn < Column #:nodoc:
|
88
|
+
TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
|
89
|
+
|
90
|
+
def initialize(name, default, sql_type = nil, null = true)
|
91
|
+
@original_default = default
|
92
|
+
super
|
93
|
+
@default = nil if missing_default_forged_as_empty_string?
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def simplified_type(field_type)
|
98
|
+
return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
|
99
|
+
return :string if field_type =~ /enum/i
|
100
|
+
super
|
101
|
+
end
|
102
|
+
|
103
|
+
# MySQL misreports NOT NULL column default when none is given.
|
104
|
+
# We can't detect this for columns which may have a legitimate ''
|
105
|
+
# default (string, text, binary) but we can for others (integer,
|
106
|
+
# datetime, boolean, and the rest).
|
107
|
+
#
|
108
|
+
# Test whether the column has default '', is not null, and is not
|
109
|
+
# a type allowing default ''.
|
110
|
+
def missing_default_forged_as_empty_string?
|
111
|
+
!null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
|
116
|
+
# the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
|
117
|
+
#
|
118
|
+
# Options:
|
119
|
+
#
|
120
|
+
# * <tt>:host</tt> -- Defaults to localhost
|
121
|
+
# * <tt>:port</tt> -- Defaults to 3306
|
122
|
+
# * <tt>:socket</tt> -- Defaults to /tmp/mysql.sock
|
123
|
+
# * <tt>:username</tt> -- Defaults to root
|
124
|
+
# * <tt>:password</tt> -- Defaults to nothing
|
125
|
+
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
|
126
|
+
# * <tt>:sslkey</tt> -- Necessary to use MySQL with an SSL connection
|
127
|
+
# * <tt>:sslcert</tt> -- Necessary to use MySQL with an SSL connection
|
128
|
+
# * <tt>:sslcapath</tt> -- Necessary to use MySQL with an SSL connection
|
129
|
+
# * <tt>:sslcipher</tt> -- Necessary to use MySQL with an SSL connection
|
130
|
+
#
|
131
|
+
# By default, the MysqlAdapter will consider all columns of type tinyint(1)
|
132
|
+
# as boolean. If you wish to disable this emulation (which was the default
|
133
|
+
# behavior in versions 0.13.1 and earlier) you can add the following line
|
134
|
+
# to your environment.rb file:
|
135
|
+
#
|
136
|
+
# ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
|
137
|
+
class MysqlAdapter < AbstractAdapter
|
138
|
+
@@emulate_booleans = true
|
139
|
+
cattr_accessor :emulate_booleans
|
140
|
+
|
141
|
+
LOST_CONNECTION_ERROR_MESSAGES = [
|
142
|
+
"Server shutdown in progress",
|
143
|
+
"Broken pipe",
|
144
|
+
"Lost connection to MySQL server during query",
|
145
|
+
"MySQL server has gone away"
|
146
|
+
]
|
147
|
+
|
148
|
+
def initialize(connection, logger, connection_options, config)
|
149
|
+
super(connection, logger)
|
150
|
+
@connection_options, @config = connection_options, config
|
151
|
+
|
152
|
+
connect
|
153
|
+
end
|
154
|
+
|
155
|
+
def adapter_name #:nodoc:
|
156
|
+
'MySQL'
|
157
|
+
end
|
158
|
+
|
159
|
+
def supports_migrations? #:nodoc:
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
def native_database_types #:nodoc:
|
164
|
+
{
|
165
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
166
|
+
:string => { :name => "varchar", :limit => 255 },
|
167
|
+
:text => { :name => "text" },
|
168
|
+
:integer => { :name => "int", :limit => 11 },
|
169
|
+
:float => { :name => "float" },
|
170
|
+
:decimal => { :name => "decimal" },
|
171
|
+
:datetime => { :name => "datetime" },
|
172
|
+
:timestamp => { :name => "datetime" },
|
173
|
+
:time => { :name => "time" },
|
174
|
+
:date => { :name => "date" },
|
175
|
+
:binary => { :name => "blob" },
|
176
|
+
:boolean => { :name => "tinyint", :limit => 1 }
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# QUOTING ==================================================
|
182
|
+
|
183
|
+
def quote(value, column = nil)
|
184
|
+
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
185
|
+
s = column.class.string_to_binary(value).unpack("H*")[0]
|
186
|
+
"x'#{s}'"
|
187
|
+
elsif value.kind_of?(BigDecimal)
|
188
|
+
"'#{value.to_s("F")}'"
|
189
|
+
else
|
190
|
+
super
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def quote_column_name(name) #:nodoc:
|
195
|
+
"`#{name}`"
|
196
|
+
end
|
197
|
+
|
198
|
+
def quote_string(string) #:nodoc:
|
199
|
+
@connection.quote(string)
|
200
|
+
end
|
201
|
+
|
202
|
+
def quoted_true
|
203
|
+
"1"
|
204
|
+
end
|
205
|
+
|
206
|
+
def quoted_false
|
207
|
+
"0"
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# CONNECTION MANAGEMENT ====================================
|
212
|
+
|
213
|
+
def active?
|
214
|
+
if @connection.respond_to?(:stat)
|
215
|
+
@connection.stat
|
216
|
+
else
|
217
|
+
@connection.query 'select 1'
|
218
|
+
end
|
219
|
+
|
220
|
+
# mysql-ruby doesn't raise an exception when stat fails.
|
221
|
+
if @connection.respond_to?(:errno)
|
222
|
+
@connection.errno.zero?
|
223
|
+
else
|
224
|
+
true
|
225
|
+
end
|
226
|
+
rescue Mysql::Error
|
227
|
+
false
|
228
|
+
end
|
229
|
+
|
230
|
+
def reconnect!
|
231
|
+
disconnect!
|
232
|
+
connect
|
233
|
+
end
|
234
|
+
|
235
|
+
def disconnect!
|
236
|
+
@connection.close rescue nil
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
# DATABASE STATEMENTS ======================================
|
241
|
+
|
242
|
+
def execute(sql, name = nil) #:nodoc:
|
243
|
+
log(sql, name) { @connection.query(sql) }
|
244
|
+
rescue ActiveRecord::StatementInvalid => exception
|
245
|
+
if exception.message.split(":").first =~ /Packets out of order/
|
246
|
+
raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
|
247
|
+
else
|
248
|
+
raise
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
253
|
+
execute(sql, name = nil)
|
254
|
+
id_value || @connection.insert_id
|
255
|
+
end
|
256
|
+
|
257
|
+
def update(sql, name = nil) #:nodoc:
|
258
|
+
execute(sql, name)
|
259
|
+
@connection.affected_rows
|
260
|
+
end
|
261
|
+
|
262
|
+
def begin_db_transaction #:nodoc:
|
263
|
+
execute "BEGIN"
|
264
|
+
rescue Exception
|
265
|
+
# Transactions aren't supported
|
266
|
+
end
|
267
|
+
|
268
|
+
def commit_db_transaction #:nodoc:
|
269
|
+
execute "COMMIT"
|
270
|
+
rescue Exception
|
271
|
+
# Transactions aren't supported
|
272
|
+
end
|
273
|
+
|
274
|
+
def rollback_db_transaction #:nodoc:
|
275
|
+
execute "ROLLBACK"
|
276
|
+
rescue Exception
|
277
|
+
# Transactions aren't supported
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
282
|
+
if limit = options[:limit]
|
283
|
+
unless offset = options[:offset]
|
284
|
+
sql << " LIMIT #{limit}"
|
285
|
+
else
|
286
|
+
sql << " LIMIT #{offset}, #{limit}"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
# SCHEMA STATEMENTS ========================================
|
293
|
+
|
294
|
+
def structure_dump #:nodoc:
|
295
|
+
if supports_views?
|
296
|
+
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
297
|
+
else
|
298
|
+
sql = "SHOW TABLES"
|
299
|
+
end
|
300
|
+
|
301
|
+
select_all(sql).inject("") do |structure, table|
|
302
|
+
table.delete('Table_type')
|
303
|
+
structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def recreate_database(name) #:nodoc:
|
308
|
+
drop_database(name)
|
309
|
+
create_database(name)
|
310
|
+
end
|
311
|
+
|
312
|
+
def create_database(name) #:nodoc:
|
313
|
+
execute "CREATE DATABASE `#{name}`"
|
314
|
+
end
|
315
|
+
|
316
|
+
def drop_database(name) #:nodoc:
|
317
|
+
execute "DROP DATABASE IF EXISTS `#{name}`"
|
318
|
+
end
|
319
|
+
|
320
|
+
def current_database
|
321
|
+
select_one("SELECT DATABASE() as db")["db"]
|
322
|
+
end
|
323
|
+
|
324
|
+
def tables(name = nil) #:nodoc:
|
325
|
+
tables = []
|
326
|
+
execute("SHOW TABLES", name).each { |field| tables << field[0] }
|
327
|
+
tables
|
328
|
+
end
|
329
|
+
|
330
|
+
def indexes(table_name, name = nil)#:nodoc:
|
331
|
+
indexes = []
|
332
|
+
current_index = nil
|
333
|
+
execute("SHOW KEYS FROM #{table_name}", name).each do |row|
|
334
|
+
if current_index != row[2]
|
335
|
+
next if row[2] == "PRIMARY" # skip the primary key
|
336
|
+
current_index = row[2]
|
337
|
+
indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
|
338
|
+
end
|
339
|
+
|
340
|
+
indexes.last.columns << row[4]
|
341
|
+
end
|
342
|
+
indexes
|
343
|
+
end
|
344
|
+
|
345
|
+
def columns(table_name, name = nil)#:nodoc:
|
346
|
+
sql = "SHOW FIELDS FROM #{table_name}"
|
347
|
+
columns = []
|
348
|
+
execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
349
|
+
columns
|
350
|
+
end
|
351
|
+
|
352
|
+
def create_table(name, options = {}) #:nodoc:
|
353
|
+
super(name, {:options => "ENGINE=InnoDB"}.merge(options))
|
354
|
+
end
|
355
|
+
|
356
|
+
def rename_table(name, new_name)
|
357
|
+
execute "RENAME TABLE #{name} TO #{new_name}"
|
358
|
+
end
|
359
|
+
|
360
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
361
|
+
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
|
362
|
+
|
363
|
+
execute("ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{current_type} DEFAULT #{quote(default)}")
|
364
|
+
end
|
365
|
+
|
366
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
367
|
+
unless options_include_default?(options)
|
368
|
+
options[:default] = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"]
|
369
|
+
end
|
370
|
+
|
371
|
+
change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
372
|
+
add_column_options!(change_column_sql, options)
|
373
|
+
execute(change_column_sql)
|
374
|
+
end
|
375
|
+
|
376
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
377
|
+
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
|
378
|
+
execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
private
|
383
|
+
def connect
|
384
|
+
encoding = @config[:encoding]
|
385
|
+
if encoding
|
386
|
+
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
|
387
|
+
end
|
388
|
+
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) if @config[:sslkey]
|
389
|
+
@connection.real_connect(*@connection_options)
|
390
|
+
execute("SET NAMES '#{encoding}'") if encoding
|
391
|
+
|
392
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
393
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
394
|
+
execute("SET SQL_AUTO_IS_NULL=0")
|
395
|
+
end
|
396
|
+
|
397
|
+
def select(sql, name = nil)
|
398
|
+
@connection.query_with_result = true
|
399
|
+
result = execute(sql, name)
|
400
|
+
rows = result.all_hashes
|
401
|
+
result.free
|
402
|
+
rows
|
403
|
+
end
|
404
|
+
|
405
|
+
def supports_views?
|
406
|
+
version[0] >= 5
|
407
|
+
end
|
408
|
+
|
409
|
+
def version
|
410
|
+
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Base
|
5
|
+
# Establishes a connection to the database that's used by all Active Record objects
|
6
|
+
def self.openbase_connection(config) # :nodoc:
|
7
|
+
require_library_or_gem 'openbase' unless self.class.const_defined?(:OpenBase)
|
8
|
+
|
9
|
+
config = config.symbolize_keys
|
10
|
+
host = config[:host]
|
11
|
+
username = config[:username].to_s
|
12
|
+
password = config[:password].to_s
|
13
|
+
|
14
|
+
|
15
|
+
if config.has_key?(:database)
|
16
|
+
database = config[:database]
|
17
|
+
else
|
18
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
19
|
+
end
|
20
|
+
|
21
|
+
oba = ConnectionAdapters::OpenBaseAdapter.new(
|
22
|
+
OpenBase.new(database, host, username, password), logger
|
23
|
+
)
|
24
|
+
|
25
|
+
oba
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
module ConnectionAdapters
|
31
|
+
class OpenBaseColumn < Column #:nodoc:
|
32
|
+
private
|
33
|
+
def simplified_type(field_type)
|
34
|
+
return :integer if field_type.downcase =~ /long/
|
35
|
+
return :decimal if field_type.downcase == "money"
|
36
|
+
return :binary if field_type.downcase == "object"
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# The OpenBase adapter works with the Ruby/Openbase driver by Tetsuya Suzuki.
|
41
|
+
# http://www.spice-of-life.net/ruby-openbase/ (needs version 0.7.3+)
|
42
|
+
#
|
43
|
+
# Options:
|
44
|
+
#
|
45
|
+
# * <tt>:host</tt> -- Defaults to localhost
|
46
|
+
# * <tt>:username</tt> -- Defaults to nothing
|
47
|
+
# * <tt>:password</tt> -- Defaults to nothing
|
48
|
+
# * <tt>:database</tt> -- The name of the database. No default, must be provided.
|
49
|
+
#
|
50
|
+
# The OpenBase adapter will make use of OpenBase's ability to generate unique ids
|
51
|
+
# for any column with an unique index applied. Thus, if the value of a primary
|
52
|
+
# key is not specified at the time an INSERT is performed, the adapter will prefetch
|
53
|
+
# a unique id for the primary key. This prefetching is also necessary in order
|
54
|
+
# to return the id after an insert.
|
55
|
+
#
|
56
|
+
# Caveat: Operations involving LIMIT and OFFSET do not yet work!
|
57
|
+
#
|
58
|
+
# Maintainer: derrick.spell@gmail.com
|
59
|
+
class OpenBaseAdapter < AbstractAdapter
|
60
|
+
def adapter_name
|
61
|
+
'OpenBase'
|
62
|
+
end
|
63
|
+
|
64
|
+
def native_database_types
|
65
|
+
{
|
66
|
+
:primary_key => "integer UNIQUE INDEX DEFAULT _rowid",
|
67
|
+
:string => { :name => "char", :limit => 4096 },
|
68
|
+
:text => { :name => "text" },
|
69
|
+
:integer => { :name => "integer" },
|
70
|
+
:float => { :name => "float" },
|
71
|
+
:decimal => { :name => "decimal" },
|
72
|
+
:datetime => { :name => "datetime" },
|
73
|
+
:timestamp => { :name => "timestamp" },
|
74
|
+
:time => { :name => "time" },
|
75
|
+
:date => { :name => "date" },
|
76
|
+
:binary => { :name => "object" },
|
77
|
+
:boolean => { :name => "boolean" }
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def supports_migrations?
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def prefetch_primary_key?(table_name = nil)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
def default_sequence_name(table_name, primary_key) # :nodoc:
|
90
|
+
"#{table_name} #{primary_key}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def next_sequence_value(sequence_name)
|
94
|
+
ary = sequence_name.split(' ')
|
95
|
+
if (!ary[1]) then
|
96
|
+
ary[0] =~ /(\w+)_nonstd_seq/
|
97
|
+
ary[0] = $1
|
98
|
+
end
|
99
|
+
@connection.unique_row_id(ary[0], ary[1])
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# QUOTING ==================================================
|
104
|
+
|
105
|
+
def quote(value, column = nil)
|
106
|
+
if value.kind_of?(String) && column && column.type == :binary
|
107
|
+
"'#{@connection.insert_binary(value)}'"
|
108
|
+
else
|
109
|
+
super
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def quoted_true
|
114
|
+
"1"
|
115
|
+
end
|
116
|
+
|
117
|
+
def quoted_false
|
118
|
+
"0"
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
# DATABASE STATEMENTS ======================================
|
124
|
+
|
125
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
126
|
+
if limit = options[:limit]
|
127
|
+
unless offset = options[:offset]
|
128
|
+
sql << " RETURN RESULTS #{limit}"
|
129
|
+
else
|
130
|
+
limit = limit + offset
|
131
|
+
sql << " RETURN RESULTS #{offset} TO #{limit}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def select_all(sql, name = nil) #:nodoc:
|
137
|
+
select(sql, name)
|
138
|
+
end
|
139
|
+
|
140
|
+
def select_one(sql, name = nil) #:nodoc:
|
141
|
+
add_limit_offset!(sql,{:limit => 1})
|
142
|
+
results = select(sql, name)
|
143
|
+
results.first if results
|
144
|
+
end
|
145
|
+
|
146
|
+
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
147
|
+
execute(sql, name)
|
148
|
+
update_nulls_after_insert(sql, name, pk, id_value, sequence_name)
|
149
|
+
id_value
|
150
|
+
end
|
151
|
+
|
152
|
+
def execute(sql, name = nil) #:nodoc:
|
153
|
+
log(sql, name) { @connection.execute(sql) }
|
154
|
+
end
|
155
|
+
|
156
|
+
def update(sql, name = nil) #:nodoc:
|
157
|
+
execute(sql, name).rows_affected
|
158
|
+
end
|
159
|
+
|
160
|
+
alias_method :delete, :update #:nodoc:
|
161
|
+
#=begin
|
162
|
+
def begin_db_transaction #:nodoc:
|
163
|
+
execute "START TRANSACTION"
|
164
|
+
rescue Exception
|
165
|
+
# Transactions aren't supported
|
166
|
+
end
|
167
|
+
|
168
|
+
def commit_db_transaction #:nodoc:
|
169
|
+
execute "COMMIT"
|
170
|
+
rescue Exception
|
171
|
+
# Transactions aren't supported
|
172
|
+
end
|
173
|
+
|
174
|
+
def rollback_db_transaction #:nodoc:
|
175
|
+
execute "ROLLBACK"
|
176
|
+
rescue Exception
|
177
|
+
# Transactions aren't supported
|
178
|
+
end
|
179
|
+
#=end
|
180
|
+
|
181
|
+
# SCHEMA STATEMENTS ========================================
|
182
|
+
|
183
|
+
# Return the list of all tables in the schema search path.
|
184
|
+
def tables(name = nil) #:nodoc:
|
185
|
+
tables = @connection.tables
|
186
|
+
tables.reject! { |t| /\A_SYS_/ === t }
|
187
|
+
tables
|
188
|
+
end
|
189
|
+
|
190
|
+
def columns(table_name, name = nil) #:nodoc:
|
191
|
+
sql = "SELECT * FROM _sys_tables "
|
192
|
+
sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
|
193
|
+
sql << "ORDER BY columnNumber"
|
194
|
+
columns = []
|
195
|
+
select_all(sql, name).each do |row|
|
196
|
+
columns << OpenBaseColumn.new(row["fieldname"],
|
197
|
+
default_value(row["defaultvalue"]),
|
198
|
+
sql_type_name(row["typename"],row["length"]),
|
199
|
+
row["notnull"]
|
200
|
+
)
|
201
|
+
# breakpoint() if row["fieldname"] == "content"
|
202
|
+
end
|
203
|
+
columns
|
204
|
+
end
|
205
|
+
|
206
|
+
def indexes(table_name, name = nil)#:nodoc:
|
207
|
+
sql = "SELECT fieldname, notnull, searchindex, uniqueindex, clusteredindex FROM _sys_tables "
|
208
|
+
sql << "WHERE tablename='#{table_name}' AND INDEXOF(fieldname,'_')<>0 "
|
209
|
+
sql << "AND primarykey=0 "
|
210
|
+
sql << "AND (searchindex=1 OR uniqueindex=1 OR clusteredindex=1) "
|
211
|
+
sql << "ORDER BY columnNumber"
|
212
|
+
indexes = []
|
213
|
+
execute(sql, name).each do |row|
|
214
|
+
indexes << IndexDefinition.new(table_name,index_name(row),row[3]==1,[row[0]])
|
215
|
+
end
|
216
|
+
indexes
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
private
|
221
|
+
def select(sql, name = nil)
|
222
|
+
sql = translate_sql(sql)
|
223
|
+
results = execute(sql, name)
|
224
|
+
|
225
|
+
date_cols = []
|
226
|
+
col_names = []
|
227
|
+
results.column_infos.each do |info|
|
228
|
+
col_names << info.name
|
229
|
+
date_cols << info.name if info.type == "date"
|
230
|
+
end
|
231
|
+
|
232
|
+
rows = []
|
233
|
+
if ( results.rows_affected )
|
234
|
+
results.each do |row| # loop through result rows
|
235
|
+
hashed_row = {}
|
236
|
+
row.each_index do |index|
|
237
|
+
hashed_row["#{col_names[index]}"] = row[index] unless col_names[index] == "_rowid"
|
238
|
+
end
|
239
|
+
date_cols.each do |name|
|
240
|
+
unless hashed_row["#{name}"].nil? or hashed_row["#{name}"].empty?
|
241
|
+
hashed_row["#{name}"] = Date.parse(hashed_row["#{name}"],false).to_s
|
242
|
+
end
|
243
|
+
end
|
244
|
+
rows << hashed_row
|
245
|
+
end
|
246
|
+
end
|
247
|
+
rows
|
248
|
+
end
|
249
|
+
|
250
|
+
def default_value(value)
|
251
|
+
# Boolean type values
|
252
|
+
return true if value =~ /true/
|
253
|
+
return false if value =~ /false/
|
254
|
+
|
255
|
+
# Date / Time magic values
|
256
|
+
return Time.now.to_s if value =~ /^now\(\)/i
|
257
|
+
|
258
|
+
# Empty strings should be set to null
|
259
|
+
return nil if value.empty?
|
260
|
+
|
261
|
+
# Otherwise return what we got from OpenBase
|
262
|
+
# and hope for the best...
|
263
|
+
return value
|
264
|
+
end
|
265
|
+
|
266
|
+
def sql_type_name(type_name, length)
|
267
|
+
return "#{type_name}(#{length})" if ( type_name =~ /char/ )
|
268
|
+
type_name
|
269
|
+
end
|
270
|
+
|
271
|
+
def index_name(row = [])
|
272
|
+
name = ""
|
273
|
+
name << "UNIQUE " if row[3]
|
274
|
+
name << "CLUSTERED " if row[4]
|
275
|
+
name << "INDEX"
|
276
|
+
name
|
277
|
+
end
|
278
|
+
|
279
|
+
def translate_sql(sql)
|
280
|
+
|
281
|
+
# Change table.* to list of columns in table
|
282
|
+
while (sql =~ /SELECT.*\s(\w+)\.\*/)
|
283
|
+
table = $1
|
284
|
+
cols = columns(table)
|
285
|
+
if ( cols.size == 0 ) then
|
286
|
+
# Maybe this is a table alias
|
287
|
+
sql =~ /FROM(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
|
288
|
+
$1 =~ /[\s|,](\w+)\s+#{table}[\s|,]/ # get the tablename for this alias
|
289
|
+
cols = columns($1)
|
290
|
+
end
|
291
|
+
select_columns = []
|
292
|
+
cols.each do |col|
|
293
|
+
select_columns << table + '.' + col.name
|
294
|
+
end
|
295
|
+
sql.gsub!(table + '.*',select_columns.join(", ")) if select_columns
|
296
|
+
end
|
297
|
+
|
298
|
+
# Change JOIN clause to table list and WHERE condition
|
299
|
+
while (sql =~ /JOIN/)
|
300
|
+
sql =~ /((LEFT )?(OUTER )?JOIN (\w+) ON )(.+?)(?:LEFT|OUTER|JOIN|WHERE|GROUP|HAVING|ORDER|RETURN|$)/
|
301
|
+
join_clause = $1 + $5
|
302
|
+
is_outer_join = $3
|
303
|
+
join_table = $4
|
304
|
+
join_condition = $5
|
305
|
+
join_condition.gsub!(/=/,"*") if is_outer_join
|
306
|
+
if (sql =~ /WHERE/)
|
307
|
+
sql.gsub!(/WHERE/,"WHERE (#{join_condition}) AND")
|
308
|
+
else
|
309
|
+
sql.gsub!(join_clause,"#{join_clause} WHERE #{join_condition}")
|
310
|
+
end
|
311
|
+
sql =~ /(FROM .+?)(?:LEFT|OUTER|JOIN|WHERE|$)/
|
312
|
+
from_clause = $1
|
313
|
+
sql.gsub!(from_clause,"#{from_clause}, #{join_table} ")
|
314
|
+
sql.gsub!(join_clause,"")
|
315
|
+
end
|
316
|
+
|
317
|
+
# ORDER BY _rowid if no explicit ORDER BY
|
318
|
+
# This will ensure that find(:first) returns the first inserted row
|
319
|
+
if (sql !~ /(ORDER BY)|(GROUP BY)/)
|
320
|
+
if (sql =~ /RETURN RESULTS/)
|
321
|
+
sql.sub!(/RETURN RESULTS/,"ORDER BY _rowid RETURN RESULTS")
|
322
|
+
else
|
323
|
+
sql << " ORDER BY _rowid"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
sql
|
328
|
+
end
|
329
|
+
|
330
|
+
def update_nulls_after_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
331
|
+
sql =~ /INSERT INTO (\w+) \((.*)\) VALUES\s*\((.*)\)/m
|
332
|
+
table = $1
|
333
|
+
cols = $2
|
334
|
+
values = $3
|
335
|
+
cols = cols.split(',')
|
336
|
+
values.gsub!(/'[^']*'/,"''")
|
337
|
+
values.gsub!(/"[^"]*"/,"\"\"")
|
338
|
+
values = values.split(',')
|
339
|
+
update_cols = []
|
340
|
+
values.each_index { |index| update_cols << cols[index] if values[index] =~ /\s*NULL\s*/ }
|
341
|
+
update_sql = "UPDATE #{table} SET"
|
342
|
+
update_cols.each { |col| update_sql << " #{col}=NULL," unless col.empty? }
|
343
|
+
update_sql.chop!()
|
344
|
+
update_sql << " WHERE #{pk}=#{quote(id_value)}"
|
345
|
+
execute(update_sql, name + " NULL Correction") if update_cols.size > 0
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|