sequel 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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,155 @@
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
+ GUARDED_DRV_NAME = /^\{.+\}$/.freeze
15
+ DRV_NAME_GUARDS = '{%s}'.freeze
16
+
17
+ def connect
18
+ if @opts.include? :driver
19
+ drv = ::ODBC::Driver.new
20
+ drv.name = 'Sequel ODBC Driver130'
21
+ @opts.each do |param, value|
22
+ if :driver == param and not (value =~ GUARDED_DRV_NAME)
23
+ value = DRV_NAME_GUARDS % value
24
+ end
25
+ drv.attrs[param.to_s.capitalize] = value
26
+ end
27
+ db = ::ODBC::Database.new
28
+ conn = db.drvconnect(drv)
29
+ else
30
+ conn = ::ODBC::connect(@opts[:database], @opts[:user], @opts[:password])
31
+ end
32
+ conn.autocommit = true
33
+ conn
34
+ end
35
+
36
+ def disconnect
37
+ @pool.disconnect {|c| c.disconnect}
38
+ end
39
+
40
+ def dataset(opts = nil)
41
+ ODBC::Dataset.new(self, opts)
42
+ end
43
+
44
+ # ODBC returns native statement objects, which must be dropped if
45
+ # you call execute manually, or you will get warnings. See the
46
+ # fetch_rows method source code for an example of how to drop
47
+ # the statements.
48
+ def execute(sql)
49
+ log_info(sql)
50
+ @pool.hold do |conn|
51
+ conn.run(sql)
52
+ end
53
+ end
54
+
55
+ def do(sql)
56
+ log_info(sql)
57
+ @pool.hold do |conn|
58
+ conn.do(sql)
59
+ end
60
+ end
61
+ alias_method :execute_dui, :do
62
+ end
63
+
64
+ class Dataset < Sequel::Dataset
65
+ BOOL_TRUE = '1'.freeze
66
+ BOOL_FALSE = '0'.freeze
67
+ ODBC_TIMESTAMP_FORMAT = "{ts '%Y-%m-%d %H:%M:%S'}".freeze
68
+ ODBC_TIMESTAMP_AFTER_SECONDS =
69
+ ODBC_TIMESTAMP_FORMAT.index( '%S' ).succ - ODBC_TIMESTAMP_FORMAT.length
70
+ ODBC_DATE_FORMAT = "{d '%Y-%m-%d'}".freeze
71
+
72
+ def literal(v)
73
+ case v
74
+ when true
75
+ BOOL_TRUE
76
+ when false
77
+ BOOL_FALSE
78
+ when Time, DateTime
79
+ formatted = v.strftime(ODBC_TIMESTAMP_FORMAT)
80
+ usec = (Time === v ? v.usec : (v.sec_fraction * 86400000000))
81
+ formatted.insert(ODBC_TIMESTAMP_AFTER_SECONDS, ".#{(usec.to_f/1000).round}") if usec >= 1000
82
+ formatted
83
+ when Date
84
+ v.strftime(ODBC_DATE_FORMAT)
85
+ else
86
+ super
87
+ end
88
+ end
89
+
90
+ UNTITLED_COLUMN = 'untitled_%d'.freeze
91
+
92
+ def fetch_rows(sql, &block)
93
+ @db.synchronize do
94
+ s = @db.execute sql
95
+ begin
96
+ untitled_count = 0
97
+ @columns = s.columns(true).map do |c|
98
+ if (n = c.name).empty?
99
+ n = UNTITLED_COLUMN % (untitled_count += 1)
100
+ end
101
+ n.to_sym
102
+ end
103
+ rows = s.fetch_all
104
+ rows.each {|row| yield hash_row(row)} if rows
105
+ ensure
106
+ s.drop unless s.nil? rescue nil
107
+ end
108
+ end
109
+ self
110
+ end
111
+
112
+ # def fetch_rows(sql, &block)
113
+ # @db.synchronize do
114
+ # s = @db.execute sql
115
+ # begin
116
+ # @columns = s.columns(true).map {|c| c.name.to_sym}
117
+ # rows = s.fetch_all
118
+ # rows.each {|row| yield hash_row(row)}
119
+ # ensure
120
+ # s.drop unless s.nil? rescue nil
121
+ # end
122
+ # end
123
+ # self
124
+ # end
125
+
126
+ def hash_row(row)
127
+ hash = {}
128
+ row.each_with_index do |v, idx|
129
+ hash[@columns[idx]] = convert_odbc_value(v)
130
+ end
131
+ hash
132
+ end
133
+
134
+ def convert_odbc_value(v)
135
+ # When fetching a result set, the Ruby ODBC driver converts all ODBC
136
+ # SQL types to an equivalent Ruby type; with the exception of
137
+ # SQL_TYPE_DATE, SQL_TYPE_TIME and SQL_TYPE_TIMESTAMP.
138
+ #
139
+ # The conversions below are consistent with the mappings in
140
+ # ODBCColumn#mapSqlTypeToGenericType and Column#klass.
141
+ case v
142
+ when ::ODBC::TimeStamp
143
+ DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
144
+ when ::ODBC::Time
145
+ now = DateTime.now
146
+ Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
147
+ when ::ODBC::Date
148
+ Date.new(v.year, v.month, v.day)
149
+ else
150
+ v
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,106 @@
1
+ if !Sequel.const_defined?('ODBC')
2
+ require File.join(File.dirname(__FILE__), 'odbc')
3
+ end
4
+
5
+ module Sequel
6
+ module ODBC
7
+ module MSSQL
8
+ class Database < ODBC::Database
9
+ set_adapter_scheme :odbc_mssql
10
+
11
+ def dataset(opts = nil)
12
+ MSSQL::Dataset.new(self, opts)
13
+ end
14
+ end
15
+
16
+ class Dataset < ODBC::Dataset
17
+ # Allows you to do .nolock on a query
18
+ def nolock
19
+ clone(:with => "(NOLOCK)")
20
+ end
21
+
22
+ # Formats a SELECT statement using the given options and the dataset
23
+ # options.
24
+ def select_sql(opts = nil)
25
+ opts = opts ? @opts.merge(opts) : @opts
26
+
27
+ if sql = opts[:sql]
28
+ return sql
29
+ end
30
+
31
+ # ADD TOP to SELECT string for LIMITS
32
+ if limit = opts[:limit]
33
+ top = "TOP #{limit} "
34
+ raise Error, "Offset not supported" if opts[:offset]
35
+ end
36
+
37
+ columns = opts[:select]
38
+ select_columns = columns ? column_list(columns) : WILDCARD
39
+
40
+ if distinct = opts[:distinct]
41
+ distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{expression_list(distinct)})"
42
+ sql = "SELECT #{top}#{distinct_clause} #{select_columns}"
43
+ else
44
+ sql = "SELECT #{top}#{select_columns}"
45
+ end
46
+
47
+ if opts[:from]
48
+ sql << " FROM #{source_list(opts[:from])}"
49
+ end
50
+
51
+ # ADD WITH to SELECT string for NOLOCK
52
+ if with = opts[:with]
53
+ sql << " WITH #{with}"
54
+ end
55
+
56
+ if join = opts[:join]
57
+ join.each{|j| sql << literal(j)}
58
+ end
59
+
60
+ if where = opts[:where]
61
+ sql << " WHERE #{literal(where)}"
62
+ end
63
+
64
+ if group = opts[:group]
65
+ sql << " GROUP BY #{expression_list(group)}"
66
+ end
67
+
68
+ if order = opts[:order]
69
+ sql << " ORDER BY #{expression_list(order)}"
70
+ end
71
+
72
+ if having = opts[:having]
73
+ sql << " HAVING #{literal(having)}"
74
+ end
75
+
76
+ if union = opts[:union]
77
+ sql << (opts[:union_all] ? \
78
+ " UNION ALL #{union.sql}" : " UNION #{union.sql}")
79
+ elsif intersect = opts[:intersect]
80
+ sql << (opts[:intersect_all] ? \
81
+ " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
82
+ elsif except = opts[:except]
83
+ sql << (opts[:except_all] ? \
84
+ " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
85
+ end
86
+
87
+ sql
88
+ end
89
+ alias_method :sql, :select_sql
90
+
91
+ def full_text_search(cols, terms, opts = {})
92
+ filter("CONTAINS (#{literal(cols)}, #{literal(terms)})")
93
+ end
94
+
95
+ def complex_expression_sql(op, args)
96
+ case op
97
+ when :'||'
98
+ super(:+, args)
99
+ else
100
+ super(op, args)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,64 @@
1
+ require 'openbase'
2
+
3
+ module Sequel
4
+ module OpenBase
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :openbase
7
+
8
+ def connect
9
+ OpenBase.new(
10
+ opts[:database],
11
+ opts[:host] || 'localhost',
12
+ opts[:user],
13
+ opts[:password]
14
+ )
15
+ end
16
+
17
+ def disconnect
18
+ # would this work?
19
+ @pool.disconnect {|c| c.disconnect}
20
+ end
21
+
22
+ def dataset(opts = nil)
23
+ OpenBase::Dataset.new(self, opts)
24
+ end
25
+
26
+ def execute(sql)
27
+ log_info(sql)
28
+ @pool.hold {|conn| conn.execute(sql)}
29
+ end
30
+
31
+ alias_method :do, :execute
32
+ end
33
+
34
+ class Dataset < Sequel::Dataset
35
+ def literal(v)
36
+ case v
37
+ when Time
38
+ literal(v.iso8601)
39
+ when Date, DateTime
40
+ literal(v.to_s)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def fetch_rows(sql, &block)
47
+ @db.synchronize do
48
+ result = @db.execute sql
49
+ begin
50
+ @columns = result.column_infos.map {|c| c.name.to_sym}
51
+ result.each do |r|
52
+ row = {}
53
+ r.each_with_index {|v, i| row[@columns[i]] = v}
54
+ yield row
55
+ end
56
+ ensure
57
+ # result.close
58
+ end
59
+ end
60
+ self
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,170 @@
1
+ require 'oci8'
2
+
3
+ module Sequel
4
+ module Oracle
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :oracle
7
+
8
+ # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
9
+ #
10
+ # def auto_increment_sql
11
+ # AUTO_INCREMENT
12
+ # end
13
+
14
+ def connect
15
+ if @opts[:database]
16
+ dbname = @opts[:host] ? \
17
+ "//#{@opts[:host]}#{":#{@opts[:port]}" if @opts[:port]}/#{@opts[:database]}" : @opts[:database]
18
+ else
19
+ dbname = @opts[:host]
20
+ end
21
+ conn = OCI8.new(@opts[:user], @opts[:password], dbname, @opts[:privilege])
22
+ conn.autocommit = true
23
+ conn.non_blocking = true
24
+ conn
25
+ end
26
+
27
+ def disconnect
28
+ @pool.disconnect {|c| c.logoff}
29
+ end
30
+
31
+ def dataset(opts = nil)
32
+ Oracle::Dataset.new(self, opts)
33
+ end
34
+
35
+ def execute(sql)
36
+ log_info(sql)
37
+ @pool.hold {|conn| conn.exec(sql)}
38
+ end
39
+
40
+ alias_method :do, :execute
41
+
42
+ def tables
43
+ from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
44
+ r[:tname].downcase.to_sym
45
+ end
46
+ end
47
+
48
+ def table_exists?(name)
49
+ from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
50
+ end
51
+
52
+ def transaction
53
+ @pool.hold do |conn|
54
+ @transactions ||= []
55
+ if @transactions.include? Thread.current
56
+ return yield(conn)
57
+ end
58
+
59
+ conn.autocommit = false
60
+ begin
61
+ @transactions << Thread.current
62
+ yield(conn)
63
+ rescue => e
64
+ conn.rollback
65
+ raise e unless Error::Rollback === e
66
+ ensure
67
+ conn.commit unless e
68
+ conn.autocommit = true
69
+ @transactions.delete(Thread.current)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ class Dataset < Sequel::Dataset
76
+ def literal(v)
77
+ case v
78
+ when OraDate
79
+ literal(Time.local(*v.to_a))
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def fetch_rows(sql, &block)
86
+ @db.synchronize do
87
+ cursor = @db.execute sql
88
+ begin
89
+ @columns = cursor.get_col_names.map {|c| c.downcase.to_sym}
90
+ while r = cursor.fetch
91
+ row = {}
92
+ r.each_with_index {|v, i| row[columns[i]] = v unless columns[i] == :raw_rnum_}
93
+ yield row
94
+ end
95
+ ensure
96
+ cursor.close
97
+ end
98
+ end
99
+ self
100
+ end
101
+
102
+ def empty?
103
+ db[:dual].where(exists).get(1) == nil
104
+ end
105
+
106
+ # Formats a SELECT statement using the given options and the dataset
107
+ # options.
108
+ def select_sql(opts = nil)
109
+ opts = opts ? @opts.merge(opts) : @opts
110
+
111
+ if sql = opts[:sql]
112
+ return sql
113
+ end
114
+
115
+ columns = opts[:select]
116
+ select_columns = columns ? column_list(columns) : WILDCARD
117
+ sql = opts[:distinct] ? \
118
+ "SELECT DISTINCT #{select_columns}" : \
119
+ "SELECT #{select_columns}"
120
+
121
+ if opts[:from]
122
+ sql << " FROM #{source_list(opts[:from])}"
123
+ end
124
+
125
+ if join = opts[:join]
126
+ join.each{|j| sql << literal(j)}
127
+ end
128
+
129
+ if where = opts[:where]
130
+ sql << " WHERE #{literal(where)}"
131
+ end
132
+
133
+ if group = opts[:group]
134
+ sql << " GROUP BY #{expression_list(group)}"
135
+ end
136
+
137
+ if having = opts[:having]
138
+ sql << " HAVING #{literal(having)}"
139
+ end
140
+
141
+ if union = opts[:union]
142
+ sql << (opts[:union_all] ? \
143
+ " UNION ALL #{union.sql}" : " UNION #{union.sql}")
144
+ elsif intersect = opts[:intersect]
145
+ sql << (opts[:intersect_all] ? \
146
+ " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
147
+ elsif except = opts[:except]
148
+ sql << (opts[:except_all] ? \
149
+ " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
150
+ end
151
+
152
+ if order = opts[:order]
153
+ sql << " ORDER BY #{expression_list(order)}"
154
+ end
155
+
156
+ if limit = opts[:limit]
157
+ if (offset = opts[:offset]) && (offset > 0)
158
+ sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
159
+ else
160
+ sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
161
+ end
162
+ end
163
+
164
+ sql
165
+ end
166
+
167
+ alias sql select_sql
168
+ end
169
+ end
170
+ end