activerecord 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (106) hide show
  1. data/CHANGELOG +581 -0
  2. data/README +361 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/dev-utils/eval_debugger.rb +9 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +88 -0
  9. data/install.rb +60 -0
  10. data/lib/active_record.rb +48 -0
  11. data/lib/active_record/aggregations.rb +165 -0
  12. data/lib/active_record/associations.rb +536 -0
  13. data/lib/active_record/associations/association_collection.rb +70 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -0
  15. data/lib/active_record/associations/has_many_association.rb +104 -0
  16. data/lib/active_record/base.rb +985 -0
  17. data/lib/active_record/callbacks.rb +337 -0
  18. data/lib/active_record/connection_adapters/abstract_adapter.rb +326 -0
  19. data/lib/active_record/connection_adapters/mysql_adapter.rb +131 -0
  20. data/lib/active_record/connection_adapters/postgresql_adapter.rb +177 -0
  21. data/lib/active_record/connection_adapters/sqlite_adapter.rb +107 -0
  22. data/lib/active_record/deprecated_associations.rb +70 -0
  23. data/lib/active_record/fixtures.rb +172 -0
  24. data/lib/active_record/observer.rb +71 -0
  25. data/lib/active_record/reflection.rb +126 -0
  26. data/lib/active_record/support/class_attribute_accessors.rb +43 -0
  27. data/lib/active_record/support/class_inheritable_attributes.rb +37 -0
  28. data/lib/active_record/support/clean_logger.rb +10 -0
  29. data/lib/active_record/support/inflector.rb +70 -0
  30. data/lib/active_record/transactions.rb +102 -0
  31. data/lib/active_record/validations.rb +205 -0
  32. data/lib/active_record/vendor/mysql.rb +1117 -0
  33. data/lib/active_record/vendor/simple.rb +702 -0
  34. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  35. data/lib/active_record/wrappings.rb +59 -0
  36. data/rakefile +122 -0
  37. data/test/abstract_unit.rb +16 -0
  38. data/test/aggregations_test.rb +34 -0
  39. data/test/all.sh +8 -0
  40. data/test/associations_test.rb +477 -0
  41. data/test/base_test.rb +513 -0
  42. data/test/class_inheritable_attributes_test.rb +33 -0
  43. data/test/connections/native_mysql/connection.rb +24 -0
  44. data/test/connections/native_postgresql/connection.rb +24 -0
  45. data/test/connections/native_sqlite/connection.rb +24 -0
  46. data/test/deprecated_associations_test.rb +336 -0
  47. data/test/finder_test.rb +67 -0
  48. data/test/fixtures/accounts/signals37 +3 -0
  49. data/test/fixtures/accounts/unknown +2 -0
  50. data/test/fixtures/auto_id.rb +4 -0
  51. data/test/fixtures/column_name.rb +3 -0
  52. data/test/fixtures/companies/first_client +6 -0
  53. data/test/fixtures/companies/first_firm +4 -0
  54. data/test/fixtures/companies/second_client +6 -0
  55. data/test/fixtures/company.rb +37 -0
  56. data/test/fixtures/company_in_module.rb +33 -0
  57. data/test/fixtures/course.rb +3 -0
  58. data/test/fixtures/courses/java +2 -0
  59. data/test/fixtures/courses/ruby +2 -0
  60. data/test/fixtures/customer.rb +30 -0
  61. data/test/fixtures/customers/david +6 -0
  62. data/test/fixtures/db_definitions/mysql.sql +96 -0
  63. data/test/fixtures/db_definitions/mysql2.sql +4 -0
  64. data/test/fixtures/db_definitions/postgresql.sql +113 -0
  65. data/test/fixtures/db_definitions/postgresql2.sql +4 -0
  66. data/test/fixtures/db_definitions/sqlite.sql +85 -0
  67. data/test/fixtures/db_definitions/sqlite2.sql +4 -0
  68. data/test/fixtures/default.rb +2 -0
  69. data/test/fixtures/developer.rb +8 -0
  70. data/test/fixtures/developers/david +2 -0
  71. data/test/fixtures/developers/jamis +2 -0
  72. data/test/fixtures/developers_projects/david_action_controller +2 -0
  73. data/test/fixtures/developers_projects/david_active_record +2 -0
  74. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  75. data/test/fixtures/entrant.rb +3 -0
  76. data/test/fixtures/entrants/first +3 -0
  77. data/test/fixtures/entrants/second +3 -0
  78. data/test/fixtures/entrants/third +3 -0
  79. data/test/fixtures/fixture_database.sqlite +0 -0
  80. data/test/fixtures/fixture_database_2.sqlite +0 -0
  81. data/test/fixtures/movie.rb +5 -0
  82. data/test/fixtures/movies/first +2 -0
  83. data/test/fixtures/movies/second +2 -0
  84. data/test/fixtures/project.rb +3 -0
  85. data/test/fixtures/projects/action_controller +2 -0
  86. data/test/fixtures/projects/active_record +2 -0
  87. data/test/fixtures/reply.rb +21 -0
  88. data/test/fixtures/subscriber.rb +5 -0
  89. data/test/fixtures/subscribers/first +2 -0
  90. data/test/fixtures/subscribers/second +2 -0
  91. data/test/fixtures/topic.rb +20 -0
  92. data/test/fixtures/topics/first +9 -0
  93. data/test/fixtures/topics/second +8 -0
  94. data/test/fixtures_test.rb +20 -0
  95. data/test/inflector_test.rb +104 -0
  96. data/test/inheritance_test.rb +125 -0
  97. data/test/lifecycle_test.rb +110 -0
  98. data/test/modules_test.rb +21 -0
  99. data/test/multiple_db_test.rb +46 -0
  100. data/test/pk_test.rb +57 -0
  101. data/test/reflection_test.rb +78 -0
  102. data/test/thread_safety_test.rb +33 -0
  103. data/test/transactions_test.rb +83 -0
  104. data/test/unconnected_test.rb +24 -0
  105. data/test/validations_test.rb +126 -0
  106. metadata +166 -0
@@ -0,0 +1,10 @@
1
+ require 'logger'
2
+
3
+ class Logger #:nodoc:
4
+ private
5
+ remove_const "Format"
6
+ Format = "%s\n"
7
+ def format_message(severity, timestamp, msg, progname)
8
+ Format % [msg]
9
+ end
10
+ end
@@ -0,0 +1,70 @@
1
+ # The Inflector transforms words from singular to plural, class names to table names, modulized class names to ones without,
2
+ # and class names to foreign keys.
3
+ module Inflector
4
+ extend self
5
+
6
+ def pluralize(word)
7
+ result = word.dup
8
+ plural_rules.each do |(rule, replacement)|
9
+ break if result.gsub!(rule, replacement)
10
+ end
11
+ return result
12
+ end
13
+
14
+ def singularize(word)
15
+ result = word.dup
16
+ singular_rules.each do |(rule, replacement)|
17
+ break if result.gsub!(rule, replacement)
18
+ end
19
+ return result
20
+ end
21
+
22
+ def camelize(lower_case_and_underscored_word)
23
+ lower_case_and_underscored_word.gsub(/(^|_)(.)/){$2.upcase}
24
+ end
25
+
26
+ def underscore(camel_cased_word)
27
+ camel_cased_word.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
28
+ end
29
+
30
+ def demodulize(class_name_in_module)
31
+ class_name_in_module.gsub(/^.*::/, '')
32
+ end
33
+
34
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
35
+ Inflector.underscore(Inflector.demodulize(class_name)) +
36
+ (separate_class_name_and_id_with_underscore ? "_id" : "id")
37
+ end
38
+
39
+ private
40
+ def plural_rules #:doc:
41
+ [
42
+ [/(x|ch|ss)$/, '\1es'], # search, switch, fix, box, process, address
43
+ [/([^aeiouy]|qu)y$/, '\1ies'], # query, ability, agency
44
+ [/(?:([^f])fe|([lr])f)$/, '\1\2ves'], # half, safe, wife
45
+ [/sis$/, 'ses'], # basis, diagnosis
46
+ [/([ti])um$/, '\1a'], # datum, medium
47
+ [/person$/, 'people'], # person, salesperson
48
+ [/man$/, 'men'], # man, woman, spokesman
49
+ [/child$/, 'children'], # child
50
+ [/s$/, 's'], # no change (compatibility)
51
+ [/$/, 's']
52
+ ]
53
+ end
54
+
55
+ def singular_rules #:doc:
56
+ [
57
+ [/(x|ch|ss)es$/, '\1'],
58
+ [/([^aeiouy]|qu)ies$/, '\1y'],
59
+ [/([lr])ves$/, '\1f'],
60
+ [/([^f])ves$/, '\1fe'],
61
+ [/(analy|ba|diagno|parenthe|progno|synop|the)ses$/, '\1sis'],
62
+ [/([ti])a$/, '\1um'],
63
+ [/people$/, 'person'],
64
+ [/men$/, 'man'],
65
+ [/status$/, 'status'],
66
+ [/children$/, 'child'],
67
+ [/s$/, '']
68
+ ]
69
+ end
70
+ end
@@ -0,0 +1,102 @@
1
+ require 'active_record/vendor/simple.rb'
2
+ require 'thread'
3
+
4
+ module ActiveRecord
5
+ module Transactions # :nodoc:
6
+ TRANSACTION_MUTEX = Mutex.new
7
+
8
+ def self.append_features(base)
9
+ super
10
+ base.extend(ClassMethods)
11
+
12
+ base.class_eval do
13
+ alias_method :destroy_without_transactions, :destroy
14
+ alias_method :destroy, :destroy_with_transactions
15
+
16
+ alias_method :save_without_transactions, :save
17
+ alias_method :save, :save_with_transactions
18
+ end
19
+ end
20
+
21
+ # Transactions are protective blocks where SQL statements are only permanent if they can all succed as one atomic action.
22
+ # The classic example is a transfer between two accounts where you can only have a deposit if the withdrawal succedded and
23
+ # vice versa. Transaction enforce the integrity of the database and guards the data against program errors or database break-downs.
24
+ # So basically you should use transaction blocks whenever you have a number of statements that must be executed together or
25
+ # not at all. Example:
26
+ #
27
+ # Account.transaction do
28
+ # david.withdrawal(100)
29
+ # mary.deposit(100)
30
+ # end
31
+ #
32
+ # This example will only take money from David and give to Mary if neither +withdrawal+ nor +deposit+ raises an exception.
33
+ # Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though,
34
+ # that the objects by default will _not_ have their instance data returned to their pre-transactional state.
35
+ #
36
+ # == Save and destroy are automatically wrapped in a transaction
37
+ #
38
+ # Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks
39
+ # will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction
40
+ # depend on or you can raise exceptions in the callbacks to rollback.
41
+ #
42
+ # == Object-level transactions
43
+ #
44
+ # You can enable object-level transactions for Active Record objects, though. You do this by naming the each of the Active Records
45
+ # that you want to enable object-level transactions for, like this:
46
+ #
47
+ # Account.transaction(david, mary) do
48
+ # david.withdrawal(100)
49
+ # mary.deposit(100)
50
+ # end
51
+ #
52
+ # If the transaction fails, David and Mary will be returned to their pre-transactional state. No money will have changed hands in
53
+ # neither object nor database.
54
+ #
55
+ # == Exception handling
56
+ #
57
+ # Also have in mind that exceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so you
58
+ # should be ready to catch those in your application code.
59
+ #
60
+ # Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler.
61
+ module ClassMethods
62
+ def transaction(*objects, &block)
63
+ TRANSACTION_MUTEX.lock
64
+
65
+ begin
66
+ objects.each { |o| o.extend(Transaction::Simple) }
67
+ objects.each { |o| o.start_transaction }
68
+ connection.begin_db_transaction
69
+
70
+ block.call
71
+
72
+ connection.commit_db_transaction
73
+ objects.each { |o| o.commit_transaction }
74
+ rescue Exception => exception
75
+ connection.rollback_db_transaction
76
+ objects.each { |o| o.abort_transaction }
77
+ raise exception
78
+ ensure
79
+ TRANSACTION_MUTEX.unlock
80
+ end
81
+ end
82
+ end
83
+
84
+ def destroy_with_transactions #:nodoc:
85
+ if TRANSACTION_MUTEX.locked?
86
+ destroy_without_transactions
87
+ else
88
+ ActiveRecord::Base.transaction { destroy_without_transactions }
89
+ end
90
+ end
91
+
92
+ def save_with_transactions(perform_validation = true) #:nodoc:
93
+ result = nil
94
+ if TRANSACTION_MUTEX.locked?
95
+ result = save_without_transactions(perform_validation)
96
+ else
97
+ ActiveRecord::Base.transaction { result = save_without_transactions(perform_validation) }
98
+ end
99
+ return result
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,205 @@
1
+ module ActiveRecord
2
+ # Active Records implement validation by overwriting Base#validate (or the variations, +validate_on_create+ and
3
+ # +validate_on_update+). Each of these methods can inspect the state of the object, which usually means ensuring
4
+ # that a number of attributes have a certain value (such as not empty, within a given range, matching a certain regular expression).
5
+ #
6
+ # Example:
7
+ #
8
+ # class Person < ActiveRecord::Base
9
+ # protected
10
+ # def validate
11
+ # errors.add_on_empty %w( first_name last_name )
12
+ # errors.add("phone_number", "has invalid format") unless phone_number =~ /[0-9]*/
13
+ # end
14
+ #
15
+ # def validate_on_create # is only run the first time a new object is saved
16
+ # unless valid_discount?(membership_discount)
17
+ # errors.add("membership_discount", "has expired")
18
+ # end
19
+ # end
20
+ #
21
+ # def validate_on_update
22
+ # errors.add_to_base("No changes have occured") if unchanged_attributes?
23
+ # end
24
+ # end
25
+ #
26
+ # person = Person.new("first_name" => "David", "phone_number" => "what?")
27
+ # person.save # => false (and doesn't do the save)
28
+ # person.errors.empty? # => false
29
+ # person.count # => 2
30
+ # person.errors.on "last_name" # => "can't be empty"
31
+ # person.errors.on "phone_number" # => "has invalid format"
32
+ # person.each_full { |msg| puts msg } # => "Last name can't be empty\n" +
33
+ # "Phone number has invalid format"
34
+ #
35
+ # person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
36
+ # person.save # => true (and person is now saved in the database)
37
+ #
38
+ # An +Errors+ object is automatically created for every Active Record.
39
+ module Validations
40
+ def self.append_features(base) # :nodoc:
41
+ super
42
+
43
+ base.class_eval do
44
+ alias_method :save_without_validation, :save
45
+ alias_method :save, :save_with_validation
46
+
47
+ alias_method :update_attribute_without_validation_skipping, :update_attribute
48
+ alias_method :update_attribute, :update_attribute_with_validation_skipping
49
+ end
50
+ end
51
+
52
+ # The validation process on save can be skipped by passing false. The regular Base#save method is
53
+ # replaced with this when the validations module is mixed in, which it is by default.
54
+ def save_with_validation(perform_validation = true)
55
+ if perform_validation && valid? || !perform_validation then save_without_validation else false end
56
+ end
57
+
58
+ # Updates a single attribute and saves the record without going through the normal validation procedure.
59
+ # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
60
+ # in Base is replaced with this when the validations module is mixed in, which it is by default.
61
+ def update_attribute_with_validation_skipping(name, value)
62
+ @attributes[name] = value
63
+ save(false)
64
+ end
65
+
66
+ # Runs validate and validate_on_create or validate_on_update and returns true if no errors were added otherwise false.
67
+ def valid?
68
+ errors.clear
69
+ validate
70
+ if new_record? then validate_on_create else validate_on_update end
71
+ errors.empty?
72
+ end
73
+
74
+ # Returns the Errors object that holds all information about attribute error messages.
75
+ def errors
76
+ @errors = Errors.new(self) if @errors.nil?
77
+ @errors
78
+ end
79
+
80
+ protected
81
+ # Overwrite this method for validation checks on all saves and use Errors.add(field, msg) for invalid attributes.
82
+ def validate #:doc:
83
+ end
84
+
85
+ # Overwrite this method for validation checks used only on creation.
86
+ def validate_on_create #:doc:
87
+ end
88
+
89
+ # Overwrite this method for validation checks used only on updates.
90
+ def validate_on_update # :doc:
91
+ end
92
+ end
93
+
94
+ # Active Record validation is reported to and from this object, which is used by Base#save to
95
+ # determine whether the object in a valid state to be saved. See usage example in Validations.
96
+ class Errors
97
+ def initialize(base) # :nodoc:
98
+ @base, @errors = base, {}
99
+ end
100
+
101
+ # Adds an error to the base object instead of any particular attribute. This is used
102
+ # to report errors that doesn't tie to any specific attribute, but rather to the object
103
+ # as a whole. These error messages doesn't get prepended with any field name when iterating
104
+ # with each_full, so they should be complete sentences.
105
+ def add_to_base(msg)
106
+ add(:base, msg)
107
+ end
108
+
109
+ # Adds an error message (+msg+) to the +attribute+, which will be returned on a call to <tt>on(attribute)</tt>
110
+ # for the same attribute and ensure that this error object returns false when asked if +empty?+. More than one
111
+ # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
112
+ # If no +msg+ is supplied, "invalid" is assumed.
113
+ def add(attribute, msg = "invalid")
114
+ @errors[attribute] = [] if @errors[attribute].nil?
115
+ @errors[attribute] << msg
116
+ end
117
+
118
+ # Will add an error message to each of the attributes in +attributes+ that is empty (defined by <tt>attribute_present?</tt>).
119
+ def add_on_empty(attributes, msg = "can't be empty")
120
+ [attributes].flatten.each { |attr| add(attr, msg) unless @base.attribute_present?(attr) }
121
+ end
122
+
123
+ # Will add an error message to each of the attributes in +attributes+ that has a length outside of the passed boundary +range+.
124
+ # If the length is above the boundary, the too_long_msg message will be used. If below, the too_short_msg.
125
+ def add_on_boundary_breaking(attributes, range, too_long_msg = "is too long (max is %d characters)", too_short_msg = "is too short (min is %d characters)")
126
+ for attr in [attributes].flatten
127
+ add(attr, too_short_msg % range.begin) if @base.attribute_present?(attr) && @base.send(attr).length < range.begin
128
+ add(attr, too_long_msg % range.end) if @base.attribute_present?(attr) && @base.send(attr).length > range.end
129
+ end
130
+ end
131
+
132
+ alias :add_on_boundry_breaking :add_on_boundary_breaking
133
+
134
+ # Returns true if the specified +attribute+ has errors associated with it.
135
+ def invalid?(attribute)
136
+ !@errors[attribute].nil?
137
+ end
138
+
139
+ # * Returns nil, if no errors are associated with the specified +attribute+.
140
+ # * Returns the error message, if one error is associated with the specified +attribute+.
141
+ # * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
142
+ def on(attribute)
143
+ if @errors[attribute].nil?
144
+ nil
145
+ elsif @errors[attribute].length == 1
146
+ @errors[attribute].first
147
+ else
148
+ @errors[attribute]
149
+ end
150
+ end
151
+
152
+ alias :[] :on
153
+
154
+ # Returns errors assigned to base object through add_to_base according to the normal rules of on(attribute).
155
+ def on_base
156
+ on(:base)
157
+ end
158
+
159
+ # Yields each attribute and associated message per error added.
160
+ def each
161
+ @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
162
+ end
163
+
164
+ # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
165
+ # through iteration as "First name can't be empty".
166
+ def each_full
167
+ full_messages.each { |msg| yield msg }
168
+ end
169
+
170
+ # Returns all the full error messages in an array.
171
+ def full_messages
172
+ full_messages = []
173
+
174
+ @errors.each_key do |attr|
175
+ @errors[attr].each do |msg|
176
+ if attr == :base
177
+ full_messages << msg
178
+ else
179
+ full_messages << @base.class.human_attribute_name(attr) + " " + msg
180
+ end
181
+ end
182
+ end
183
+
184
+ return full_messages
185
+ end
186
+
187
+ # Returns true if no errors have been added.
188
+ def empty?
189
+ return @errors.empty?
190
+ end
191
+
192
+ # Removes all the errors that have been added.
193
+ def clear
194
+ @errors = {}
195
+ end
196
+
197
+ # Returns the total number of errors added. Two errors added to the same attribute will be counted as such
198
+ # with this as well.
199
+ def count
200
+ error_count = 0
201
+ @errors.each_value { |attribute| error_count += attribute.length }
202
+ error_count
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,1117 @@
1
+ # $Id: mysql.rb,v 1.1 2004/02/24 15:42:29 webster132 Exp $
2
+ #
3
+ # Copyright (C) 2003 TOMITA Masahiro
4
+ # tommy@tmtm.org
5
+ #
6
+
7
+ class Mysql
8
+
9
+ VERSION = "4.0-ruby-0.2.4"
10
+
11
+ require "socket"
12
+
13
+ MAX_PACKET_LENGTH = 256*256*256-1
14
+ MAX_ALLOWED_PACKET = 1024*1024*1024
15
+
16
+ MYSQL_UNIX_ADDR = "/tmp/mysql.sock"
17
+ MYSQL_PORT = 3306
18
+ PROTOCOL_VERSION = 10
19
+
20
+ # Command
21
+ COM_SLEEP = 0
22
+ COM_QUIT = 1
23
+ COM_INIT_DB = 2
24
+ COM_QUERY = 3
25
+ COM_FIELD_LIST = 4
26
+ COM_CREATE_DB = 5
27
+ COM_DROP_DB = 6
28
+ COM_REFRESH = 7
29
+ COM_SHUTDOWN = 8
30
+ COM_STATISTICS = 9
31
+ COM_PROCESS_INFO = 10
32
+ COM_CONNECT = 11
33
+ COM_PROCESS_KILL = 12
34
+ COM_DEBUG = 13
35
+ COM_PING = 14
36
+ COM_TIME = 15
37
+ COM_DELAYED_INSERT = 16
38
+ COM_CHANGE_USER = 17
39
+ COM_BINLOG_DUMP = 18
40
+ COM_TABLE_DUMP = 19
41
+ COM_CONNECT_OUT = 20
42
+ COM_REGISTER_SLAVE = 21
43
+
44
+ # Client flag
45
+ CLIENT_LONG_PASSWORD = 1
46
+ CLIENT_FOUND_ROWS = 1 << 1
47
+ CLIENT_LONG_FLAG = 1 << 2
48
+ CLIENT_CONNECT_WITH_DB= 1 << 3
49
+ CLIENT_NO_SCHEMA = 1 << 4
50
+ CLIENT_COMPRESS = 1 << 5
51
+ CLIENT_ODBC = 1 << 6
52
+ CLIENT_LOCAL_FILES = 1 << 7
53
+ CLIENT_IGNORE_SPACE = 1 << 8
54
+ CLIENT_INTERACTIVE = 1 << 10
55
+ CLIENT_SSL = 1 << 11
56
+ CLIENT_IGNORE_SIGPIPE = 1 << 12
57
+ CLIENT_TRANSACTIONS = 1 << 13
58
+ CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
59
+
60
+ # Connection Option
61
+ OPT_CONNECT_TIMEOUT = 0
62
+ OPT_COMPRESS = 1
63
+ OPT_NAMED_PIPE = 2
64
+ INIT_COMMAND = 3
65
+ READ_DEFAULT_FILE = 4
66
+ READ_DEFAULT_GROUP = 5
67
+ SET_CHARSET_DIR = 6
68
+ SET_CHARSET_NAME = 7
69
+ OPT_LOCAL_INFILE = 8
70
+
71
+ # Server Status
72
+ SERVER_STATUS_IN_TRANS = 1
73
+ SERVER_STATUS_AUTOCOMMIT = 2
74
+
75
+ # Refresh parameter
76
+ REFRESH_GRANT = 1
77
+ REFRESH_LOG = 2
78
+ REFRESH_TABLES = 4
79
+ REFRESH_HOSTS = 8
80
+ REFRESH_STATUS = 16
81
+ REFRESH_THREADS = 32
82
+ REFRESH_SLAVE = 64
83
+ REFRESH_MASTER = 128
84
+
85
+ def initialize(*args)
86
+ @client_flag = 0
87
+ @max_allowed_packet = MAX_ALLOWED_PACKET
88
+ @query_with_result = true
89
+ @status = :STATUS_READY
90
+ if args[0] != :INIT then
91
+ real_connect(*args)
92
+ end
93
+ end
94
+
95
+ def real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=nil)
96
+ @server_status = SERVER_STATUS_AUTOCOMMIT
97
+ if (host == nil or host == "localhost") and defined? UNIXSocket then
98
+ unix_socket = socket || ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_ADDR
99
+ sock = UNIXSocket::new(unix_socket)
100
+ @host_info = Error::err(Error::CR_LOCALHOST_CONNECTION)
101
+ @unix_socket = unix_socket
102
+ else
103
+ sock = TCPSocket::new(host, port||ENV["MYSQL_TCP_PORT"]||(Socket::getservbyname("mysql","tcp") rescue MYSQL_PORT))
104
+ @host_info = sprintf Error::err(Error::CR_TCP_CONNECTION), host
105
+ end
106
+ @host = host ? host.dup : nil
107
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true
108
+ @net = Net::new sock
109
+
110
+ a = read
111
+ @protocol_version = a.slice!(0)
112
+ @server_version, a = a.split(/\0/,2)
113
+ @thread_id, @scramble_buff = a.slice!(0,13).unpack("La8")
114
+ if a.size >= 2 then
115
+ @server_capabilities, = a.slice!(0,2).unpack("v")
116
+ end
117
+ if a.size >= 16 then
118
+ @server_language, @server_status = a.unpack("cv")
119
+ end
120
+
121
+ flag = 0 if flag == nil
122
+ flag |= @client_flag | CLIENT_CAPABILITIES
123
+ flag |= CLIENT_CONNECT_WITH_DB if db
124
+ data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+(user||"")+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)
125
+ if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
126
+ data << "\0"+db
127
+ @db = db.dup
128
+ end
129
+ write data
130
+ read
131
+ self
132
+ end
133
+ alias :connect :real_connect
134
+
135
+ def escape_string(str)
136
+ Mysql::escape_string str
137
+ end
138
+ alias :quote :escape_string
139
+
140
+ def get_client_info()
141
+ VERSION
142
+ end
143
+ alias :client_info :get_client_info
144
+
145
+ def options(option, arg=nil)
146
+ if option == OPT_LOCAL_INFILE then
147
+ if arg == false or arg == 0 then
148
+ @client_flag &= ~CLIENT_LOCAL_FILES
149
+ else
150
+ @client_flag |= CLIENT_LOCAL_FILES
151
+ end
152
+ else
153
+ raise "not implemented"
154
+ end
155
+ end
156
+
157
+ def real_query(query)
158
+ command COM_QUERY, query, true
159
+ read_query_result
160
+ self
161
+ end
162
+
163
+ def use_result()
164
+ if @status != :STATUS_GET_RESULT then
165
+ error Error::CR_COMMANDS_OUT_OF_SYNC
166
+ end
167
+ res = Result::new self, @fields, @field_count
168
+ @status = :STATUS_USE_RESULT
169
+ res
170
+ end
171
+
172
+ def store_result()
173
+ if @status != :STATUS_GET_RESULT then
174
+ error Error::CR_COMMANDS_OUT_OF_SYNC
175
+ end
176
+ @status = :STATUS_READY
177
+ data = read_rows @field_count
178
+ res = Result::new self, @fields, @field_count, data
179
+ @fields = nil
180
+ @affected_rows = data.length
181
+ res
182
+ end
183
+
184
+ def change_user(user="", passwd="", db="")
185
+ data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
186
+ command COM_CHANGE_USER, data
187
+ @user = user
188
+ @passwd = passwd
189
+ @db = db
190
+ end
191
+
192
+ def character_set_name()
193
+ raise "not implemented"
194
+ end
195
+
196
+ def close()
197
+ @status = :STATUS_READY
198
+ command COM_QUIT, nil, true
199
+ @net.close
200
+ self
201
+ end
202
+
203
+ def create_db(db)
204
+ command COM_CREATE_DB, db
205
+ self
206
+ end
207
+
208
+ def drop_db(db)
209
+ command COM_DROP_DB, db
210
+ self
211
+ end
212
+
213
+ def dump_debug_info()
214
+ command COM_DEBUG
215
+ self
216
+ end
217
+
218
+ def get_host_info()
219
+ @host_info
220
+ end
221
+ alias :host_info :get_host_info
222
+
223
+ def get_proto_info()
224
+ @protocol_version
225
+ end
226
+ alias :proto_info :get_proto_info
227
+
228
+ def get_server_info()
229
+ @server_version
230
+ end
231
+ alias :server_info :get_server_info
232
+
233
+ def kill(id)
234
+ command COM_PROCESS_KILL, Net::int4str(id)
235
+ self
236
+ end
237
+
238
+ def list_dbs(db=nil)
239
+ real_query "show databases #{db}"
240
+ @status = :STATUS_READY
241
+ read_rows(1).flatten
242
+ end
243
+
244
+ def list_fields(table, field=nil)
245
+ command COM_FIELD_LIST, "#{table}\0#{field}", true
246
+ f = read_rows 6
247
+ fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
248
+ res = Result::new self, fields, f.length
249
+ res.eof = true
250
+ res
251
+ end
252
+
253
+ def list_processes()
254
+ data = command COM_PROCESS_INFO
255
+ @field_count = get_length data
256
+ fields = read_rows 5
257
+ @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
258
+ @status = :STATUS_GET_RESULT
259
+ store_result
260
+ end
261
+
262
+ def list_tables(table=nil)
263
+ real_query "show tables #{table}"
264
+ @status = :STATUS_READY
265
+ read_rows(1).flatten
266
+ end
267
+
268
+ def ping()
269
+ command COM_PING
270
+ self
271
+ end
272
+
273
+ def query(query)
274
+ real_query query
275
+ if not @query_with_result then
276
+ return self
277
+ end
278
+ if @field_count == 0 then
279
+ return nil
280
+ end
281
+ store_result
282
+ end
283
+
284
+ def refresh(r)
285
+ command COM_REFRESH, r.chr
286
+ self
287
+ end
288
+
289
+ def reload()
290
+ refresh REFRESH_GRANT
291
+ self
292
+ end
293
+
294
+ def select_db(db)
295
+ command COM_INIT_DB, db
296
+ @db = db
297
+ self
298
+ end
299
+
300
+ def shutdown()
301
+ command COM_SHUTDOWN
302
+ self
303
+ end
304
+
305
+ def stat()
306
+ command COM_STATISTICS
307
+ end
308
+
309
+ attr_reader :info, :insert_id, :affected_rows, :field_count, :thread_id
310
+ attr_accessor :query_with_result, :status
311
+
312
+ def read_one_row(field_count)
313
+ data = read
314
+ return if data[0] == 254 and data.length == 1
315
+ rec = []
316
+ field_count.times do
317
+ len = get_length data
318
+ if len == nil then
319
+ rec << len
320
+ else
321
+ rec << data.slice!(0,len)
322
+ end
323
+ end
324
+ rec
325
+ end
326
+
327
+ def skip_result()
328
+ if @status == :STATUS_USE_RESULT then
329
+ loop do
330
+ data = read
331
+ break if data[0] == 254 and data.length == 1
332
+ end
333
+ @status = :STATUS_READY
334
+ end
335
+ end
336
+
337
+ def inspect()
338
+ "#<#{self.class}>"
339
+ end
340
+
341
+ private
342
+
343
+ def read_query_result()
344
+ data = read
345
+ @field_count = get_length(data)
346
+ if @field_count == nil then # LOAD DATA LOCAL INFILE
347
+ File::open(data) do |f|
348
+ write f.read
349
+ end
350
+ write "" # mark EOF
351
+ data = read
352
+ @field_count = get_length(data)
353
+ end
354
+ if @field_count == 0 then
355
+ @affected_rows = get_length(data, true)
356
+ @insert_id = get_length(data, true)
357
+ if @server_capabilities & CLIENT_TRANSACTIONS != 0 then
358
+ a = data.slice!(0,2)
359
+ @server_status = a[0]+a[1]*256
360
+ end
361
+ if data.size > 0 and get_length(data) then
362
+ @info = data
363
+ end
364
+ else
365
+ @extra_info = get_length(data, true)
366
+ fields = read_rows 5
367
+ @fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
368
+ @status = :STATUS_GET_RESULT
369
+ end
370
+ self
371
+ end
372
+
373
+ def unpack_fields(data, long_flag_protocol)
374
+ ret = []
375
+ data.each do |f|
376
+ table = org_table = f[0]
377
+ name = f[1]
378
+ length = f[2][0]+f[2][1]*256+f[2][2]*256*256
379
+ type = f[3][0]
380
+ if long_flag_protocol then
381
+ flags = f[4][0]+f[4][1]*256
382
+ decimals = f[4][2]
383
+ else
384
+ flags = f[4][0]
385
+ decimals = f[4][1]
386
+ end
387
+ def_value = f[5]
388
+ max_length = 0
389
+ ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
390
+ end
391
+ ret
392
+ end
393
+
394
+ def read_rows(field_count)
395
+ ret = []
396
+ while rec = read_one_row(field_count) do
397
+ ret << rec
398
+ end
399
+ ret
400
+ end
401
+
402
+ def get_length(data, longlong=nil)
403
+ return if data.length == 0
404
+ c = data.slice!(0)
405
+ case c
406
+ when 251
407
+ return nil
408
+ when 252
409
+ a = data.slice!(0,2)
410
+ return a[0]+a[1]*256
411
+ when 253
412
+ a = data.slice!(0,3)
413
+ return a[0]+a[1]*256+a[2]*256**2
414
+ when 254
415
+ a = data.slice!(0,8)
416
+ if longlong then
417
+ return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3+
418
+ a[4]*256**4+a[5]*256**5+a[6]*256**6+a[7]*256**7
419
+ else
420
+ return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3
421
+ end
422
+ else
423
+ c
424
+ end
425
+ end
426
+
427
+ def command(cmd, arg=nil, skip_check=nil)
428
+ unless @net then
429
+ error Error::CR_SERVER_GONE_ERROR
430
+ end
431
+ if @status != :STATUS_READY then
432
+ error Error::CR_COMMANDS_OUT_OF_SYNC
433
+ end
434
+ @net.clear
435
+ write cmd.chr+(arg||"")
436
+ read unless skip_check
437
+ end
438
+
439
+ def read()
440
+ unless @net then
441
+ error Error::CR_SERVER_GONE_ERROR
442
+ end
443
+ a = @net.read
444
+ if a[0] == 255 then
445
+ if a.length > 3 then
446
+ @errno = a[1]+a[2]*256
447
+ @error = a[3 .. -1]
448
+ else
449
+ @errno = Error::CR_UNKNOWN_ERROR
450
+ @error = Error::err @errno
451
+ end
452
+ raise Error::new(@errno, @error)
453
+ end
454
+ a
455
+ end
456
+
457
+ def write(arg)
458
+ unless @net then
459
+ error Error::CR_SERVER_GONE_ERROR
460
+ end
461
+ @net.write arg
462
+ end
463
+
464
+ def hash_password(password)
465
+ nr = 1345345333
466
+ add = 7
467
+ nr2 = 0x12345671
468
+ password.each_byte do |i|
469
+ next if i == 0x20 or i == 9
470
+ nr ^= (((nr & 63) + add) * i) + (nr << 8)
471
+ nr2 += (nr2 << 8) ^ nr
472
+ add += i
473
+ end
474
+ [nr & ((1 << 31) - 1), nr2 & ((1 << 31) - 1)]
475
+ end
476
+
477
+ def scramble(password, message, old_ver)
478
+ return "" if password == nil or password == ""
479
+ raise "old version password is not implemented" if old_ver
480
+ hash_pass = hash_password password
481
+ hash_message = hash_password message
482
+ rnd = Random::new hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]
483
+ to = []
484
+ 1.upto(message.length) do
485
+ to << ((rnd.rnd*31)+64).floor
486
+ end
487
+ extra = (rnd.rnd*31).floor
488
+ to.map! do |t| (t ^ extra).chr end
489
+ to.join
490
+ end
491
+
492
+ def error(errno)
493
+ @errno = errno
494
+ @error = Error::err errno
495
+ raise Error::new(@errno, @error)
496
+ end
497
+
498
+ class Result
499
+ def initialize(mysql, fields, field_count, data=nil)
500
+ @handle = mysql
501
+ @fields = fields
502
+ @field_count = field_count
503
+ @data = data
504
+ @current_field = 0
505
+ @current_row = 0
506
+ @eof = false
507
+ @row_count = 0
508
+ end
509
+ attr_accessor :eof
510
+
511
+ def data_seek(n)
512
+ @current_row = n
513
+ end
514
+
515
+ def fetch_field()
516
+ return if @current_field >= @field_count
517
+ f = @fields[@current_field]
518
+ @current_field += 1
519
+ f
520
+ end
521
+
522
+ def fetch_fields()
523
+ @fields
524
+ end
525
+
526
+ def fetch_field_direct(n)
527
+ @fields[n]
528
+ end
529
+
530
+ def fetch_lengths()
531
+ @data ? @data[@current_row].map{|i| i ? i.length : 0} : @lengths
532
+ end
533
+
534
+ def fetch_row()
535
+ if @data then
536
+ if @current_row >= @data.length then
537
+ @handle.status = :STATUS_READY
538
+ return
539
+ end
540
+ ret = @data[@current_row]
541
+ @current_row += 1
542
+ else
543
+ return if @eof
544
+ ret = @handle.read_one_row @field_count
545
+ if ret == nil then
546
+ @eof = true
547
+ return
548
+ end
549
+ @lengths = ret.map{|i| i ? i.length : 0}
550
+ @row_count += 1
551
+ end
552
+ ret
553
+ end
554
+
555
+ def fetch_hash(with_table=nil)
556
+ row = fetch_row
557
+ return if row == nil
558
+ hash = {}
559
+ @fields.each_index do |i|
560
+ f = with_table ? @fields[i].table+"."+@fields[i].name : @fields[i].name
561
+ hash[f] = row[i]
562
+ end
563
+ hash
564
+ end
565
+
566
+ def field_seek(n)
567
+ @current_field = n
568
+ end
569
+
570
+ def field_tell()
571
+ @current_field
572
+ end
573
+
574
+ def free()
575
+ @handle.skip_result
576
+ @handle = @fields = @data = nil
577
+ GC::start
578
+ end
579
+
580
+ def num_fields()
581
+ @field_count
582
+ end
583
+
584
+ def num_rows()
585
+ @data ? @data.length : @row_count
586
+ end
587
+
588
+ def row_seek(n)
589
+ @current_row = n
590
+ end
591
+
592
+ def row_tell()
593
+ @current_row
594
+ end
595
+
596
+ def each()
597
+ while row = fetch_row do
598
+ yield row
599
+ end
600
+ end
601
+
602
+ def each_hash(with_table=nil)
603
+ while hash = fetch_hash(with_table) do
604
+ yield hash
605
+ end
606
+ end
607
+
608
+ def inspect()
609
+ "#<#{self.class}>"
610
+ end
611
+
612
+ end
613
+
614
+ class Field
615
+ # Field type
616
+ TYPE_DECIMAL = 0
617
+ TYPE_TINY = 1
618
+ TYPE_SHORT = 2
619
+ TYPE_LONG = 3
620
+ TYPE_FLOAT = 4
621
+ TYPE_DOUBLE = 5
622
+ TYPE_NULL = 6
623
+ TYPE_TIMESTAMP = 7
624
+ TYPE_LONGLONG = 8
625
+ TYPE_INT24 = 9
626
+ TYPE_DATE = 10
627
+ TYPE_TIME = 11
628
+ TYPE_DATETIME = 12
629
+ TYPE_YEAR = 13
630
+ TYPE_NEWDATE = 14
631
+ TYPE_ENUM = 247
632
+ TYPE_SET = 248
633
+ TYPE_TINY_BLOB = 249
634
+ TYPE_MEDIUM_BLOB = 250
635
+ TYPE_LONG_BLOB = 251
636
+ TYPE_BLOB = 252
637
+ TYPE_VAR_STRING = 253
638
+ TYPE_STRING = 254
639
+ TYPE_GEOMETRY = 255
640
+ TYPE_CHAR = TYPE_TINY
641
+ TYPE_INTERVAL = TYPE_ENUM
642
+
643
+ # Flag
644
+ NOT_NULL_FLAG = 1
645
+ PRI_KEY_FLAG = 2
646
+ UNIQUE_KEY_FLAG = 4
647
+ MULTIPLE_KEY_FLAG = 8
648
+ BLOB_FLAG = 16
649
+ UNSIGNED_FLAG = 32
650
+ ZEROFILL_FLAG = 64
651
+ BINARY_FLAG = 128
652
+ ENUM_FLAG = 256
653
+ AUTO_INCREMENT_FLAG = 512
654
+ TIMESTAMP_FLAG = 1024
655
+ SET_FLAG = 2048
656
+ NUM_FLAG = 32768
657
+ PART_KEY_FLAG = 16384
658
+ GROUP_FLAG = 32768
659
+ UNIQUE_FLAG = 65536
660
+
661
+ def initialize(table, org_table, name, length, type, flags, decimals, def_value, max_length)
662
+ @table = table
663
+ @org_table = org_table
664
+ @name = name
665
+ @length = length
666
+ @type = type
667
+ @flags = flags
668
+ @decimals = decimals
669
+ @def = def_value
670
+ @max_length = max_length
671
+ if (type <= TYPE_INT24 and (type != TYPE_TIMESTAMP or length == 14 or length == 8)) or type == TYPE_YEAR then
672
+ @flags |= NUM_FLAG
673
+ end
674
+ end
675
+ attr_reader :table, :org_table, :name, :length, :type, :flags, :decimals, :def, :max_length
676
+
677
+ def inspect()
678
+ "#<#{self.class}:#{@name}>"
679
+ end
680
+ end
681
+
682
+ class Error < StandardError
683
+ # Server Error
684
+ ER_HASHCHK = 1000
685
+ ER_NISAMCHK = 1001
686
+ ER_NO = 1002
687
+ ER_YES = 1003
688
+ ER_CANT_CREATE_FILE = 1004
689
+ ER_CANT_CREATE_TABLE = 1005
690
+ ER_CANT_CREATE_DB = 1006
691
+ ER_DB_CREATE_EXISTS = 1007
692
+ ER_DB_DROP_EXISTS = 1008
693
+ ER_DB_DROP_DELETE = 1009
694
+ ER_DB_DROP_RMDIR = 1010
695
+ ER_CANT_DELETE_FILE = 1011
696
+ ER_CANT_FIND_SYSTEM_REC = 1012
697
+ ER_CANT_GET_STAT = 1013
698
+ ER_CANT_GET_WD = 1014
699
+ ER_CANT_LOCK = 1015
700
+ ER_CANT_OPEN_FILE = 1016
701
+ ER_FILE_NOT_FOUND = 1017
702
+ ER_CANT_READ_DIR = 1018
703
+ ER_CANT_SET_WD = 1019
704
+ ER_CHECKREAD = 1020
705
+ ER_DISK_FULL = 1021
706
+ ER_DUP_KEY = 1022
707
+ ER_ERROR_ON_CLOSE = 1023
708
+ ER_ERROR_ON_READ = 1024
709
+ ER_ERROR_ON_RENAME = 1025
710
+ ER_ERROR_ON_WRITE = 1026
711
+ ER_FILE_USED = 1027
712
+ ER_FILSORT_ABORT = 1028
713
+ ER_FORM_NOT_FOUND = 1029
714
+ ER_GET_ERRNO = 1030
715
+ ER_ILLEGAL_HA = 1031
716
+ ER_KEY_NOT_FOUND = 1032
717
+ ER_NOT_FORM_FILE = 1033
718
+ ER_NOT_KEYFILE = 1034
719
+ ER_OLD_KEYFILE = 1035
720
+ ER_OPEN_AS_READONLY = 1036
721
+ ER_OUTOFMEMORY = 1037
722
+ ER_OUT_OF_SORTMEMORY = 1038
723
+ ER_UNEXPECTED_EOF = 1039
724
+ ER_CON_COUNT_ERROR = 1040
725
+ ER_OUT_OF_RESOURCES = 1041
726
+ ER_BAD_HOST_ERROR = 1042
727
+ ER_HANDSHAKE_ERROR = 1043
728
+ ER_DBACCESS_DENIED_ERROR = 1044
729
+ ER_ACCESS_DENIED_ERROR = 1045
730
+ ER_NO_DB_ERROR = 1046
731
+ ER_UNKNOWN_COM_ERROR = 1047
732
+ ER_BAD_NULL_ERROR = 1048
733
+ ER_BAD_DB_ERROR = 1049
734
+ ER_TABLE_EXISTS_ERROR = 1050
735
+ ER_BAD_TABLE_ERROR = 1051
736
+ ER_NON_UNIQ_ERROR = 1052
737
+ ER_SERVER_SHUTDOWN = 1053
738
+ ER_BAD_FIELD_ERROR = 1054
739
+ ER_WRONG_FIELD_WITH_GROUP = 1055
740
+ ER_WRONG_GROUP_FIELD = 1056
741
+ ER_WRONG_SUM_SELECT = 1057
742
+ ER_WRONG_VALUE_COUNT = 1058
743
+ ER_TOO_LONG_IDENT = 1059
744
+ ER_DUP_FIELDNAME = 1060
745
+ ER_DUP_KEYNAME = 1061
746
+ ER_DUP_ENTRY = 1062
747
+ ER_WRONG_FIELD_SPEC = 1063
748
+ ER_PARSE_ERROR = 1064
749
+ ER_EMPTY_QUERY = 1065
750
+ ER_NONUNIQ_TABLE = 1066
751
+ ER_INVALID_DEFAULT = 1067
752
+ ER_MULTIPLE_PRI_KEY = 1068
753
+ ER_TOO_MANY_KEYS = 1069
754
+ ER_TOO_MANY_KEY_PARTS = 1070
755
+ ER_TOO_LONG_KEY = 1071
756
+ ER_KEY_COLUMN_DOES_NOT_EXITS = 1072
757
+ ER_BLOB_USED_AS_KEY = 1073
758
+ ER_TOO_BIG_FIELDLENGTH = 1074
759
+ ER_WRONG_AUTO_KEY = 1075
760
+ ER_READY = 1076
761
+ ER_NORMAL_SHUTDOWN = 1077
762
+ ER_GOT_SIGNAL = 1078
763
+ ER_SHUTDOWN_COMPLETE = 1079
764
+ ER_FORCING_CLOSE = 1080
765
+ ER_IPSOCK_ERROR = 1081
766
+ ER_NO_SUCH_INDEX = 1082
767
+ ER_WRONG_FIELD_TERMINATORS = 1083
768
+ ER_BLOBS_AND_NO_TERMINATED = 1084
769
+ ER_TEXTFILE_NOT_READABLE = 1085
770
+ ER_FILE_EXISTS_ERROR = 1086
771
+ ER_LOAD_INFO = 1087
772
+ ER_ALTER_INFO = 1088
773
+ ER_WRONG_SUB_KEY = 1089
774
+ ER_CANT_REMOVE_ALL_FIELDS = 1090
775
+ ER_CANT_DROP_FIELD_OR_KEY = 1091
776
+ ER_INSERT_INFO = 1092
777
+ ER_INSERT_TABLE_USED = 1093
778
+ ER_NO_SUCH_THREAD = 1094
779
+ ER_KILL_DENIED_ERROR = 1095
780
+ ER_NO_TABLES_USED = 1096
781
+ ER_TOO_BIG_SET = 1097
782
+ ER_NO_UNIQUE_LOGFILE = 1098
783
+ ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099
784
+ ER_TABLE_NOT_LOCKED = 1100
785
+ ER_BLOB_CANT_HAVE_DEFAULT = 1101
786
+ ER_WRONG_DB_NAME = 1102
787
+ ER_WRONG_TABLE_NAME = 1103
788
+ ER_TOO_BIG_SELECT = 1104
789
+ ER_UNKNOWN_ERROR = 1105
790
+ ER_UNKNOWN_PROCEDURE = 1106
791
+ ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107
792
+ ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108
793
+ ER_UNKNOWN_TABLE = 1109
794
+ ER_FIELD_SPECIFIED_TWICE = 1110
795
+ ER_INVALID_GROUP_FUNC_USE = 1111
796
+ ER_UNSUPPORTED_EXTENSION = 1112
797
+ ER_TABLE_MUST_HAVE_COLUMNS = 1113
798
+ ER_RECORD_FILE_FULL = 1114
799
+ ER_UNKNOWN_CHARACTER_SET = 1115
800
+ ER_TOO_MANY_TABLES = 1116
801
+ ER_TOO_MANY_FIELDS = 1117
802
+ ER_TOO_BIG_ROWSIZE = 1118
803
+ ER_STACK_OVERRUN = 1119
804
+ ER_WRONG_OUTER_JOIN = 1120
805
+ ER_NULL_COLUMN_IN_INDEX = 1121
806
+ ER_CANT_FIND_UDF = 1122
807
+ ER_CANT_INITIALIZE_UDF = 1123
808
+ ER_UDF_NO_PATHS = 1124
809
+ ER_UDF_EXISTS = 1125
810
+ ER_CANT_OPEN_LIBRARY = 1126
811
+ ER_CANT_FIND_DL_ENTRY = 1127
812
+ ER_FUNCTION_NOT_DEFINED = 1128
813
+ ER_HOST_IS_BLOCKED = 1129
814
+ ER_HOST_NOT_PRIVILEGED = 1130
815
+ ER_PASSWORD_ANONYMOUS_USER = 1131
816
+ ER_PASSWORD_NOT_ALLOWED = 1132
817
+ ER_PASSWORD_NO_MATCH = 1133
818
+ ER_UPDATE_INFO = 1134
819
+ ER_CANT_CREATE_THREAD = 1135
820
+ ER_WRONG_VALUE_COUNT_ON_ROW = 1136
821
+ ER_CANT_REOPEN_TABLE = 1137
822
+ ER_INVALID_USE_OF_NULL = 1138
823
+ ER_REGEXP_ERROR = 1139
824
+ ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140
825
+ ER_NONEXISTING_GRANT = 1141
826
+ ER_TABLEACCESS_DENIED_ERROR = 1142
827
+ ER_COLUMNACCESS_DENIED_ERROR = 1143
828
+ ER_ILLEGAL_GRANT_FOR_TABLE = 1144
829
+ ER_GRANT_WRONG_HOST_OR_USER = 1145
830
+ ER_NO_SUCH_TABLE = 1146
831
+ ER_NONEXISTING_TABLE_GRANT = 1147
832
+ ER_NOT_ALLOWED_COMMAND = 1148
833
+ ER_SYNTAX_ERROR = 1149
834
+ ER_DELAYED_CANT_CHANGE_LOCK = 1150
835
+ ER_TOO_MANY_DELAYED_THREADS = 1151
836
+ ER_ABORTING_CONNECTION = 1152
837
+ ER_NET_PACKET_TOO_LARGE = 1153
838
+ ER_NET_READ_ERROR_FROM_PIPE = 1154
839
+ ER_NET_FCNTL_ERROR = 1155
840
+ ER_NET_PACKETS_OUT_OF_ORDER = 1156
841
+ ER_NET_UNCOMPRESS_ERROR = 1157
842
+ ER_NET_READ_ERROR = 1158
843
+ ER_NET_READ_INTERRUPTED = 1159
844
+ ER_NET_ERROR_ON_WRITE = 1160
845
+ ER_NET_WRITE_INTERRUPTED = 1161
846
+ ER_TOO_LONG_STRING = 1162
847
+ ER_TABLE_CANT_HANDLE_BLOB = 1163
848
+ ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164
849
+ ER_DELAYED_INSERT_TABLE_LOCKED = 1165
850
+ ER_WRONG_COLUMN_NAME = 1166
851
+ ER_WRONG_KEY_COLUMN = 1167
852
+ ER_WRONG_MRG_TABLE = 1168
853
+ ER_DUP_UNIQUE = 1169
854
+ ER_BLOB_KEY_WITHOUT_LENGTH = 1170
855
+ ER_PRIMARY_CANT_HAVE_NULL = 1171
856
+ ER_TOO_MANY_ROWS = 1172
857
+ ER_REQUIRES_PRIMARY_KEY = 1173
858
+ ER_NO_RAID_COMPILED = 1174
859
+ ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175
860
+ ER_KEY_DOES_NOT_EXITS = 1176
861
+ ER_CHECK_NO_SUCH_TABLE = 1177
862
+ ER_CHECK_NOT_IMPLEMENTED = 1178
863
+ ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179
864
+ ER_ERROR_DURING_COMMIT = 1180
865
+ ER_ERROR_DURING_ROLLBACK = 1181
866
+ ER_ERROR_DURING_FLUSH_LOGS = 1182
867
+ ER_ERROR_DURING_CHECKPOINT = 1183
868
+ ER_NEW_ABORTING_CONNECTION = 1184
869
+ ER_DUMP_NOT_IMPLEMENTED = 1185
870
+ ER_FLUSH_MASTER_BINLOG_CLOSED = 1186
871
+ ER_INDEX_REBUILD = 1187
872
+ ER_MASTER = 1188
873
+ ER_MASTER_NET_READ = 1189
874
+ ER_MASTER_NET_WRITE = 1190
875
+ ER_FT_MATCHING_KEY_NOT_FOUND = 1191
876
+ ER_LOCK_OR_ACTIVE_TRANSACTION = 1192
877
+ ER_UNKNOWN_SYSTEM_VARIABLE = 1193
878
+ ER_CRASHED_ON_USAGE = 1194
879
+ ER_CRASHED_ON_REPAIR = 1195
880
+ ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196
881
+ ER_TRANS_CACHE_FULL = 1197
882
+ ER_SLAVE_MUST_STOP = 1198
883
+ ER_SLAVE_NOT_RUNNING = 1199
884
+ ER_BAD_SLAVE = 1200
885
+ ER_MASTER_INFO = 1201
886
+ ER_SLAVE_THREAD = 1202
887
+ ER_TOO_MANY_USER_CONNECTIONS = 1203
888
+ ER_SET_CONSTANTS_ONLY = 1204
889
+ ER_LOCK_WAIT_TIMEOUT = 1205
890
+ ER_LOCK_TABLE_FULL = 1206
891
+ ER_READ_ONLY_TRANSACTION = 1207
892
+ ER_DROP_DB_WITH_READ_LOCK = 1208
893
+ ER_CREATE_DB_WITH_READ_LOCK = 1209
894
+ ER_WRONG_ARGUMENTS = 1210
895
+ ER_NO_PERMISSION_TO_CREATE_USER = 1211
896
+ ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212
897
+ ER_LOCK_DEADLOCK = 1213
898
+ ER_TABLE_CANT_HANDLE_FULLTEXT = 1214
899
+ ER_CANNOT_ADD_FOREIGN = 1215
900
+ ER_NO_REFERENCED_ROW = 1216
901
+ ER_ROW_IS_REFERENCED = 1217
902
+ ER_CONNECT_TO_MASTER = 1218
903
+ ER_QUERY_ON_MASTER = 1219
904
+ ER_ERROR_WHEN_EXECUTING_COMMAND = 1220
905
+ ER_WRONG_USAGE = 1221
906
+ ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222
907
+ ER_CANT_UPDATE_WITH_READLOCK = 1223
908
+ ER_MIXING_NOT_ALLOWED = 1224
909
+ ER_DUP_ARGUMENT = 1225
910
+ ER_USER_LIMIT_REACHED = 1226
911
+ ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227
912
+ ER_LOCAL_VARIABLE = 1228
913
+ ER_GLOBAL_VARIABLE = 1229
914
+ ER_NO_DEFAULT = 1230
915
+ ER_WRONG_VALUE_FOR_VAR = 1231
916
+ ER_WRONG_TYPE_FOR_VAR = 1232
917
+ ER_VAR_CANT_BE_READ = 1233
918
+ ER_CANT_USE_OPTION_HERE = 1234
919
+ ER_NOT_SUPPORTED_YET = 1235
920
+ ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236
921
+ ER_SLAVE_IGNORED_TABLE = 1237
922
+ ER_ERROR_MESSAGES = 238
923
+
924
+ # Client Error
925
+ CR_MIN_ERROR = 2000
926
+ CR_MAX_ERROR = 2999
927
+ CR_UNKNOWN_ERROR = 2000
928
+ CR_SOCKET_CREATE_ERROR = 2001
929
+ CR_CONNECTION_ERROR = 2002
930
+ CR_CONN_HOST_ERROR = 2003
931
+ CR_IPSOCK_ERROR = 2004
932
+ CR_UNKNOWN_HOST = 2005
933
+ CR_SERVER_GONE_ERROR = 2006
934
+ CR_VERSION_ERROR = 2007
935
+ CR_OUT_OF_MEMORY = 2008
936
+ CR_WRONG_HOST_INFO = 2009
937
+ CR_LOCALHOST_CONNECTION = 2010
938
+ CR_TCP_CONNECTION = 2011
939
+ CR_SERVER_HANDSHAKE_ERR = 2012
940
+ CR_SERVER_LOST = 2013
941
+ CR_COMMANDS_OUT_OF_SYNC = 2014
942
+ CR_NAMEDPIPE_CONNECTION = 2015
943
+ CR_NAMEDPIPEWAIT_ERROR = 2016
944
+ CR_NAMEDPIPEOPEN_ERROR = 2017
945
+ CR_NAMEDPIPESETSTATE_ERROR = 2018
946
+ CR_CANT_READ_CHARSET = 2019
947
+ CR_NET_PACKET_TOO_LARGE = 2020
948
+ CR_EMBEDDED_CONNECTION = 2021
949
+ CR_PROBE_SLAVE_STATUS = 2022
950
+ CR_PROBE_SLAVE_HOSTS = 2023
951
+ CR_PROBE_SLAVE_CONNECT = 2024
952
+ CR_PROBE_MASTER_CONNECT = 2025
953
+ CR_SSL_CONNECTION_ERROR = 2026
954
+ CR_MALFORMED_PACKET = 2027
955
+
956
+ CLIENT_ERRORS = [
957
+ "Unknown MySQL error",
958
+ "Can't create UNIX socket (%d)",
959
+ "Can't connect to local MySQL server through socket '%-.64s' (%d)",
960
+ "Can't connect to MySQL server on '%-.64s' (%d)",
961
+ "Can't create TCP/IP socket (%d)",
962
+ "Unknown MySQL Server Host '%-.64s' (%d)",
963
+ "MySQL server has gone away",
964
+ "Protocol mismatch. Server Version = %d Client Version = %d",
965
+ "MySQL client run out of memory",
966
+ "Wrong host info",
967
+ "Localhost via UNIX socket",
968
+ "%-.64s via TCP/IP",
969
+ "Error in server handshake",
970
+ "Lost connection to MySQL server during query",
971
+ "Commands out of sync; You can't run this command now",
972
+ "%-.64s via named pipe",
973
+ "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
974
+ "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
975
+ "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
976
+ "Can't initialize character set %-.64s (path: %-.64s)",
977
+ "Got packet bigger than 'max_allowed_packet'",
978
+ "Embedded server",
979
+ "Error on SHOW SLAVE STATUS:",
980
+ "Error on SHOW SLAVE HOSTS:",
981
+ "Error connecting to slave:",
982
+ "Error connecting to master:",
983
+ "SSL connection error",
984
+ "Malformed packet"
985
+ ]
986
+
987
+ def initialize(errno, error)
988
+ @errno = errno
989
+ @error = error
990
+ super error
991
+ end
992
+ attr_reader :errno, :error
993
+
994
+ def Error::err(errno)
995
+ CLIENT_ERRORS[errno - Error::CR_MIN_ERROR]
996
+ end
997
+ end
998
+
999
+ class Net
1000
+ def initialize(sock)
1001
+ @sock = sock
1002
+ @pkt_nr = 0
1003
+ end
1004
+
1005
+ def clear()
1006
+ @pkt_nr = 0
1007
+ end
1008
+
1009
+ def read()
1010
+ buf = []
1011
+ len = nil
1012
+ @sock.sync = false
1013
+ while len == nil or len == MAX_PACKET_LENGTH do
1014
+ a = @sock.read(4)
1015
+ len = a[0]+a[1]*256+a[2]*256*256
1016
+ pkt_nr = a[3]
1017
+ if @pkt_nr != pkt_nr then
1018
+ raise "Packets out of order: #{@pkt_nr}<>#{pkt_nr}"
1019
+ end
1020
+ @pkt_nr = @pkt_nr + 1 & 0xff
1021
+ buf << @sock.read(len)
1022
+ end
1023
+ @sock.sync = true
1024
+ buf.join
1025
+ end
1026
+
1027
+ def write(data)
1028
+ if data.is_a? Array then
1029
+ data = data.join
1030
+ end
1031
+ @sock.sync = false
1032
+ ptr = 0
1033
+ while data.length >= MAX_PACKET_LENGTH do
1034
+ @sock.write Net::int3str(MAX_PACKET_LENGTH)+@pkt_nr.chr+data[ptr, MAX_PACKET_LENGTH]
1035
+ @pkt_nr = @pkt_nr + 1 & 0xff
1036
+ ptr += MAX_PACKET_LENGTH
1037
+ end
1038
+ @sock.write Net::int3str(data.length-ptr)+@pkt_nr.chr+data[ptr .. -1]
1039
+ @pkt_nr = @pkt_nr + 1 & 0xff
1040
+ @sock.sync = true
1041
+ @sock.flush
1042
+ end
1043
+
1044
+ def close()
1045
+ @sock.close
1046
+ end
1047
+
1048
+ def Net::int2str(n)
1049
+ [n].pack("v")
1050
+ end
1051
+
1052
+ def Net::int3str(n)
1053
+ [n%256, n>>8].pack("cv")
1054
+ end
1055
+
1056
+ def Net::int4str(n)
1057
+ [n].pack("V")
1058
+ end
1059
+
1060
+ end
1061
+
1062
+ class Random
1063
+ def initialize(seed1, seed2)
1064
+ @max_value = 0x3FFFFFFF
1065
+ @seed1 = seed1 % @max_value
1066
+ @seed2 = seed2 % @max_value
1067
+ end
1068
+
1069
+ def rnd()
1070
+ @seed1 = (@seed1*3+@seed2) % @max_value
1071
+ @seed2 = (@seed1+@seed2+33) % @max_value
1072
+ @seed1.to_f / @max_value
1073
+ end
1074
+ end
1075
+
1076
+ end
1077
+
1078
+ class << Mysql
1079
+ def init()
1080
+ Mysql::new :INIT
1081
+ end
1082
+
1083
+ def real_connect(*args)
1084
+ Mysql::new(*args)
1085
+ end
1086
+ alias :connect :real_connect
1087
+
1088
+ def escape_string(str)
1089
+ str.gsub(/([\0\n\r\032\'\"\\])/) do
1090
+ case $1
1091
+ when "\0" then "\\0"
1092
+ when "\n" then "\\n"
1093
+ when "\r" then "\\r"
1094
+ when "\032" then "\Z"
1095
+ else "\\"+$1
1096
+ end
1097
+ end
1098
+ end
1099
+ alias :quote :escape_string
1100
+
1101
+ def get_client_info()
1102
+ Mysql::VERSION
1103
+ end
1104
+ alias :client_info :get_client_info
1105
+
1106
+ def debug(str)
1107
+ raise "not implemented"
1108
+ end
1109
+ end
1110
+
1111
+ #
1112
+ # for compatibility
1113
+ #
1114
+
1115
+ MysqlRes = Mysql::Result
1116
+ MysqlField = Mysql::Field
1117
+ MysqlError = Mysql::Error