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,351 @@
1
+ module Sequel
2
+ module Postgres
3
+ CONVERTED_EXCEPTIONS = []
4
+
5
+ module AdapterMethods
6
+ SELECT_CURRVAL = "SELECT currval('%s')".freeze
7
+ SELECT_PK = <<-end_sql
8
+ SELECT pg_attribute.attname
9
+ FROM pg_class, pg_attribute, pg_index
10
+ WHERE pg_class.oid = pg_attribute.attrelid AND
11
+ pg_class.oid = pg_index.indrelid AND
12
+ pg_index.indkey[0] = pg_attribute.attnum AND
13
+ pg_index.indisprimary = 't' AND
14
+ pg_class.relname = '%s'
15
+ end_sql
16
+ SELECT_PK_AND_CUSTOM_SEQUENCE = <<-end_sql
17
+ SELECT attr.attname,
18
+ CASE
19
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
20
+ substr(split_part(def.adsrc, '''', 2),
21
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
22
+ ELSE split_part(def.adsrc, '''', 2)
23
+ END
24
+ FROM pg_class t
25
+ JOIN pg_namespace name ON (t.relnamespace = name.oid)
26
+ JOIN pg_attribute attr ON (t.oid = attrelid)
27
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
28
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
29
+ WHERE t.oid = '%s'::regclass
30
+ AND cons.contype = 'p'
31
+ AND def.adsrc ~* 'nextval'
32
+ end_sql
33
+ SELECT_PK_AND_SERIAL_SEQUENCE = <<-end_sql
34
+ SELECT attr.attname, name.nspname, seq.relname
35
+ FROM pg_class seq, pg_attribute attr, pg_depend dep,
36
+ pg_namespace name, pg_constraint cons
37
+ WHERE seq.oid = dep.objid
38
+ AND seq.relnamespace = name.oid
39
+ AND seq.relkind = 'S'
40
+ AND attr.attrelid = dep.refobjid
41
+ AND attr.attnum = dep.refobjsubid
42
+ AND attr.attrelid = cons.conrelid
43
+ AND attr.attnum = cons.conkey[1]
44
+ AND cons.contype = 'p'
45
+ AND dep.refobjid = '%s'::regclass
46
+ end_sql
47
+
48
+ attr_accessor :transaction_depth
49
+
50
+ def last_insert_id(table)
51
+ @table_sequences ||= {}
52
+ if !@table_sequences.include?(table)
53
+ pkey_and_seq = pkey_and_sequence(table)
54
+ if pkey_and_seq
55
+ @table_sequences[table] = pkey_and_seq[1]
56
+ end
57
+ end
58
+ if seq = @table_sequences[table]
59
+ execute(SELECT_CURRVAL % seq) do |r|
60
+ val = result_set_values(r, 0)
61
+ val.to_i if val
62
+ end
63
+ end
64
+ end
65
+
66
+ def pkey_and_sequence(table)
67
+ execute(SELECT_PK_AND_SERIAL_SEQUENCE % table) do |r|
68
+ vals = result_set_values(r, 2, 2)
69
+ return vals if vals
70
+ end
71
+
72
+ execute(SELECT_PK_AND_CUSTOM_SEQUENCE % table) do |r|
73
+ result_set_values(r, 0, 1)
74
+ end
75
+ end
76
+
77
+ def primary_key(table)
78
+ execute(SELECT_PK % table) do |r|
79
+ result_set_values(r, 0)
80
+ end
81
+ end
82
+ end
83
+
84
+ module DatabaseMethods
85
+ RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session/.freeze
86
+ RELATION_QUERY = {:from => [:pg_class], :select => [:relname]}.freeze
87
+ RELATION_FILTER = "(relkind = 'r') AND (relname !~ '^pg|sql')".freeze
88
+ SQL_BEGIN = 'BEGIN'.freeze
89
+ SQL_SAVEPOINT = 'SAVEPOINT autopoint_%d'.freeze
90
+ SQL_COMMIT = 'COMMIT'.freeze
91
+ SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TO SAVEPOINT autopoint_%d'.freeze
92
+ SQL_ROLLBACK = 'ROLLBACK'.freeze
93
+ SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
94
+ SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
95
+
96
+ def drop_table_sql(name)
97
+ "DROP TABLE #{name} CASCADE"
98
+ end
99
+
100
+ def execute_insert(sql, table, values)
101
+ begin
102
+ log_info(sql)
103
+ @pool.hold do |conn|
104
+ conn.execute(sql)
105
+ insert_result(conn, table, values)
106
+ end
107
+ rescue => e
108
+ log_info(e.message)
109
+ raise convert_pgerror(e)
110
+ end
111
+ end
112
+
113
+ def index_definition_sql(table_name, index)
114
+ index_name = index[:name] || default_index_name(table_name, index[:columns])
115
+ expr = literal(Array(index[:columns]))
116
+ unique = "UNIQUE " if index[:unique]
117
+ index_type = index[:type]
118
+ filter = index[:where] || index[:filter]
119
+ filter = " WHERE #{filter_expr(filter)}" if filter
120
+ case index_type
121
+ when :full_text
122
+ lang = index[:language] ? "#{literal(index[:language])}, " : ""
123
+ cols = index[:columns].map {|c| literal(c)}.join(" || ")
124
+ expr = "(to_tsvector(#{lang}#{cols}))"
125
+ index_type = :gin
126
+ when :spatial
127
+ index_type = :gist
128
+ end
129
+ "CREATE #{unique}INDEX #{index_name} ON #{table_name} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
130
+ end
131
+
132
+ def insert_result(conn, table, values)
133
+ begin
134
+ result = conn.last_insert_id(table)
135
+ return result if result
136
+ rescue Exception => e
137
+ convert_pgerror(e) unless RE_CURRVAL_ERROR.match(e.message)
138
+ end
139
+
140
+ case values
141
+ when Hash
142
+ values[primary_key_for_table(conn, table)]
143
+ when Array
144
+ values.first
145
+ else
146
+ nil
147
+ end
148
+ end
149
+
150
+ def locks
151
+ dataset.from("pg_class, pg_locks").
152
+ select("pg_class.relname, pg_locks.*").
153
+ filter("pg_class.relfilenode=pg_locks.relation")
154
+ end
155
+
156
+ def primary_key_for_table(conn, table)
157
+ @primary_keys ||= {}
158
+ @primary_keys[table] ||= conn.primary_key(table)
159
+ end
160
+
161
+ def serial_primary_key_options
162
+ {:primary_key => true, :type => :serial}
163
+ end
164
+
165
+ def server_version
166
+ return @server_version if @server_version
167
+ @server_version = pool.hold do |conn|
168
+ (conn.server_version rescue nil) if conn.respond_to?(:server_version)
169
+ end
170
+ unless @server_version
171
+ m = /PostgreSQL (\d+)\.(\d+)\.(\d+)/.match(get(:version[]))
172
+ @server_version = (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
173
+ end
174
+ @server_version
175
+ end
176
+
177
+ def tables
178
+ dataset(RELATION_QUERY).filter(RELATION_FILTER).map {|r| r[:relname].to_sym}
179
+ end
180
+
181
+ def transaction
182
+ @pool.hold do |conn|
183
+ conn.transaction_depth = 0 if conn.transaction_depth.nil?
184
+ if conn.transaction_depth > 0
185
+ log_info(SQL_SAVEPOINT % conn.transaction_depth)
186
+ conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
187
+ else
188
+ log_info(SQL_BEGIN)
189
+ conn.execute(SQL_BEGIN)
190
+ end
191
+ begin
192
+ conn.transaction_depth += 1
193
+ yield conn
194
+ rescue ::Exception => e
195
+ if conn.transaction_depth > 1
196
+ log_info(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
197
+ conn.execute(SQL_ROLLBACK_TO_SAVEPOINT % [conn.transaction_depth - 1])
198
+ else
199
+ log_info(SQL_ROLLBACK)
200
+ conn.execute(SQL_ROLLBACK) rescue nil
201
+ end
202
+ raise convert_pgerror(e) unless Error::Rollback === e
203
+ ensure
204
+ unless e
205
+ begin
206
+ if conn.transaction_depth < 2
207
+ log_info(SQL_COMMIT)
208
+ conn.execute(SQL_COMMIT)
209
+ else
210
+ log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
211
+ conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
212
+ end
213
+ rescue => e
214
+ log_info(e.message)
215
+ raise convert_pgerror(e)
216
+ end
217
+ end
218
+ conn.transaction_depth -= 1
219
+ end
220
+ end
221
+ end
222
+
223
+ private
224
+
225
+ def convert_pgerror(e)
226
+ e.is_one_of?(*CONVERTED_EXCEPTIONS) ? Error.new(e.message) : e
227
+ end
228
+
229
+ def schema_ds_filter(table_name, opts)
230
+ filt = super
231
+ # Restrict it to the given or public schema, unless specifically requesting :schema = nil
232
+ filt = SQL::BooleanExpression.new(:AND, filt, {:c__table_schema=>opts[:schema] || 'public'}) if opts[:schema] || !opts.include?(:schema)
233
+ filt
234
+ end
235
+ end
236
+
237
+ module DatasetMethods
238
+ ACCESS_SHARE = 'ACCESS SHARE'.freeze
239
+ ACCESS_EXCLUSIVE = 'ACCESS EXCLUSIVE'.freeze
240
+ BOOL_FALSE = 'false'.freeze
241
+ BOOL_TRUE = 'true'.freeze
242
+ COMMA_SEPARATOR = ', '.freeze
243
+ EXCLUSIVE = 'EXCLUSIVE'.freeze
244
+ EXPLAIN = 'EXPLAIN '.freeze
245
+ EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
246
+ FOR_SHARE = ' FOR SHARE'.freeze
247
+ FOR_UPDATE = ' FOR UPDATE'.freeze
248
+ LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
249
+ PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
250
+ QUERY_PLAN = 'QUERY PLAN'.to_sym
251
+ ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
252
+ ROW_SHARE = 'ROW SHARE'.freeze
253
+ SHARE = 'SHARE'.freeze
254
+ SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
255
+ SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
256
+
257
+ def analyze(opts = nil)
258
+ analysis = []
259
+ fetch_rows(EXPLAIN_ANALYZE + select_sql(opts)) do |r|
260
+ analysis << r[QUERY_PLAN]
261
+ end
262
+ analysis.join("\r\n")
263
+ end
264
+
265
+ def explain(opts = nil)
266
+ analysis = []
267
+ fetch_rows(EXPLAIN + select_sql(opts)) do |r|
268
+ analysis << r[QUERY_PLAN]
269
+ end
270
+ analysis.join("\r\n")
271
+ end
272
+
273
+ def for_share
274
+ clone(:lock => :share)
275
+ end
276
+
277
+ def for_update
278
+ clone(:lock => :update)
279
+ end
280
+
281
+ def full_text_search(cols, terms, opts = {})
282
+ lang = opts[:language] ? "#{literal(opts[:language])}, " : ""
283
+ cols = cols.is_a?(Array) ? cols.map {|c| literal(c)}.join(" || ") : literal(cols)
284
+ terms = terms.is_a?(Array) ? literal(terms.join(" | ")) : literal(terms)
285
+ filter("to_tsvector(#{lang}#{cols}) @@ to_tsquery(#{lang}#{terms})")
286
+ end
287
+
288
+ def insert(*values)
289
+ @db.execute_insert(insert_sql(*values), source_list(@opts[:from]),
290
+ values.size == 1 ? values.first : values)
291
+ end
292
+
293
+ def literal(v)
294
+ case v
295
+ when LiteralString
296
+ v
297
+ when String
298
+ db.synchronize{|c| "'#{SQL::Blob === v ? c.escape_bytea(v) : c.escape_string(v)}'"}
299
+ when Time
300
+ "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d",v.usec)}'"
301
+ when DateTime
302
+ "#{v.strftime(PG_TIMESTAMP_FORMAT)}.#{sprintf("%06d", (v.sec_fraction * 86400000000).to_i)}'"
303
+ when TrueClass
304
+ BOOL_TRUE
305
+ when FalseClass
306
+ BOOL_FALSE
307
+ else
308
+ super
309
+ end
310
+ end
311
+
312
+ # Locks the table with the specified mode.
313
+ def lock(mode, &block)
314
+ sql = LOCK % [source_list(@opts[:from]), mode]
315
+ @db.synchronize do
316
+ if block # perform locking inside a transaction and yield to block
317
+ @db.transaction {@db.execute(sql); yield}
318
+ else
319
+ @db.execute(sql) # lock without a transaction
320
+ self
321
+ end
322
+ end
323
+ end
324
+
325
+ def multi_insert_sql(columns, values)
326
+ return super if @db.server_version < 80200
327
+
328
+ # postgresql 8.2 introduces support for multi-row insert
329
+ columns = column_list(columns)
330
+ values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
331
+ ["INSERT INTO #{source_list(@opts[:from])} (#{columns}) VALUES #{values}"]
332
+ end
333
+
334
+ def quoted_identifier(c)
335
+ "\"#{c}\""
336
+ end
337
+
338
+ def select_sql(opts = nil)
339
+ row_lock_mode = opts ? opts[:lock] : @opts[:lock]
340
+ sql = super
341
+ case row_lock_mode
342
+ when :update
343
+ sql << FOR_UPDATE
344
+ when :share
345
+ sql << FOR_SHARE
346
+ end
347
+ sql
348
+ end
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,146 @@
1
+ module Sequel
2
+ module SQLite
3
+ module DatabaseMethods
4
+ AUTO_VACUUM = {'0' => :none, '1' => :full, '2' => :incremental}.freeze
5
+ SCHEMA_TYPE_RE = /\A(\w+)\((\d+)\)\z/
6
+ SYNCHRONOUS = {'0' => :off, '1' => :normal, '2' => :full}.freeze
7
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
8
+ TEMP_STORE = {'0' => :default, '1' => :file, '2' => :memory}.freeze
9
+
10
+ def alter_table_sql(table, op)
11
+ case op[:op]
12
+ when :add_column
13
+ super
14
+ when :add_index
15
+ index_definition_sql(table, op)
16
+ when :drop_column
17
+ columns_str = (schema_parse_table(table, {}).map{|c| c[0]} - Array(op[:name])).join(",")
18
+ ["BEGIN TRANSACTION",
19
+ "CREATE TEMPORARY TABLE #{table}_backup(#{columns_str})",
20
+ "INSERT INTO #{table}_backup SELECT #{columns_str} FROM #{table}",
21
+ "DROP TABLE #{table}",
22
+ "CREATE TABLE #{table}(#{columns_str})",
23
+ "INSERT INTO #{table} SELECT #{columns_str} FROM #{table}_backup",
24
+ "DROP TABLE #{table}_backup",
25
+ "COMMIT"]
26
+ else
27
+ raise Error, "Unsupported ALTER TABLE operation"
28
+ end
29
+ end
30
+
31
+ def auto_vacuum
32
+ AUTO_VACUUM[pragma_get(:auto_vacuum).to_s]
33
+ end
34
+
35
+ def auto_vacuum=(value)
36
+ value = AUTO_VACUUM.key(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
37
+ pragma_set(:auto_vacuum, value)
38
+ end
39
+
40
+ def pragma_get(name)
41
+ self["PRAGMA #{name}"].single_value
42
+ end
43
+
44
+ def pragma_set(name, value)
45
+ execute_ddl("PRAGMA #{name} = #{value}")
46
+ end
47
+
48
+ def serial_primary_key_options
49
+ {:primary_key => true, :type => :integer, :auto_increment => true}
50
+ end
51
+
52
+ def synchronous
53
+ SYNCHRONOUS[pragma_get(:synchronous).to_s]
54
+ end
55
+
56
+ def synchronous=(value)
57
+ value = SYNCHRONOUS.key(value) || (raise Error, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
58
+ pragma_set(:synchronous, value)
59
+ end
60
+
61
+ def tables
62
+ self[:sqlite_master].filter(TABLES_FILTER).map {|r| r[:name].to_sym}
63
+ end
64
+
65
+ def temp_store
66
+ TEMP_STORE[pragma_get(:temp_store).to_s]
67
+ end
68
+
69
+ def temp_store=(value)
70
+ value = TEMP_STORE.key(value) || (raise Error, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
71
+ pragma_set(:temp_store, value)
72
+ end
73
+
74
+ private
75
+
76
+ def schema_parse_table(table_name, opts)
77
+ rows = self["PRAGMA table_info(?)", table_name].collect do |row|
78
+ row.delete(:cid)
79
+ row[:column] = row.delete(:name)
80
+ row[:allow_null] = row.delete(:notnull).to_i == 0 ? 'YES' : 'NO'
81
+ row[:default] = row.delete(:dflt_value)
82
+ row[:primary_key] = row.delete(:pk).to_i == 1 ? true : false
83
+ row[:db_type] = row.delete(:type)
84
+ if m = SCHEMA_TYPE_RE.match(row[:db_type])
85
+ row[:db_type] = m[1]
86
+ row[:max_chars] = m[2].to_i
87
+ else
88
+ row[:max_chars] = nil
89
+ end
90
+ row[:numeric_precision] = nil
91
+ row
92
+ end
93
+ schema_parse_rows(rows)
94
+ end
95
+
96
+ def schema_parse_tables(opts)
97
+ schemas = {}
98
+ tables.each{|table| schemas[table] = schema_parse_table(table, opts)}
99
+ schemas
100
+ end
101
+ end
102
+
103
+ module DatasetMethods
104
+ def complex_expression_sql(op, args)
105
+ case op
106
+ when :~, :'!~', :'~*', :'!~*'
107
+ raise Error, "SQLite does not support pattern matching via regular expressions"
108
+ when :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
109
+ # SQLite is case insensitive for ASCII, and non case sensitive for other character sets
110
+ "#{'NOT ' if [:'NOT LIKE', :'NOT ILIKE'].include?(op)}(#{literal(args.at(0))} LIKE #{literal(args.at(1))})"
111
+ else
112
+ super(op, args)
113
+ end
114
+ end
115
+
116
+ def delete(opts = nil)
117
+ # check if no filter is specified
118
+ unless (opts && opts[:where]) || @opts[:where]
119
+ @db.transaction do
120
+ unfiltered_count = count
121
+ @db.execute_dui delete_sql(opts)
122
+ unfiltered_count
123
+ end
124
+ else
125
+ @db.execute_dui delete_sql(opts)
126
+ end
127
+ end
128
+
129
+ def insert(*values)
130
+ @db.execute_insert insert_sql(*values)
131
+ end
132
+
133
+ def insert_sql(*values)
134
+ if (values.size == 1) && values.first.is_a?(Sequel::Dataset)
135
+ "INSERT INTO #{source_list(@opts[:from])} #{values.first.sql};"
136
+ else
137
+ super(*values)
138
+ end
139
+ end
140
+
141
+ def quoted_identifier(c)
142
+ "`#{c}`"
143
+ end
144
+ end
145
+ end
146
+ end