sequel_core 1.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 (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