activerecord_authorails 1.0.0
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 +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
|