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