sequel_core 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGELOG +1003 -0
  2. data/COPYING +18 -0
  3. data/README +81 -0
  4. data/Rakefile +176 -0
  5. data/bin/sequel +41 -0
  6. data/lib/sequel_core.rb +59 -0
  7. data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
  8. data/lib/sequel_core/adapters/ado.rb +100 -0
  9. data/lib/sequel_core/adapters/db2.rb +158 -0
  10. data/lib/sequel_core/adapters/dbi.rb +126 -0
  11. data/lib/sequel_core/adapters/informix.rb +87 -0
  12. data/lib/sequel_core/adapters/jdbc.rb +108 -0
  13. data/lib/sequel_core/adapters/mysql.rb +269 -0
  14. data/lib/sequel_core/adapters/odbc.rb +145 -0
  15. data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
  16. data/lib/sequel_core/adapters/openbase.rb +90 -0
  17. data/lib/sequel_core/adapters/oracle.rb +99 -0
  18. data/lib/sequel_core/adapters/postgres.rb +519 -0
  19. data/lib/sequel_core/adapters/sqlite.rb +192 -0
  20. data/lib/sequel_core/array_keys.rb +296 -0
  21. data/lib/sequel_core/connection_pool.rb +152 -0
  22. data/lib/sequel_core/core_ext.rb +59 -0
  23. data/lib/sequel_core/core_sql.rb +191 -0
  24. data/lib/sequel_core/database.rb +433 -0
  25. data/lib/sequel_core/dataset.rb +409 -0
  26. data/lib/sequel_core/dataset/convenience.rb +321 -0
  27. data/lib/sequel_core/dataset/sequelizer.rb +354 -0
  28. data/lib/sequel_core/dataset/sql.rb +586 -0
  29. data/lib/sequel_core/exceptions.rb +45 -0
  30. data/lib/sequel_core/migration.rb +191 -0
  31. data/lib/sequel_core/model.rb +8 -0
  32. data/lib/sequel_core/pretty_table.rb +73 -0
  33. data/lib/sequel_core/schema.rb +8 -0
  34. data/lib/sequel_core/schema/schema_generator.rb +131 -0
  35. data/lib/sequel_core/schema/schema_sql.rb +131 -0
  36. data/lib/sequel_core/worker.rb +58 -0
  37. data/spec/adapters/informix_spec.rb +139 -0
  38. data/spec/adapters/mysql_spec.rb +330 -0
  39. data/spec/adapters/oracle_spec.rb +130 -0
  40. data/spec/adapters/postgres_spec.rb +189 -0
  41. data/spec/adapters/sqlite_spec.rb +345 -0
  42. data/spec/array_keys_spec.rb +679 -0
  43. data/spec/connection_pool_spec.rb +356 -0
  44. data/spec/core_ext_spec.rb +67 -0
  45. data/spec/core_sql_spec.rb +301 -0
  46. data/spec/database_spec.rb +812 -0
  47. data/spec/dataset_spec.rb +2381 -0
  48. data/spec/migration_spec.rb +261 -0
  49. data/spec/pretty_table_spec.rb +66 -0
  50. data/spec/rcov.opts +4 -0
  51. data/spec/schema_generator_spec.rb +86 -0
  52. data/spec/schema_spec.rb +230 -0
  53. data/spec/sequelizer_spec.rb +448 -0
  54. data/spec/spec.opts +5 -0
  55. data/spec/spec_helper.rb +44 -0
  56. data/spec/worker_spec.rb +96 -0
  57. metadata +162 -0
@@ -0,0 +1,108 @@
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
+
8
+ def self.load_driver(driver)
9
+ JavaLang::Class.forName(driver)
10
+ # "com.mysql.jdbc.Driver"
11
+ end
12
+
13
+ class Database < Sequel::Database
14
+ set_adapter_scheme :jdbc
15
+
16
+ def connect
17
+ unless conn_string = @opts[:uri] || @opts[:url] || @opts[:database]
18
+ raise Error, "No connection string specified"
19
+ end
20
+ unless conn_string =~ /^jdbc:/
21
+ conn_string = "jdbc:#{conn_string}"
22
+ end
23
+ JavaSQL::DriverManager.getConnection(
24
+ conn_string,
25
+ @opts[:user],
26
+ @opts[:password]
27
+ )
28
+ # "jdbc:mysql://127.0.0.1:3306/ruby?user=root"
29
+ # "mysql://127.0.0.1:3306/ruby?user=root"
30
+ end
31
+
32
+ def disconnect
33
+ @pool.disconnect {|c| c.close}
34
+ end
35
+
36
+ def dataset(opts = nil)
37
+ JDBC::Dataset.new(self, opts)
38
+ end
39
+
40
+ def execute_and_forget(sql)
41
+ @logger.info(sql) if @logger
42
+ @pool.hold do |conn|
43
+ stmt = conn.createStatement
44
+ begin
45
+ stmt.executeQuery(sql)
46
+ ensure
47
+ stmt.close
48
+ end
49
+ end
50
+ end
51
+
52
+ def execute(sql)
53
+ @logger.info(sql) if @logger
54
+ @pool.hold do |conn|
55
+ stmt = conn.createStatement
56
+ begin
57
+ yield stmt.executeQuery(sql)
58
+ ensure
59
+ stmt.close
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ class Dataset < Sequel::Dataset
66
+ def literal(v)
67
+ case v
68
+ when Time
69
+ literal(v.iso8601)
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ def fetch_rows(sql, &block)
76
+ @db.synchronize do
77
+ @db.execute(sql) do |result|
78
+ # get column names
79
+ meta = result.getMetaData
80
+ column_count = meta.getColumnCount
81
+ @columns = []
82
+ column_count.times {|i| @columns << meta.getColumnName(i).to_sym}
83
+
84
+ # get rows
85
+ while result.next
86
+ row = {}
87
+ @columns.each_with_index {|v, i| row[v] = result.getObject(i)}
88
+ yield row
89
+ end
90
+ end
91
+ end
92
+ self
93
+ end
94
+
95
+ def insert(*values)
96
+ @db.execute_and_forget insert_sql(*values)
97
+ end
98
+
99
+ def update(*args, &block)
100
+ @db.execute_and_forget update_sql(*args, &block)
101
+ end
102
+
103
+ def delete(opts = nil)
104
+ @db.execute_and_forget delete_sql(opts)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,269 @@
1
+ require 'mysql'
2
+
3
+ # Monkey patch Mysql::Result to yield hashes with symbol keys
4
+ class Mysql::Result
5
+ MYSQL_TYPES = {
6
+ 0 => :to_d, # MYSQL_TYPE_DECIMAL
7
+ 1 => :to_i, # MYSQL_TYPE_TINY
8
+ 2 => :to_i, # MYSQL_TYPE_SHORT
9
+ 3 => :to_i, # MYSQL_TYPE_LONG
10
+ 4 => :to_f, # MYSQL_TYPE_FLOAT
11
+ 5 => :to_f, # MYSQL_TYPE_DOUBLE
12
+ # 6 => ??, # MYSQL_TYPE_NULL
13
+ 7 => :to_time, # MYSQL_TYPE_TIMESTAMP
14
+ 8 => :to_i, # MYSQL_TYPE_LONGLONG
15
+ 9 => :to_i, # MYSQL_TYPE_INT24
16
+ 10 => :to_time, # MYSQL_TYPE_DATE
17
+ 11 => :to_time, # MYSQL_TYPE_TIME
18
+ 12 => :to_time, # MYSQL_TYPE_DATETIME
19
+ 13 => :to_i, # MYSQL_TYPE_YEAR
20
+ 14 => :to_time, # MYSQL_TYPE_NEWDATE
21
+ # 15 => :to_s # MYSQL_TYPE_VARCHAR
22
+ # 16 => :to_s, # MYSQL_TYPE_BIT
23
+ 246 => :to_d, # MYSQL_TYPE_NEWDECIMAL
24
+ 247 => :to_i, # MYSQL_TYPE_ENUM
25
+ 248 => :to_i # MYSQL_TYPE_SET
26
+ # 249 => :to_s, # MYSQL_TYPE_TINY_BLOB
27
+ # 250 => :to_s, # MYSQL_TYPE_MEDIUM_BLOB
28
+ # 251 => :to_s, # MYSQL_TYPE_LONG_BLOB
29
+ # 252 => :to_s, # MYSQL_TYPE_BLOB
30
+ # 253 => :to_s, # MYSQL_TYPE_VAR_STRING
31
+ # 254 => :to_s, # MYSQL_TYPE_STRING
32
+ # 255 => :to_s # MYSQL_TYPE_GEOMETRY
33
+ }
34
+
35
+ def convert_type(v, type)
36
+ v ? ((t = MYSQL_TYPES[type]) ? v.send(t) : v) : nil
37
+ end
38
+
39
+ def columns(with_table = nil)
40
+ unless @columns
41
+ @column_types = []
42
+ @columns = fetch_fields.map do |f|
43
+ @column_types << f.type
44
+ (with_table ? (f.table + "." + f.name) : f.name).to_sym
45
+ end
46
+ end
47
+ @columns
48
+ end
49
+
50
+ def each_array(with_table = nil)
51
+ c = columns
52
+ while row = fetch_row
53
+ c.each_with_index do |f, i|
54
+ if (t = MYSQL_TYPES[@column_types[i]]) && (v = row[i])
55
+ row[i] = v.send(t)
56
+ end
57
+ end
58
+ row.keys = c
59
+ yield row
60
+ end
61
+ end
62
+
63
+ def each_hash(with_table = nil)
64
+ c = columns
65
+ while row = fetch_row
66
+ h = {}
67
+ c.each_with_index {|f, i| h[f] = convert_type(row[i], @column_types[i])}
68
+ yield h
69
+ end
70
+ end
71
+ end
72
+
73
+ module Sequel
74
+ module MySQL
75
+ class Database < Sequel::Database
76
+ set_adapter_scheme :mysql
77
+
78
+ def serial_primary_key_options
79
+ {:primary_key => true, :type => :integer, :auto_increment => true}
80
+ end
81
+
82
+ AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
83
+
84
+ def auto_increment_sql
85
+ AUTO_INCREMENT
86
+ end
87
+
88
+ def connect
89
+ conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
90
+ @opts[:database], @opts[:port], nil, Mysql::CLIENT_MULTI_RESULTS)
91
+ conn.query_with_result = false
92
+ if encoding = @opts[:encoding] || @opts[:charset]
93
+ conn.query("set character_set_connection = '#{encoding}'")
94
+ conn.query("set character_set_client = '#{encoding}'")
95
+ conn.query("set character_set_results = '#{encoding}'")
96
+ end
97
+ conn.reconnect = true
98
+ conn
99
+ end
100
+
101
+ def disconnect
102
+ @pool.disconnect {|c| c.close}
103
+ end
104
+
105
+ def tables
106
+ @pool.hold do |conn|
107
+ conn.list_tables.map {|t| t.to_sym}
108
+ end
109
+ end
110
+
111
+ def dataset(opts = nil)
112
+ MySQL::Dataset.new(self, opts)
113
+ end
114
+
115
+ def execute(sql)
116
+ @logger.info(sql) if @logger
117
+ @pool.hold do |conn|
118
+ conn.query(sql)
119
+ end
120
+ end
121
+
122
+ def execute_select(sql)
123
+ @logger.info(sql) if @logger
124
+ @pool.hold do |conn|
125
+ conn.query(sql)
126
+ conn.use_result
127
+ end
128
+ end
129
+
130
+ def execute_insert(sql)
131
+ @logger.info(sql) if @logger
132
+ @pool.hold do |conn|
133
+ conn.query(sql)
134
+ conn.insert_id
135
+ end
136
+ end
137
+
138
+ def execute_affected(sql)
139
+ @logger.info(sql) if @logger
140
+ @pool.hold do |conn|
141
+ conn.query(sql)
142
+ conn.affected_rows
143
+ end
144
+ end
145
+
146
+ def alter_table_sql(table, op)
147
+ case op[:op]
148
+ when :rename_column
149
+ "ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:new_name])} #{op[:type]}"
150
+ when :set_column_type
151
+ "ALTER TABLE #{table} CHANGE COLUMN #{literal(op[:name])} #{literal(op[:name])} #{op[:type]}"
152
+ when :drop_index
153
+ "DROP INDEX #{default_index_name(table, op[:columns])} ON #{table}"
154
+ else
155
+ super(table, op)
156
+ end
157
+ end
158
+
159
+ def transaction
160
+ @pool.hold do |conn|
161
+ @transactions ||= []
162
+ if @transactions.include? Thread.current
163
+ return yield(conn)
164
+ end
165
+ conn.query(SQL_BEGIN)
166
+ begin
167
+ @transactions << Thread.current
168
+ result = yield(conn)
169
+ conn.query(SQL_COMMIT)
170
+ result
171
+ rescue => e
172
+ conn.query(SQL_ROLLBACK)
173
+ raise e unless Error::Rollback === e
174
+ ensure
175
+ @transactions.delete(Thread.current)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ class Dataset < Sequel::Dataset
182
+ def quote_column_ref(c); "`#{c}`"; end
183
+
184
+ TRUE = '1'
185
+ FALSE = '0'
186
+
187
+ def literal(v)
188
+ case v
189
+ when LiteralString
190
+ v
191
+ when String
192
+ "'#{v.gsub(/'|\\/, '\&\&')}'"
193
+ when true
194
+ TRUE
195
+ when false
196
+ FALSE
197
+ else
198
+ super
199
+ end
200
+ end
201
+
202
+ def match_expr(l, r)
203
+ case r
204
+ when Regexp
205
+ r.casefold? ? \
206
+ "(#{literal(l)} REGEXP #{literal(r.source)})" :
207
+ "(#{literal(l)} REGEXP BINARY #{literal(r.source)})"
208
+ else
209
+ super
210
+ end
211
+ end
212
+
213
+ # MySQL supports ORDER and LIMIT clauses in UPDATE statements.
214
+ def update_sql(values, opts = nil)
215
+ sql = super
216
+
217
+ opts = opts ? @opts.merge(opts) : @opts
218
+
219
+ if order = opts[:order]
220
+ sql << " ORDER BY #{column_list(order)}"
221
+ end
222
+
223
+ if limit = opts[:limit]
224
+ sql << " LIMIT #{limit}"
225
+ end
226
+
227
+ sql
228
+ end
229
+
230
+ def insert(*values)
231
+ @db.execute_insert(insert_sql(*values))
232
+ end
233
+
234
+ def update(*args, &block)
235
+ @db.execute_affected(update_sql(*args, &block))
236
+ end
237
+
238
+ def delete(opts = nil)
239
+ @db.execute_affected(delete_sql(opts))
240
+ end
241
+
242
+ def fetch_rows(sql)
243
+ @db.synchronize do
244
+ r = @db.execute_select(sql)
245
+ begin
246
+ @columns = r.columns
247
+ r.each_hash {|row| yield row}
248
+ ensure
249
+ r.free
250
+ end
251
+ end
252
+ self
253
+ end
254
+
255
+ def array_tuples_fetch_rows(sql, &block)
256
+ @db.synchronize do
257
+ r = @db.execute_select(sql)
258
+ begin
259
+ @columns = r.columns
260
+ r.each_array(&block)
261
+ ensure
262
+ r.free
263
+ end
264
+ end
265
+ self
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,145 @@
1
+ require 'odbc'
2
+
3
+ module Sequel
4
+ module ODBC
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :odbc
7
+
8
+ def connect
9
+ conn = ::ODBC::connect(@opts[:database], @opts[:user], @opts[:password])
10
+ conn.autocommit = true
11
+ conn
12
+ end
13
+
14
+ def disconnect
15
+ @pool.disconnect {|c| c.disconnect}
16
+ end
17
+
18
+ def dataset(opts = nil)
19
+ ODBC::Dataset.new(self, opts)
20
+ end
21
+
22
+ def execute(sql)
23
+ @logger.info(sql) if @logger
24
+ @pool.hold do |conn|
25
+ conn.run(sql)
26
+ end
27
+ end
28
+
29
+ def do(sql)
30
+ @logger.info(sql) if @logger
31
+ @pool.hold do |conn|
32
+ conn.do(sql)
33
+ end
34
+ end
35
+ end
36
+
37
+ class Dataset < Sequel::Dataset
38
+ BOOL_TRUE = '1'.freeze
39
+ BOOL_FALSE = '0'.freeze
40
+
41
+ def literal(v)
42
+ case v
43
+ when true
44
+ BOOL_TRUE
45
+ when false
46
+ BOOL_FALSE
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ def fetch_rows(sql, &block)
53
+ @db.synchronize do
54
+ s = @db.execute sql
55
+ begin
56
+ @columns = s.columns(true).map {|c| c.name.to_sym}
57
+ rows = s.fetch_all
58
+ rows.each {|row| yield hash_row(row)}
59
+ ensure
60
+ s.drop unless s.nil? rescue nil
61
+ end
62
+ end
63
+ self
64
+ end
65
+
66
+ def hash_row(row)
67
+ hash = {}
68
+ row.each_with_index do |v, idx|
69
+ hash[@columns[idx]] = convert_odbc_value(v)
70
+ end
71
+ hash
72
+ end
73
+
74
+ def convert_odbc_value(v)
75
+ # When fetching a result set, the Ruby ODBC driver converts all ODBC
76
+ # SQL types to an equivalent Ruby type; with the exception of
77
+ # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
78
+ #
79
+ # The conversions below are consistent with the mappings in
80
+ # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
81
+ case v
82
+ when ::ODBC::TimeStamp
83
+ DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
84
+ when ::ODBC::Time
85
+ DateTime.now
86
+ Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
87
+ when ::ODBC::Date
88
+ Date.new(v.year, v.month, v.day)
89
+ else
90
+ v
91
+ end
92
+ end
93
+
94
+ def array_tuples_fetch_rows(sql, &block)
95
+ @db.synchronize do
96
+ s = @db.execute sql
97
+ begin
98
+ @columns = s.columns(true).map {|c| c.name.to_sym}
99
+ rows = s.fetch_all
100
+ rows.each {|r| yield array_tuples_make_row(r)}
101
+ ensure
102
+ s.drop unless s.nil? rescue nil
103
+ end
104
+ end
105
+ self
106
+ end
107
+
108
+ def array_tuples_make_row(row)
109
+ row.keys = @columns
110
+ row.each_with_index do |v, idx|
111
+ # When fetching a result set, the Ruby ODBC driver converts all ODBC
112
+ # SQL types to an equivalent Ruby type; with the exception of
113
+ # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
114
+ #
115
+ # The conversions below are consistent with the mappings in
116
+ # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
117
+ case v
118
+ when ::ODBC::TimeStamp
119
+ row[idx] = DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
120
+ when ::ODBC::Time
121
+ now = DateTime.now
122
+ row[idx] = Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
123
+ when ::ODBC::Date
124
+ row[idx] = Date.new(v.year, v.month, v.day)
125
+ end
126
+ end
127
+ row
128
+ end
129
+
130
+
131
+ def insert(*values)
132
+ @db.do insert_sql(*values)
133
+ end
134
+
135
+ def update(*args, &block)
136
+ @db.do update_sql(*args, &block)
137
+ self
138
+ end
139
+
140
+ def delete(opts = nil)
141
+ @db.do delete_sql(opts)
142
+ end
143
+ end
144
+ end
145
+ end