baza 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "datet"
7
+ gem "wref"
8
+ gem "knjrbfw"
9
+
10
+ # Add dependencies to develop your gem here.
11
+ # Include everything needed to run rake, tests, features, etc.
12
+ group :development do
13
+ gem "rspec", "~> 2.8.0"
14
+ gem "rdoc", "~> 3.12"
15
+ gem "bundler", ">= 1.0.0"
16
+ gem "jeweler", "~> 1.8.4"
17
+ gem "sqlite3"
18
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ datet (0.0.25)
5
+ diff-lcs (1.1.3)
6
+ git (1.2.5)
7
+ http2 (0.0.13)
8
+ jeweler (1.8.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ rdoc
13
+ json (1.7.7)
14
+ knjrbfw (0.0.104)
15
+ datet
16
+ http2
17
+ php4r
18
+ ruby_process
19
+ tsafe
20
+ wref
21
+ php4r (0.0.4)
22
+ datet
23
+ http2
24
+ string-strtr
25
+ rake (10.0.4)
26
+ rdoc (3.12.2)
27
+ json (~> 1.4)
28
+ rspec (2.8.0)
29
+ rspec-core (~> 2.8.0)
30
+ rspec-expectations (~> 2.8.0)
31
+ rspec-mocks (~> 2.8.0)
32
+ rspec-core (2.8.0)
33
+ rspec-expectations (2.8.0)
34
+ diff-lcs (~> 1.1.2)
35
+ rspec-mocks (2.8.0)
36
+ ruby_process (0.0.8)
37
+ tsafe
38
+ wref
39
+ sqlite3 (1.3.7)
40
+ string-strtr (0.0.3)
41
+ tsafe (0.0.11)
42
+ wref (0.0.6)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ bundler (>= 1.0.0)
49
+ datet
50
+ jeweler (~> 1.8.4)
51
+ knjrbfw
52
+ rdoc (~> 3.12)
53
+ rspec (~> 2.8.0)
54
+ sqlite3
55
+ wref
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Kasper Johansen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = baza
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to baza
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2013 Kasper Johansen. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "baza"
18
+ gem.homepage = "http://github.com/kaspernj/baza"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A database abstraction layer, model framework and database framework.}
21
+ gem.description = %Q{A database abstraction layer, model framework and database framework.}
22
+ gem.email = "kj@gfish.com"
23
+ gem.authors = ["Kasper Johansen"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "baza #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/include/db.rb ADDED
@@ -0,0 +1,784 @@
1
+ require "knjrbfw"
2
+ Knj.gem_require([:wref, :datet])
3
+
4
+ #A wrapper of several possible database-types.
5
+ #
6
+ #===Examples
7
+ # db = Baza::Db.new(:type => "mysql", :subtype => "mysql2", :db => "mysql", :user => "user", :pass => "password")
8
+ # mysql_table = db.tables['mysql']
9
+ # name = mysql_table.name
10
+ # cols = mysql_table.columns
11
+ #
12
+ # db = Baza::Db.new(:type => "sqlite3", :path => "some_db.sqlite3")
13
+ #
14
+ # db.q("SELECT * FROM users") do |data|
15
+ # print data[:name]
16
+ # end
17
+ class Baza::Db
18
+ attr_reader :sep_col, :sep_table, :sep_val, :opts, :conn, :conns, :int_types
19
+
20
+ def initialize(opts)
21
+ self.setOpts(opts) if opts != nil
22
+
23
+ @int_types = ["int", "bigint", "tinyint", "smallint", "mediumint"]
24
+
25
+ if !@opts[:threadsafe]
26
+ require "monitor"
27
+ @mutex = Monitor.new
28
+ end
29
+
30
+ @debug = @opts[:debug]
31
+
32
+ self.conn_exec do |driver|
33
+ @sep_table = driver.sep_table
34
+ @sep_col = driver.sep_col
35
+ @sep_val = driver.sep_val
36
+ @esc_driver = driver
37
+ end
38
+ end
39
+
40
+ def args
41
+ return @opts
42
+ end
43
+
44
+ def setOpts(arr_opts)
45
+ @opts = {}
46
+ arr_opts.each do |key, val|
47
+ @opts[key.to_sym] = val
48
+ end
49
+
50
+ if RUBY_PLATFORM == "java"
51
+ @opts[:subtype] = "java"
52
+ elsif @opts[:type] == "sqlite3" and RUBY_PLATFORM.index("mswin32") != nil
53
+ @opts[:subtype] = "ironruby"
54
+ end
55
+
56
+ @type_cc = "#{@opts[:type].to_s.slice(0, 1).upcase}#{@opts[:type].to_s.slice(1, @opts[:type].to_s.length)}"
57
+ self.connect
58
+ end
59
+
60
+ #Actually connects to the database. This is useually done automatically.
61
+ def connect
62
+ if @opts[:threadsafe]
63
+ require "#{$knjpath}threadhandler"
64
+ @conns = Knj::Threadhandler.new
65
+
66
+ @conns.on_spawn_new do
67
+ self.spawn
68
+ end
69
+
70
+ @conns.on_inactive do |data|
71
+ data[:obj].close
72
+ end
73
+
74
+ @conns.on_activate do |data|
75
+ data[:obj].reconnect
76
+ end
77
+ else
78
+ @conn = self.spawn
79
+ end
80
+ end
81
+
82
+ #Spawns a new driver (useally done automatically).
83
+ #===Examples
84
+ # driver_instance = db.spawn
85
+ def spawn
86
+ raise "No type given (#{@opts.keys.join(",")})." if !@opts[:type]
87
+ rpath = "#{File.dirname(__FILE__)}/#{"drivers/#{@opts[:type]}/#{@opts[:type]}.rb"}"
88
+ require rpath if (!@opts.key?(:require) or @opts[:require]) and File.exists?(rpath)
89
+ return Baza::Driver.const_get(@type_cc).new(self)
90
+ end
91
+
92
+ #Registers a driver to the current thread.
93
+ def get_and_register_thread
94
+ raise "KnjDB-object is not in threadding mode." if !@conns
95
+
96
+ thread_cur = Thread.current
97
+ tid = self.__id__
98
+ thread_cur[:knjdb] = {} if !thread_cur[:knjdb]
99
+
100
+ if thread_cur[:knjdb][tid]
101
+ #An object has already been spawned - free that first to avoid endless "used" objects.
102
+ self.free_thread
103
+ end
104
+
105
+ thread_cur[:knjdb][tid] = @conns.get_and_lock if !thread_cur[:knjdb][tid]
106
+
107
+ #If block given then be ensure to free thread after yielding.
108
+ if block_given?
109
+ begin
110
+ yield
111
+ ensure
112
+ self.free_thread
113
+ end
114
+ end
115
+ end
116
+
117
+ #Frees the current driver from the current thread.
118
+ def free_thread
119
+ thread_cur = Thread.current
120
+ tid = self.__id__
121
+
122
+ if thread_cur[:knjdb] and thread_cur[:knjdb].key?(tid)
123
+ db = thread_cur[:knjdb][tid]
124
+ thread_cur[:knjdb].delete(tid)
125
+ @conns.free(db) if @conns
126
+ end
127
+ end
128
+
129
+ #Clean up various memory-stuff if possible.
130
+ def clean
131
+ if @conns
132
+ @conns.objects.each do |data|
133
+ data[:object].clean if data[:object].respond_to?("clean")
134
+ end
135
+ elsif @conn
136
+ @conn.clean if @conn.respond_to?("clean")
137
+ end
138
+ end
139
+
140
+ #The all driver-database-connections.
141
+ def close
142
+ @conn.close if @conn
143
+ @conns.destroy if @conns
144
+
145
+ @conn = nil
146
+ @conns = nil
147
+ end
148
+
149
+ #Clones the current database-connection with possible extra arguments.
150
+ def clone_conn(args = {})
151
+ conn = Baza::Db.new(@opts.clone.merge(args))
152
+
153
+ if block_given?
154
+ begin
155
+ yield(conn)
156
+ ensure
157
+ conn.close
158
+ end
159
+
160
+ return nil
161
+ else
162
+ return conn
163
+ end
164
+ end
165
+
166
+ #Copies the content of the current database to another instance of Baza::Db.
167
+ def copy_to(db, args = {})
168
+ data["tables"].each do |table|
169
+ table_args = nil
170
+ table_args = args["tables"][table["name"].to_s] if args and args["tables"] and args["tables"][table["name"].to_s]
171
+ next if table_args and table_args["skip"]
172
+ table.delete("indexes") if table.key?("indexes") and args["skip_indexes"]
173
+ db.tables.create(table["name"], table)
174
+
175
+ limit_from = 0
176
+ limit_incr = 1000
177
+
178
+ loop do
179
+ ins_arr = []
180
+ q_rows = self.select(table["name"], {}, {"limit_from" => limit_from, "limit_to" => limit_incr})
181
+ while d_rows = q_rows.fetch
182
+ col_args = nil
183
+
184
+ if table_args and table_args["columns"]
185
+ d_rows.each do |col_name, col_data|
186
+ col_args = table_args["columns"][col_name.to_s] if table_args and table_args["columns"]
187
+ d_rows[col_name] = "" if col_args and col_args["empty"]
188
+ end
189
+ end
190
+
191
+ ins_arr << d_rows
192
+ end
193
+
194
+ break if ins_arr.empty?
195
+
196
+ db.insert_multi(table["name"], ins_arr)
197
+ limit_from += limit_incr
198
+ end
199
+ end
200
+ end
201
+
202
+ #Returns the data of this database in a hash.
203
+ #===Examples
204
+ # data = db.data
205
+ # tables_hash = data['tables']
206
+ def data
207
+ tables_ret = []
208
+ tables.list.each do |name, table|
209
+ tables_ret << table.data
210
+ end
211
+
212
+ return {
213
+ "tables" => tables_ret
214
+ }
215
+ end
216
+
217
+ #Simply inserts data into a table.
218
+ #
219
+ #===Examples
220
+ # db.insert(:users, {:name => "John", :lastname => "Doe"})
221
+ # id = db.insert(:users, {:name => "John", :lastname => "Doe"}, :return_id => true)
222
+ # sql = db.insert(:users, {:name => "John", :lastname => "Doe"}, :return_sql => true) #=> "INSERT INTO `users` (`name`, `lastname`) VALUES ('John', 'Doe')"
223
+ def insert(tablename, arr_insert, args = nil)
224
+ sql = "INSERT INTO #{@sep_table}#{tablename}#{@sep_table}"
225
+
226
+ if !arr_insert or arr_insert.empty?
227
+ #This is the correct syntax for inserting a blank row in MySQL.
228
+ if @opts[:type].to_s == "mysql"
229
+ sql << " VALUES ()"
230
+ elsif @opts[:type].to_s == "sqlite3"
231
+ sql << " DEFAULT VALUES"
232
+ else
233
+ raise "Unknown database-type: '#{@opts[:type]}'."
234
+ end
235
+ else
236
+ sql << " ("
237
+
238
+ first = true
239
+ arr_insert.each do |key, value|
240
+ if first
241
+ first = false
242
+ else
243
+ sql << ", "
244
+ end
245
+
246
+ sql << "#{@sep_col}#{key}#{@sep_col}"
247
+ end
248
+
249
+ sql << ") VALUES ("
250
+
251
+ first = true
252
+ arr_insert.each do |key, value|
253
+ if first
254
+ first = false
255
+ else
256
+ sql << ", "
257
+ end
258
+
259
+ sql << self.sqlval(value)
260
+ end
261
+
262
+ sql << ")"
263
+ end
264
+
265
+ return sql if args and args[:return_sql]
266
+
267
+ self.conn_exec do |driver|
268
+ driver.query(sql)
269
+ return driver.lastID if args and args[:return_id]
270
+ return nil
271
+ end
272
+ end
273
+
274
+ #Returns the correct SQL-value for the given value. If it is a number, then just the raw number as a string will be returned. nil's will be NULL and strings will have quotes and will be escaped.
275
+ def sqlval(val)
276
+ return @conn.sqlval(val) if @conn.respond_to?(:sqlval)
277
+
278
+ if val.is_a?(Fixnum) or val.is_a?(Integer)
279
+ return val.to_s
280
+ elsif val == nil
281
+ return "NULL"
282
+ elsif val.is_a?(Date)
283
+ return "#{@sep_val}#{Datet.in(val).dbstr(:time => false)}#{@sep_val}"
284
+ elsif val.is_a?(Time) or val.is_a?(DateTime)
285
+ return "#{@sep_val}#{Datet.in(val).dbstr}#{@sep_val}"
286
+ else
287
+ return "#{@sep_val}#{self.escape(val)}#{@sep_val}"
288
+ end
289
+ end
290
+
291
+ #Simply and optimal insert multiple rows into a table in a single query. Uses the drivers functionality if supported or inserts each row manually.
292
+ #
293
+ #===Examples
294
+ # db.insert_multi(:users, [
295
+ # {:name => "John", :lastname => "Doe"},
296
+ # {:name => "Kasper", :lastname => "Johansen"}
297
+ # ])
298
+ def insert_multi(tablename, arr_hashes, args = nil)
299
+ return false if arr_hashes.empty?
300
+
301
+ if @esc_driver.respond_to?(:insert_multi)
302
+ if args and args[:return_sql]
303
+ return [@esc_driver.insert_multi(tablename, arr_hashes, args)]
304
+ end
305
+
306
+ self.conn_exec do |driver|
307
+ return driver.insert_multi(tablename, arr_hashes, args)
308
+ end
309
+ else
310
+ ret = [] if args and (args[:return_id] or args[:return_sql])
311
+ arr_hashes.each do |hash|
312
+ if ret
313
+ ret << self.insert(tablename, hash, args)
314
+ else
315
+ self.insert(tablename, hash, args)
316
+ end
317
+ end
318
+
319
+ if ret
320
+ return ret
321
+ else
322
+ return nil
323
+ end
324
+ end
325
+ end
326
+
327
+ #Simple updates rows.
328
+ #
329
+ #===Examples
330
+ # db.update(:users, {:name => "John"}, {:lastname => "Doe"})
331
+ def update(tablename, arr_update, arr_terms = {}, args = nil)
332
+ raise "'arr_update' was not a hash." if !arr_update.is_a?(Hash)
333
+ return false if arr_update.empty?
334
+
335
+ sql = ""
336
+ sql << "UPDATE #{@sep_col}#{tablename}#{@sep_col} SET "
337
+
338
+ first = true
339
+ arr_update.each do |key, value|
340
+ if first
341
+ first = false
342
+ else
343
+ sql << ", "
344
+ end
345
+
346
+ #Convert dates to valid dbstr.
347
+ value = self.date_out(value) if value.is_a?(Datet) or value.is_a?(Time)
348
+
349
+ sql << "#{@sep_col}#{key}#{@sep_col} = "
350
+ sql << "#{@sep_val}#{@esc_driver.escape(value)}#{@sep_val}"
351
+ end
352
+
353
+ if arr_terms and arr_terms.length > 0
354
+ sql << " WHERE #{self.makeWhere(arr_terms)}"
355
+ end
356
+
357
+ return sql if args and args[:return_sql]
358
+
359
+ self.conn_exec do |driver|
360
+ driver.query(sql)
361
+ end
362
+ end
363
+
364
+ #Checks if a given selector exists. If it does, updates it to match data. If not inserts the row.
365
+ def upsert(table, selector, data)
366
+ row = self.select(table, selector, "limit" => 1).fetch
367
+
368
+ if row
369
+ self.update(table, data, selector)
370
+ else
371
+ self.insert(table, selector.merge(data))
372
+ end
373
+ end
374
+
375
+ #Makes a select from the given arguments: table-name, where-terms and other arguments as limits and orders. Also takes a block to avoid raping of memory.
376
+ def select(tablename, arr_terms = nil, args = nil, &block)
377
+ #Set up vars.
378
+ sql = ""
379
+ args_q = nil
380
+ select_sql = "*"
381
+
382
+ #Give 'cloned_ubuf' argument to 'q'-method.
383
+ if args and args[:cloned_ubuf]
384
+ args_q = {:cloned_ubuf => true}
385
+ end
386
+
387
+ #Set up IDQuery-stuff if that is given in arguments.
388
+ if args and args[:idquery]
389
+ if args[:idquery] == true
390
+ select_sql = "`id`"
391
+ col = :id
392
+ else
393
+ select_sql = "`#{self.esc_col(args[:idquery])}`"
394
+ col = args[:idquery]
395
+ end
396
+ end
397
+
398
+ sql = "SELECT #{select_sql} FROM #{@sep_table}#{tablename}#{@sep_table}"
399
+
400
+ if arr_terms != nil and !arr_terms.empty?
401
+ sql << " WHERE #{self.makeWhere(arr_terms)}"
402
+ end
403
+
404
+ if args != nil
405
+ if args["orderby"]
406
+ sql << " ORDER BY #{args["orderby"]}"
407
+ end
408
+
409
+ if args["limit"]
410
+ sql << " LIMIT #{args["limit"]}"
411
+ end
412
+
413
+ if args["limit_from"] and args["limit_to"]
414
+ raise "'limit_from' was not numeric: '#{args["limit_from"]}'." if !(Float(args["limit_from"]) rescue false)
415
+ raise "'limit_to' was not numeric: '#{args["limit_to"]}'." if !(Float(args["limit_to"]) rescue false)
416
+ sql << " LIMIT #{args["limit_from"]}, #{args["limit_to"]}"
417
+ end
418
+ end
419
+
420
+ #Do IDQuery if given in arguments.
421
+ if args and args[:idquery]
422
+ res = Baza::Idquery.new(:db => self, :table => tablename, :query => sql, :col => col, &block)
423
+ else
424
+ res = self.q(sql, args_q, &block)
425
+ end
426
+
427
+ #Return result if a block wasnt given.
428
+ if block
429
+ return nil
430
+ else
431
+ return res
432
+ end
433
+ end
434
+
435
+ #Returns a single row from a database.
436
+ #
437
+ #===Examples
438
+ # row = db.single(:users, {:lastname => "Doe"})
439
+ def single(tablename, arr_terms = nil, args = {})
440
+ args["limit"] = 1
441
+
442
+ #Experienced very weird memory leak if this was not done by block. Maybe bug in Ruby 1.9.2? - knj
443
+ self.select(tablename, arr_terms, args) do |data|
444
+ return data
445
+ end
446
+
447
+ return false
448
+ end
449
+
450
+ alias :selectsingle :single
451
+
452
+ #Deletes rows from the database.
453
+ #
454
+ #===Examples
455
+ # db.delete(:users, {:lastname => "Doe"})
456
+ def delete(tablename, arr_terms, args = nil)
457
+ sql = "DELETE FROM #{@sep_table}#{tablename}#{@sep_table}"
458
+
459
+ if arr_terms != nil and !arr_terms.empty?
460
+ sql << " WHERE #{self.makeWhere(arr_terms)}"
461
+ end
462
+
463
+ return sql if args and args[:return_sql]
464
+
465
+ self.conn_exec do |driver|
466
+ driver.query(sql)
467
+ end
468
+
469
+ return nil
470
+ end
471
+
472
+ #Internally used to generate SQL.
473
+ #
474
+ #===Examples
475
+ # sql = db.makeWhere({:lastname => "Doe"}, driver_obj)
476
+ def makeWhere(arr_terms, driver = nil)
477
+ sql = ""
478
+
479
+ first = true
480
+ arr_terms.each do |key, value|
481
+ if first
482
+ first = false
483
+ else
484
+ sql << " AND "
485
+ end
486
+
487
+ if value.is_a?(Array)
488
+ raise "Array for column '#{key}' was empty." if value.empty?
489
+ sql << "#{@sep_col}#{key}#{@sep_col} IN (#{Knj::ArrayExt.join(:arr => value, :sep => ",", :surr => "'", :callback => proc{|ele| self.esc(ele)})})"
490
+ elsif value.is_a?(Hash)
491
+ raise "Dont know how to handle hash."
492
+ else
493
+ sql << "#{@sep_col}#{key}#{@sep_col} = #{@sep_val}#{@esc_driver.escape(value)}#{@sep_val}"
494
+ end
495
+ end
496
+
497
+ return sql
498
+ end
499
+
500
+ #Returns a driver-object based on the current thread and free driver-objects.
501
+ #
502
+ #===Examples
503
+ # db.conn_exec do |driver|
504
+ # str = driver.escape('something̈́')
505
+ # end
506
+ def conn_exec
507
+ if tcur = Thread.current and tcur[:knjdb]
508
+ tid = self.__id__
509
+
510
+ if tcur[:knjdb].key?(tid)
511
+ yield(tcur[:knjdb][tid])
512
+ return nil
513
+ end
514
+ end
515
+
516
+ if @conns
517
+ conn = @conns.get_and_lock
518
+
519
+ begin
520
+ yield(conn)
521
+ return nil
522
+ ensure
523
+ @conns.free(conn)
524
+ end
525
+ elsif @conn
526
+ @mutex.synchronize do
527
+ yield(@conn)
528
+ return nil
529
+ end
530
+ end
531
+
532
+ raise "Could not figure out how to find a driver to use?"
533
+ end
534
+
535
+ #Executes a query and returns the result.
536
+ #
537
+ #===Examples
538
+ # res = db.query('SELECT * FROM users')
539
+ # while data = res.fetch
540
+ # print data[:name]
541
+ # end
542
+ def query(string)
543
+ if @debug
544
+ print "SQL: #{string}\n"
545
+
546
+ if @debug.is_a?(Fixnum) and @debug >= 2
547
+ print caller.join("\n")
548
+ print "\n"
549
+ end
550
+ end
551
+
552
+ self.conn_exec do |driver|
553
+ return driver.query(string)
554
+ end
555
+ end
556
+
557
+ #Execute an ubuffered query and returns the result.
558
+ #
559
+ #===Examples
560
+ # db.query_ubuf('SELECT * FROM users') do |data|
561
+ # print data[:name]
562
+ # end
563
+ def query_ubuf(string, &block)
564
+ ret = nil
565
+
566
+ self.conn_exec do |driver|
567
+ ret = driver.query_ubuf(string, &block)
568
+ end
569
+
570
+ if block
571
+ ret.each(&block)
572
+ return nil
573
+ end
574
+
575
+ return ret
576
+ end
577
+
578
+ #Clones the connection, executes the given block and closes the connection again.
579
+ #
580
+ #===Examples
581
+ # db.cloned_conn do |conn|
582
+ # conn.q('SELCET * FROM users') do |data|
583
+ # print data[:name]
584
+ # end
585
+ # end
586
+ def cloned_conn(args = nil, &block)
587
+ clone_conn_args = {
588
+ :threadsafe => false
589
+ }
590
+
591
+ clone_conn_args.merge!(args[:clone_args]) if args and args[:clone_args]
592
+ dbconn = self.clone_conn(clone_conn_args)
593
+
594
+ begin
595
+ yield(dbconn)
596
+ ensure
597
+ dbconn.close
598
+ end
599
+ end
600
+
601
+ #Executes a query and returns the result. If a block is given the result is iterated over that block instead and it returns nil.
602
+ #
603
+ #===Examples
604
+ # db.q('SELECT * FROM users') do |data|
605
+ # print data[:name]
606
+ # end
607
+ def q(str, args = nil, &block)
608
+ #If the query should be executed in a new connection unbuffered.
609
+ if args
610
+ if args[:cloned_ubuf]
611
+ raise "No block given." if !block
612
+
613
+ self.cloned_conn(:clone_args => args[:clone_args]) do |cloned_conn|
614
+ ret = cloned_conn.query_ubuf(str)
615
+ ret.each(&block)
616
+ end
617
+
618
+ return nil
619
+ else
620
+ raise "Invalid arguments given: '#{args}'."
621
+ end
622
+ end
623
+
624
+ ret = self.query(str)
625
+
626
+ if block
627
+ ret.each(&block)
628
+ return nil
629
+ end
630
+
631
+ return ret
632
+ end
633
+
634
+ #Yields a query-buffer and flushes at the end of the block given.
635
+ def q_buffer(&block)
636
+ Baza::QueryBuffer.new(:db => self, &block)
637
+ return nil
638
+ end
639
+
640
+ #Returns the last inserted ID.
641
+ #
642
+ #===Examples
643
+ # id = db.last_id
644
+ def lastID
645
+ self.conn_exec do |driver|
646
+ return driver.lastID
647
+ end
648
+ end
649
+
650
+ alias :last_id :lastID
651
+
652
+ #Escapes a string to be safe-to-use in a query-string.
653
+ #
654
+ #===Examples
655
+ # db.q("INSERT INTO users (name) VALUES ('#{db.esc('John')}')")
656
+ def escape(string)
657
+ return @esc_driver.escape(string)
658
+ end
659
+
660
+ alias :esc :escape
661
+
662
+ #Escapes the given string to be used as a column.
663
+ def esc_col(str)
664
+ return @esc_driver.esc_col(str)
665
+ end
666
+
667
+ #Escapes the given string to be used as a table.
668
+ def esc_table(str)
669
+ return @esc_driver.esc_table(str)
670
+ end
671
+
672
+ #Returns a string which can be used in SQL with the current driver.
673
+ #===Examples
674
+ # str = db.date_out(Time.now) #=> "2012-05-20 22:06:09"
675
+ def date_out(date_obj = Datet.new, args = {})
676
+ if @esc_driver.respond_to?(:date_out)
677
+ return @esc_driver.date_out(date_obj, args)
678
+ end
679
+
680
+ return Datet.in(date_obj).dbstr(args)
681
+ end
682
+
683
+ #Takes a valid date-db-string and converts it into a Datet.
684
+ #===Examples
685
+ # db.date_in('2012-05-20 22:06:09') #=> 2012-05-20 22:06:09 +0200
686
+ def date_in(date_obj)
687
+ if @esc_driver.respond_to?(:date_in)
688
+ return @esc_driver.date_in(date_obj)
689
+ end
690
+
691
+ return Datet.in(date_obj)
692
+ end
693
+
694
+ #Returns the table-module and spawns it if it isnt already spawned.
695
+ def tables
696
+ if !@tables
697
+ require "#{File.dirname(__FILE__)}/drivers/#{@opts[:type]}/#{@opts[:type]}_tables" if (!@opts.key?(:require) or @opts[:require])
698
+ @tables = Baza::Driver.const_get(@type_cc).const_get(:Tables).new(
699
+ :db => self
700
+ )
701
+ end
702
+
703
+ return @tables
704
+ end
705
+
706
+ #Returns the columns-module and spawns it if it isnt already spawned.
707
+ def cols
708
+ if !@cols
709
+ require "#{File.dirname(__FILE__)}/drivers/#{@opts[:type]}/#{@opts[:type]}_columns" if (!@opts.key?(:require) or @opts[:require])
710
+ @cols = Baza::Driver.const_get(@type_cc).const_get(:Columns).new(
711
+ :db => self
712
+ )
713
+ end
714
+
715
+ return @cols
716
+ end
717
+
718
+ #Returns the index-module and spawns it if it isnt already spawned.
719
+ def indexes
720
+ if !@indexes
721
+ require "#{File.dirname(__FILE__)}/drivers/#{@opts[:type]}/#{@opts[:type]}_indexes" if (!@opts.key?(:require) or @opts[:require])
722
+ @indexes = Baza::Driver.const_get(@type_cc).const_get(:Indexes).new(
723
+ :db => self
724
+ )
725
+ end
726
+
727
+ return @indexes
728
+ end
729
+
730
+ #Returns the SQLSpec-module and spawns it if it isnt already spawned.
731
+ def sqlspecs
732
+ if !@sqlspecs
733
+ require "#{File.dirname(__FILE__)}/drivers/#{@opts[:type]}/#{@opts[:type]}_sqlspecs" if (!@opts.key?(:require) or @opts[:require])
734
+ @sqlspecs = Baza::Driver.const_get(@type_cc).const_get(:Sqlspecs).new(
735
+ :db => self
736
+ )
737
+ end
738
+
739
+ return @sqlspecs
740
+ end
741
+
742
+ #Beings a transaction and commits when the block ends.
743
+ #
744
+ #===Examples
745
+ # db.transaction do |db|
746
+ # db.insert(:users, {:name => "John"})
747
+ # db.insert(:users, {:name => "Kasper"})
748
+ # end
749
+ def transaction(&block)
750
+ self.conn_exec do |driver|
751
+ driver.transaction(&block)
752
+ end
753
+ end
754
+
755
+ #Optimizes all tables in the database.
756
+ def optimize(args = nil)
757
+ STDOUT.puts "Beginning optimization of database." if @debug or (args and args[:debug])
758
+ self.tables.list do |table|
759
+ STDOUT.puts "Optimizing table: '#{table.name}'." if @debug or (args and args[:debug])
760
+ table.optimize
761
+ end
762
+
763
+ return nil
764
+ end
765
+
766
+ #Proxies the method to the driver.
767
+ #
768
+ #===Examples
769
+ # db.method_on_driver
770
+ def method_missing(method_name, *args)
771
+ self.conn_exec do |driver|
772
+ if driver.respond_to?(method_name.to_sym)
773
+ return driver.send(method_name, *args)
774
+ end
775
+ end
776
+
777
+ raise "Method not found: #{method_name}"
778
+ end
779
+ end
780
+
781
+ #Subclass that contains all the drivers as further subclasses.
782
+ class Baza::Driver
783
+
784
+ end