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,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