sequel 2.2.0 → 2.3.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.
Files changed (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,78 @@
1
+ require 'informix'
2
+
3
+ module Sequel
4
+ module Informix
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :informix
7
+
8
+ # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
9
+ #
10
+ # def auto_increment_sql
11
+ # AUTO_INCREMENT
12
+ # end
13
+
14
+ def connect
15
+ ::Informix.connect(@opts[:database], @opts[:user], @opts[:password])
16
+ end
17
+
18
+ def disconnect
19
+ @pool.disconnect {|c| c.close}
20
+ end
21
+
22
+ def dataset(opts = nil)
23
+ Sequel::Informix::Dataset.new(self, opts)
24
+ end
25
+
26
+ # Returns number of rows affected
27
+ def execute_dui(sql)
28
+ log_info(sql)
29
+ @pool.hold {|c| c.immediate(sql)}
30
+ end
31
+ alias_method :do, :execute_dui
32
+
33
+ def execute(sql, &block)
34
+ log_info(sql)
35
+ @pool.hold {|c| block[c.cursor(sql)]}
36
+ end
37
+ alias_method :query, :execute
38
+ end
39
+
40
+ class Dataset < Sequel::Dataset
41
+ def literal(v)
42
+ case v
43
+ when Time
44
+ literal(v.iso8601)
45
+ when Date, DateTime
46
+ literal(v.to_s)
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ def select_sql(opts = nil)
53
+ limit = opts.delete(:limit)
54
+ offset = opts.delete(:offset)
55
+ sql = super
56
+ if limit
57
+ limit = "FIRST #{limit}"
58
+ offset = offset ? "SKIP #{offset}" : ""
59
+ sql.sub!(/^select /i,"SELECT #{offset} #{limit} ")
60
+ end
61
+ sql
62
+ end
63
+
64
+ def fetch_rows(sql, &block)
65
+ @db.synchronize do
66
+ @db.execute(sql) do |cursor|
67
+ begin
68
+ cursor.open.each_hash(&block)
69
+ ensure
70
+ cursor.drop
71
+ end
72
+ end
73
+ end
74
+ self
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,186 @@
1
+ require 'java'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ module JavaLang; include_package 'java.lang'; end
6
+ module JavaSQL; include_package 'java.sql'; end
7
+ DATABASE_SETUP = {:postgresql=>proc do |db|
8
+ require 'sequel_core/adapters/jdbc/postgresql'
9
+ db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
10
+ JDBC.load_gem('postgres')
11
+ org.postgresql.Driver
12
+ end,
13
+ :mysql=>proc do |db|
14
+ require 'sequel_core/adapters/jdbc/mysql'
15
+ db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
16
+ JDBC.load_gem('mysql')
17
+ com.mysql.jdbc.Driver
18
+ end,
19
+ :sqlite=>proc do |db|
20
+ require 'sequel_core/adapters/jdbc/sqlite'
21
+ db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
22
+ JDBC.load_gem('sqlite3')
23
+ org.sqlite.JDBC
24
+ end,
25
+ :oracle=>proc{oracle.jdbc.driver.OracleDriver},
26
+ :sqlserver=>proc{com.microsoft.sqlserver.jdbc.SQLServerDriver}
27
+ }
28
+
29
+ def self.load_gem(name)
30
+ begin
31
+ require "jdbc/#{name}"
32
+ rescue LoadError
33
+ # jdbc gem not used, hopefully the user has the .jar in their CLASSPATH
34
+ end
35
+ end
36
+
37
+ class Database < Sequel::Database
38
+ set_adapter_scheme :jdbc
39
+
40
+ # The type of database we are connecting to
41
+ attr_reader :database_type
42
+
43
+ def initialize(opts)
44
+ super(opts)
45
+ raise(Error, "No connection string specified") unless uri
46
+ if match = /\Ajdbc:([^:]+)/.match(uri) and prok = DATABASE_SETUP[match[1].to_sym]
47
+ prok.call(self)
48
+ end
49
+ end
50
+
51
+ def connect
52
+ setup_connection(JavaSQL::DriverManager.getConnection(uri))
53
+ end
54
+
55
+ def dataset(opts = nil)
56
+ JDBC::Dataset.new(self, opts)
57
+ end
58
+
59
+ def disconnect
60
+ @pool.disconnect {|c| c.close}
61
+ end
62
+
63
+ def execute(sql)
64
+ log_info(sql)
65
+ @pool.hold do |conn|
66
+ stmt = conn.createStatement
67
+ begin
68
+ yield stmt.executeQuery(sql)
69
+ rescue NativeException, JavaSQL::SQLException => e
70
+ raise Error, e.message
71
+ ensure
72
+ stmt.close
73
+ end
74
+ end
75
+ end
76
+
77
+ def execute_ddl(sql)
78
+ log_info(sql)
79
+ @pool.hold do |conn|
80
+ stmt = conn.createStatement
81
+ begin
82
+ stmt.execute(sql)
83
+ rescue NativeException, JavaSQL::SQLException => e
84
+ raise Error, e.message
85
+ ensure
86
+ stmt.close
87
+ end
88
+ end
89
+ end
90
+
91
+ def execute_dui(sql)
92
+ log_info(sql)
93
+ @pool.hold do |conn|
94
+ stmt = conn.createStatement
95
+ begin
96
+ stmt.executeUpdate(sql)
97
+ rescue NativeException, JavaSQL::SQLException => e
98
+ raise Error, e.message
99
+ ensure
100
+ stmt.close
101
+ end
102
+ end
103
+ end
104
+
105
+ def setup_connection(conn)
106
+ conn
107
+ end
108
+
109
+ def transaction
110
+ @pool.hold do |conn|
111
+ @transactions ||= []
112
+ return yield(conn) if @transactions.include?(Thread.current)
113
+ stmt = conn.createStatement
114
+ begin
115
+ log_info(Sequel::Database::SQL_BEGIN)
116
+ stmt.execute(Sequel::Database::SQL_BEGIN)
117
+ @transactions << Thread.current
118
+ yield(conn)
119
+ rescue Exception => e
120
+ log_info(Sequel::Database::SQL_ROLLBACK)
121
+ stmt.execute(Sequel::Database::SQL_ROLLBACK)
122
+ raise e unless Error::Rollback === e
123
+ ensure
124
+ unless e
125
+ log_info(Sequel::Database::SQL_COMMIT)
126
+ stmt.execute(Sequel::Database::SQL_COMMIT)
127
+ end
128
+ stmt.close
129
+ @transactions.delete(Thread.current)
130
+ end
131
+ end
132
+ end
133
+
134
+ def uri
135
+ ur = @opts[:uri] || @opts[:url] || @opts[:database]
136
+ ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
137
+ end
138
+ alias url uri
139
+
140
+ private
141
+
142
+ def connection_pool_default_options
143
+ super.merge(:pool_convert_exceptions=>false)
144
+ end
145
+ end
146
+
147
+ class Dataset < Sequel::Dataset
148
+ def literal(v)
149
+ case v
150
+ when Time
151
+ literal(v.iso8601)
152
+ when Date, DateTime, Java::JavaSql::Timestamp
153
+ literal(v.to_s)
154
+ else
155
+ super
156
+ end
157
+ end
158
+
159
+ def fetch_rows(sql, &block)
160
+ @db.synchronize do
161
+ @db.execute(sql) do |result|
162
+ # get column names
163
+ meta = result.getMetaData
164
+ column_count = meta.getColumnCount
165
+ @columns = []
166
+ column_count.times {|i| @columns << meta.getColumnName(i+1).to_sym}
167
+
168
+ # get rows
169
+ while result.next
170
+ row = {}
171
+ @columns.each_with_index {|v, i| row[v] = result.getObject(i+1)}
172
+ yield row
173
+ end
174
+ end
175
+ end
176
+ self
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ class Java::JavaSQL::Timestamp
183
+ def usec
184
+ getNanos/1000
185
+ end
186
+ end
@@ -0,0 +1,55 @@
1
+ require 'sequel_core/adapters/shared/mysql'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ module MySQL
6
+ module DatabaseMethods
7
+ include Sequel::MySQL::DatabaseMethods
8
+
9
+ def dataset(opts=nil)
10
+ Sequel::JDBC::MySQL::Dataset.new(self, opts)
11
+ end
12
+
13
+ def execute_insert(sql)
14
+ begin
15
+ log_info(sql)
16
+ @pool.hold do |conn|
17
+ stmt = conn.createStatement
18
+ begin
19
+ stmt.executeUpdate(sql)
20
+ rs = stmt.executeQuery('SELECT LAST_INSERT_ID()')
21
+ rs.next
22
+ rs.getInt(1)
23
+ rescue NativeException, JavaSQL::SQLException => e
24
+ raise Error, e.message
25
+ ensure
26
+ stmt.close
27
+ end
28
+ end
29
+ rescue NativeException, JavaSQL::SQLException => e
30
+ raise Error, "#{sql}\r\n#{e.message}"
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def database_name
37
+ u = URI.parse(uri.sub(/\Ajdbc:/, ''))
38
+ (m = /\/(.*)/.match(u.path)) && m[1]
39
+ end
40
+ end
41
+
42
+ class Dataset < JDBC::Dataset
43
+ include Sequel::MySQL::DatasetMethods
44
+
45
+ def insert(*values)
46
+ @db.execute_insert(insert_sql(*values))
47
+ end
48
+
49
+ def replace(*args)
50
+ @db.execute_insert(replace_sql(*args))
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,66 @@
1
+ require 'sequel_core/adapters/shared/postgres'
2
+
3
+ module Sequel
4
+ Postgres::CONVERTED_EXCEPTIONS << NativeException
5
+
6
+ module JDBC
7
+ module Postgres
8
+ module AdapterMethods
9
+ include Sequel::Postgres::AdapterMethods
10
+
11
+ def execute(sql, method=:execute)
12
+ method = :executeQuery if block_given?
13
+ stmt = createStatement
14
+ begin
15
+ rows = stmt.send(method, sql)
16
+ yield(rows) if block_given?
17
+ rescue NativeException => e
18
+ raise Error, e.message
19
+ ensure
20
+ stmt.close
21
+ end
22
+ end
23
+
24
+ def result_set_values(r, *vals)
25
+ return if r.nil?
26
+ r.next
27
+ return if r.getRow == 0
28
+ case vals.length
29
+ when 1
30
+ r.getString(vals.first+1)
31
+ else
32
+ vals.collect{|col| r.getString(col+1)}
33
+ end
34
+ end
35
+ end
36
+
37
+ module DatabaseMethods
38
+ include Sequel::Postgres::DatabaseMethods
39
+
40
+ def dataset(opts=nil)
41
+ Sequel::JDBC::Postgres::Dataset.new(self, opts)
42
+ end
43
+
44
+ def setup_connection(conn)
45
+ conn.extend(Sequel::JDBC::Postgres::AdapterMethods)
46
+ conn
47
+ end
48
+ end
49
+
50
+ class Dataset < JDBC::Dataset
51
+ include Sequel::Postgres::DatasetMethods
52
+
53
+ def literal(v)
54
+ case v
55
+ when SQL::Blob
56
+ "'#{v.gsub(/[\000-\037\047\134\177-\377]/){|b| "\\#{ b[0].to_s(8).rjust(3, '0') }"}}'"
57
+ when Java::JavaSql::Timestamp
58
+ "TIMESTAMP #{literal(v.to_s)}"
59
+ else
60
+ super
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,47 @@
1
+ require 'sequel_core/adapters/shared/sqlite'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ module SQLite
6
+ module DatabaseMethods
7
+ include Sequel::SQLite::DatabaseMethods
8
+
9
+ def dataset(opts=nil)
10
+ Sequel::JDBC::SQLite::Dataset.new(self, opts)
11
+ end
12
+
13
+ def execute_insert(sql)
14
+ begin
15
+ log_info(sql)
16
+ @pool.hold do |conn|
17
+ stmt = conn.createStatement
18
+ begin
19
+ stmt.executeUpdate(sql)
20
+ rs = stmt.executeQuery('SELECT last_insert_rowid()')
21
+ rs.next
22
+ rs.getInt(1)
23
+ rescue NativeException, JavaSQL::SQLException => e
24
+ raise Error, e.message
25
+ ensure
26
+ stmt.close
27
+ end
28
+ end
29
+ rescue NativeException, JavaSQL::SQLException => e
30
+ raise Error, "#{sql}\r\n#{e.message}"
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def connection_pool_default_options
37
+ o = super
38
+ uri == 'jdbc:sqlite::memory:' ? o.merge(:max_connections=>1) : o
39
+ end
40
+ end
41
+
42
+ class Dataset < JDBC::Dataset
43
+ include Sequel::SQLite::DatasetMethods
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,231 @@
1
+ require 'mysql'
2
+ require 'sequel_core/adapters/shared/mysql'
3
+
4
+ # Monkey patch Mysql::Result to yield hashes with symbol keys
5
+ class Mysql::Result
6
+ MYSQL_TYPES = {
7
+ 0 => :to_d, # MYSQL_TYPE_DECIMAL
8
+ 1 => :to_i, # MYSQL_TYPE_TINY
9
+ 2 => :to_i, # MYSQL_TYPE_SHORT
10
+ 3 => :to_i, # MYSQL_TYPE_LONG
11
+ 4 => :to_f, # MYSQL_TYPE_FLOAT
12
+ 5 => :to_f, # MYSQL_TYPE_DOUBLE
13
+ # 6 => ??, # MYSQL_TYPE_NULL
14
+ 7 => :to_sequel_time, # MYSQL_TYPE_TIMESTAMP
15
+ 8 => :to_i, # MYSQL_TYPE_LONGLONG
16
+ 9 => :to_i, # MYSQL_TYPE_INT24
17
+ 10 => :to_date, # MYSQL_TYPE_DATE
18
+ 11 => :to_time, # MYSQL_TYPE_TIME
19
+ 12 => :to_sequel_time, # MYSQL_TYPE_DATETIME
20
+ 13 => :to_i, # MYSQL_TYPE_YEAR
21
+ 14 => :to_date, # MYSQL_TYPE_NEWDATE
22
+ # 15 => :to_s # MYSQL_TYPE_VARCHAR
23
+ # 16 => :to_s, # MYSQL_TYPE_BIT
24
+ 246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
25
+ 247 => :to_i, # MYSQL_TYPE_ENUM
26
+ 248 => :to_i, # MYSQL_TYPE_SET
27
+ 249 => :to_blob, # MYSQL_TYPE_TINY_BLOB
28
+ 250 => :to_blob, # MYSQL_TYPE_MEDIUM_BLOB
29
+ 251 => :to_blob, # MYSQL_TYPE_LONG_BLOB
30
+ 252 => :to_blob, # MYSQL_TYPE_BLOB
31
+ # 253 => :to_s, # MYSQL_TYPE_VAR_STRING
32
+ # 254 => :to_s, # MYSQL_TYPE_STRING
33
+ # 255 => :to_s # MYSQL_TYPE_GEOMETRY
34
+ }
35
+
36
+ def convert_type(v, type)
37
+ if v
38
+ if type == 1 && Sequel.convert_tinyint_to_bool
39
+ # We special case tinyint here to avoid adding
40
+ # a method to an ancestor of Fixnum
41
+ v.to_i == 0 ? false : true
42
+ else
43
+ (t = MYSQL_TYPES[type]) ? v.send(t) : v
44
+ end
45
+ else
46
+ nil
47
+ end
48
+ end
49
+
50
+ def columns(with_table = nil)
51
+ unless @columns
52
+ @column_types = []
53
+ @columns = fetch_fields.map do |f|
54
+ @column_types << f.type
55
+ (with_table ? "#{f.table}.#{f.name}" : f.name).to_sym
56
+ end
57
+ end
58
+ @columns
59
+ end
60
+
61
+ def each_array(with_table = nil)
62
+ c = columns
63
+ while row = fetch_row
64
+ c.each_with_index do |f, i|
65
+ if (t = MYSQL_TYPES[@column_types[i]]) && (v = row[i])
66
+ row[i] = v.send(t)
67
+ end
68
+ end
69
+ yield row
70
+ end
71
+ end
72
+
73
+ def sequel_each_hash(with_table = nil)
74
+ c = columns
75
+ while row = fetch_row
76
+ h = {}
77
+ c.each_with_index {|f, i| h[f] = convert_type(row[i], @column_types[i])}
78
+ yield h
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ module Sequel
85
+ module MySQL
86
+ class Database < Sequel::Database
87
+ include Sequel::MySQL::DatabaseMethods
88
+
89
+ set_adapter_scheme :mysql
90
+
91
+ def connect
92
+ conn = Mysql.init
93
+ conn.options(Mysql::OPT_LOCAL_INFILE, "client")
94
+ conn.real_connect(
95
+ @opts[:host] || 'localhost',
96
+ @opts[:user],
97
+ @opts[:password],
98
+ @opts[:database],
99
+ @opts[:port],
100
+ @opts[:socket],
101
+ Mysql::CLIENT_MULTI_RESULTS +
102
+ Mysql::CLIENT_MULTI_STATEMENTS +
103
+ Mysql::CLIENT_COMPRESS
104
+ )
105
+ conn.query_with_result = false
106
+ if encoding = @opts[:encoding] || @opts[:charset]
107
+ conn.query("set character_set_connection = '#{encoding}'")
108
+ conn.query("set character_set_client = '#{encoding}'")
109
+ conn.query("set character_set_database = '#{encoding}'")
110
+ conn.query("set character_set_server = '#{encoding}'")
111
+ conn.query("set character_set_results = '#{encoding}'")
112
+ end
113
+ conn.reconnect = true
114
+ conn
115
+ end
116
+
117
+ def dataset(opts = nil)
118
+ MySQL::Dataset.new(self, opts)
119
+ end
120
+
121
+ def disconnect
122
+ @pool.disconnect {|c| c.close}
123
+ end
124
+
125
+ def execute(sql, &block)
126
+ begin
127
+ log_info(sql)
128
+ @pool.hold do |conn|
129
+ conn.query(sql)
130
+ block[conn] if block
131
+ end
132
+ rescue Mysql::Error => e
133
+ raise Error.new(e.message)
134
+ end
135
+ end
136
+
137
+ def execute_select(sql, &block)
138
+ execute(sql) do |c|
139
+ r = c.use_result
140
+ begin
141
+ block[r]
142
+ ensure
143
+ r.free
144
+ end
145
+ end
146
+ end
147
+
148
+ def server_version
149
+ @server_version ||= (synchronize{|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
150
+ end
151
+
152
+ def tables
153
+ @pool.hold do |conn|
154
+ conn.list_tables.map {|t| t.to_sym}
155
+ end
156
+ end
157
+
158
+ def transaction
159
+ @pool.hold do |conn|
160
+ @transactions ||= []
161
+ return yield(conn) if @transactions.include? Thread.current
162
+ log_info(SQL_BEGIN)
163
+ conn.query(SQL_BEGIN)
164
+ begin
165
+ @transactions << Thread.current
166
+ yield(conn)
167
+ rescue ::Exception => e
168
+ log_info(SQL_ROLLBACK)
169
+ conn.query(SQL_ROLLBACK)
170
+ raise (Mysql::Error === e ? Error.new(e.message) : e) unless Error::Rollback === e
171
+ ensure
172
+ unless e
173
+ log_info(SQL_COMMIT)
174
+ conn.query(SQL_COMMIT)
175
+ end
176
+ @transactions.delete(Thread.current)
177
+ end
178
+ end
179
+ end
180
+
181
+ private
182
+
183
+ def connection_pool_default_options
184
+ super.merge(:pool_convert_exceptions=>false)
185
+ end
186
+
187
+ def database_name
188
+ @opts[:database]
189
+ end
190
+ end
191
+
192
+ class Dataset < Sequel::Dataset
193
+ include Sequel::MySQL::DatasetMethods
194
+
195
+ def delete(opts = nil)
196
+ @db.execute(delete_sql(opts)) {|c| c.affected_rows}
197
+ end
198
+
199
+ def fetch_rows(sql)
200
+ @db.execute_select(sql) do |r|
201
+ @columns = r.columns
202
+ r.sequel_each_hash {|row| yield row}
203
+ end
204
+ self
205
+ end
206
+
207
+ def insert(*values)
208
+ @db.execute(insert_sql(*values)) {|c| c.insert_id}
209
+ end
210
+
211
+ def literal(v)
212
+ case v
213
+ when LiteralString
214
+ v
215
+ when String
216
+ "'#{::Mysql.quote(v)}'"
217
+ else
218
+ super
219
+ end
220
+ end
221
+
222
+ def replace(*args)
223
+ @db.execute(replace_sql(*args)) {|c| c.insert_id}
224
+ end
225
+
226
+ def update(*args)
227
+ @db.execute(update_sql(*args)) {|c| c.affected_rows}
228
+ end
229
+ end
230
+ end
231
+ end