audrey 0.2 → 0.3
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.
- checksums.yaml +4 -4
- data/lib/audrey/engine/sqlite3/init.rb +573 -0
- data/lib/audrey/engine/sqlite3/query/q0.rb +342 -0
- data/lib/audrey/engine/sqlite3.rb +1609 -0
- data/lib/audrey/query/q0.rb +373 -0
- data/lib/audrey.rb +1 -1
- metadata +5 -1
@@ -0,0 +1,1609 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
#===============================================================================
|
5
|
+
# Audrey::Engine::SQLite3
|
6
|
+
#
|
7
|
+
class Audrey::Engine::SQLite3 < Audrey::Engine
|
8
|
+
attr_reader :dbh
|
9
|
+
attr_reader :transactions
|
10
|
+
attr_reader :path
|
11
|
+
attr_reader :preps
|
12
|
+
attr_accessor :partition
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
|
16
|
+
#---------------------------------------------------------------------------
|
17
|
+
# initialize
|
18
|
+
#
|
19
|
+
def initialize(p_path, p_mode, opts={})
|
20
|
+
# default options
|
21
|
+
opts = {'immediate_commit'=>true}.merge(opts)
|
22
|
+
|
23
|
+
# set some properties
|
24
|
+
@path = p_path
|
25
|
+
@immediate_commit = opts['immediate_commit'] ? true : false
|
26
|
+
@preps = {}
|
27
|
+
@mode = p_mode
|
28
|
+
|
29
|
+
# KLUDGE: For now, default to partition 'm'
|
30
|
+
@partition = 'm'
|
31
|
+
|
32
|
+
# lock file
|
33
|
+
set_lock()
|
34
|
+
|
35
|
+
# if file exists
|
36
|
+
existed = File.exist?(@path)
|
37
|
+
|
38
|
+
# open database handle
|
39
|
+
@dbh = get_dbh()
|
40
|
+
|
41
|
+
# custom functions
|
42
|
+
custom_functions()
|
43
|
+
|
44
|
+
# create and structure database if the file doesn't exist
|
45
|
+
if not existed
|
46
|
+
require 'audrey/engine/sqlite3/init'
|
47
|
+
Audrey::Engine::SQLite3::Init.build @dbh
|
48
|
+
end
|
49
|
+
|
50
|
+
# transactions
|
51
|
+
@transactions = []
|
52
|
+
|
53
|
+
# initial transaction
|
54
|
+
if @immediate_commit
|
55
|
+
@transactions.push Audrey::Engine::SQLite3::Transaction::AutoCommit.new(self)
|
56
|
+
else
|
57
|
+
@transactions.push Audrey::Engine::SQLite3::Transaction::RC.new(self)
|
58
|
+
end
|
59
|
+
|
60
|
+
# create stuff just for this session
|
61
|
+
self.class.session_tables @dbh
|
62
|
+
end
|
63
|
+
#
|
64
|
+
# initialize
|
65
|
+
#---------------------------------------------------------------------------
|
66
|
+
|
67
|
+
|
68
|
+
#---------------------------------------------------------------------------
|
69
|
+
# get_dbh
|
70
|
+
#
|
71
|
+
def get_dbh
|
72
|
+
# get database handle
|
73
|
+
rv = ::SQLite3::Database.new(@path)
|
74
|
+
|
75
|
+
# enforce referential integrity
|
76
|
+
rv.execute 'pragma foreign_keys=on'
|
77
|
+
|
78
|
+
# results as hash
|
79
|
+
rv.results_as_hash = true
|
80
|
+
|
81
|
+
# return
|
82
|
+
return rv
|
83
|
+
end
|
84
|
+
#
|
85
|
+
# get_dbh
|
86
|
+
#---------------------------------------------------------------------------
|
87
|
+
|
88
|
+
|
89
|
+
#---------------------------------------------------------------------------
|
90
|
+
# commit, rollback
|
91
|
+
#
|
92
|
+
def commit
|
93
|
+
return @transactions[0].commit
|
94
|
+
end
|
95
|
+
|
96
|
+
def rollback
|
97
|
+
return @transactions[0].rollback
|
98
|
+
end
|
99
|
+
#
|
100
|
+
# commit, rollback
|
101
|
+
#---------------------------------------------------------------------------
|
102
|
+
|
103
|
+
|
104
|
+
#---------------------------------------------------------------------------
|
105
|
+
# in_transaction?
|
106
|
+
#
|
107
|
+
def in_transaction?
|
108
|
+
# loop through transactions
|
109
|
+
@transactions.each do |tr|
|
110
|
+
tr.is_a?(Audrey::Engine::SQLite3::Transaction::RC) and return true
|
111
|
+
end
|
112
|
+
|
113
|
+
# else not in transaction
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
#
|
117
|
+
# in_transaction?
|
118
|
+
#---------------------------------------------------------------------------
|
119
|
+
|
120
|
+
|
121
|
+
#---------------------------------------------------------------------------
|
122
|
+
# transaction
|
123
|
+
#
|
124
|
+
def transaction(opts={})
|
125
|
+
# $tm.hrm
|
126
|
+
tr = Audrey::Engine::SQLite3::Transaction::RC.new(self)
|
127
|
+
@transactions.push tr
|
128
|
+
|
129
|
+
# block
|
130
|
+
if block_given?
|
131
|
+
begin
|
132
|
+
# yield
|
133
|
+
yield tr
|
134
|
+
|
135
|
+
# autocommit if necessary
|
136
|
+
if opts['autocommit']
|
137
|
+
tr.commit
|
138
|
+
end
|
139
|
+
|
140
|
+
# exit transaction
|
141
|
+
rescue Audrey::Exception::TransactionExit => e
|
142
|
+
unless e.tr == tr
|
143
|
+
raise e
|
144
|
+
end
|
145
|
+
|
146
|
+
# return out of this method
|
147
|
+
return nil
|
148
|
+
ensure
|
149
|
+
tr.terminate
|
150
|
+
@transactions.pop
|
151
|
+
end
|
152
|
+
|
153
|
+
# return nil
|
154
|
+
return nil
|
155
|
+
|
156
|
+
# non-block
|
157
|
+
else
|
158
|
+
return tr
|
159
|
+
end
|
160
|
+
end
|
161
|
+
#
|
162
|
+
# transaction
|
163
|
+
#---------------------------------------------------------------------------
|
164
|
+
|
165
|
+
|
166
|
+
#---------------------------------------------------------------------------
|
167
|
+
# reset_savepoints
|
168
|
+
#
|
169
|
+
def reset_savepoints(ftr)
|
170
|
+
found = false
|
171
|
+
|
172
|
+
@transactions.each do |tr|
|
173
|
+
if tr == ftr
|
174
|
+
found = true
|
175
|
+
end
|
176
|
+
|
177
|
+
if found
|
178
|
+
tr.start
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
#
|
183
|
+
# reset_savepoints
|
184
|
+
#---------------------------------------------------------------------------
|
185
|
+
|
186
|
+
|
187
|
+
#---------------------------------------------------------------------------
|
188
|
+
# close
|
189
|
+
#
|
190
|
+
def close(opts={})
|
191
|
+
# $tm.hrm
|
192
|
+
|
193
|
+
# close prepared statements
|
194
|
+
@preps.keys.each do |key|
|
195
|
+
stmt = @preps[key]
|
196
|
+
|
197
|
+
if stmt and (not stmt.closed?)
|
198
|
+
stmt.close
|
199
|
+
end
|
200
|
+
|
201
|
+
@preps.delete key
|
202
|
+
end
|
203
|
+
|
204
|
+
# purge
|
205
|
+
if opts['purge']
|
206
|
+
purge()
|
207
|
+
end
|
208
|
+
|
209
|
+
# close database handle
|
210
|
+
if not @dbh.closed?
|
211
|
+
@dbh.close
|
212
|
+
end
|
213
|
+
|
214
|
+
# unlock file
|
215
|
+
if @lock
|
216
|
+
@lock.flock File::LOCK_UN
|
217
|
+
@lock.close
|
218
|
+
end
|
219
|
+
end
|
220
|
+
#
|
221
|
+
# close
|
222
|
+
#---------------------------------------------------------------------------
|
223
|
+
|
224
|
+
|
225
|
+
#---------------------------------------------------------------------------
|
226
|
+
# custom_functions
|
227
|
+
#
|
228
|
+
def custom_functions()
|
229
|
+
#-----------------------------------------------------------------------
|
230
|
+
# partition
|
231
|
+
#
|
232
|
+
@dbh.create_function('current_partition', 0) do |func|
|
233
|
+
func.result = @partition
|
234
|
+
end
|
235
|
+
#
|
236
|
+
# partition
|
237
|
+
#-----------------------------------------------------------------------
|
238
|
+
|
239
|
+
|
240
|
+
#-----------------------------------------------------------------------
|
241
|
+
# insert_object_journal
|
242
|
+
#
|
243
|
+
@dbh.create_function('insert_object_journal', 4 ) do |func, partition, root, fclass, scalar, xt|
|
244
|
+
# $tm.hrm
|
245
|
+
|
246
|
+
# translate scalars to objects for storage in JSON
|
247
|
+
if converter = Audrey::AUDREY_SCALAR_TO_RUBY[fclass]
|
248
|
+
scalar = converter.call(scalar)
|
249
|
+
end
|
250
|
+
|
251
|
+
# initialize
|
252
|
+
rv = {}
|
253
|
+
rv['fc'] = fclass
|
254
|
+
rv['pt'] = partition
|
255
|
+
|
256
|
+
# scalar
|
257
|
+
if rv['s'].is_a?(String)
|
258
|
+
rv['s'] = scalar.force_encoding('UTF-8')
|
259
|
+
end
|
260
|
+
|
261
|
+
# xt
|
262
|
+
if xt
|
263
|
+
rv['xt'] = xt
|
264
|
+
end
|
265
|
+
|
266
|
+
# root
|
267
|
+
if root == 1
|
268
|
+
rv['root'] = true
|
269
|
+
end
|
270
|
+
|
271
|
+
# set return result
|
272
|
+
func.result = JSON.generate(rv)
|
273
|
+
end
|
274
|
+
#
|
275
|
+
# insert_object_journal
|
276
|
+
#-----------------------------------------------------------------------
|
277
|
+
|
278
|
+
|
279
|
+
#-----------------------------------------------------------------------
|
280
|
+
# fclass_fco, fclass_isolate
|
281
|
+
#
|
282
|
+
@dbh.create_function('fclass_fco', 1) do |func, fclass|
|
283
|
+
func.result = Audrey::Util.fclass_fco(fclass) ? 1 : 0
|
284
|
+
end
|
285
|
+
|
286
|
+
@dbh.create_function('fclass_isolate', 1) do |func, fclass|
|
287
|
+
func.result = Audrey::Util.fclass_isolate(fclass) ? 1 : 0
|
288
|
+
end
|
289
|
+
#
|
290
|
+
# fclass_fco, fclass_isolate
|
291
|
+
#-----------------------------------------------------------------------
|
292
|
+
|
293
|
+
|
294
|
+
#-----------------------------------------------------------------------
|
295
|
+
# graph_field
|
296
|
+
#
|
297
|
+
@dbh.create_function('graph_field', 2) do |func, fclass, hkey|
|
298
|
+
func.result = Audrey::Util.graph_field?(fclass, hkey) ? 1 : 0
|
299
|
+
end
|
300
|
+
#
|
301
|
+
# graph_field
|
302
|
+
#-----------------------------------------------------------------------
|
303
|
+
|
304
|
+
|
305
|
+
#-----------------------------------------------------------------------
|
306
|
+
# change_to_true
|
307
|
+
#
|
308
|
+
# @dbh.create_function('change_to_true', 1) do |func, fclass|
|
309
|
+
# func.result = 1
|
310
|
+
# end
|
311
|
+
#
|
312
|
+
# change_to_true
|
313
|
+
#-----------------------------------------------------------------------
|
314
|
+
|
315
|
+
|
316
|
+
#-----------------------------------------------------------------------
|
317
|
+
# custom_class
|
318
|
+
#
|
319
|
+
@dbh.create_function('custom_class', 1) do |func, fclass|
|
320
|
+
func.result = Audrey::Util.custom_class?(fclass) ? 1 : 0
|
321
|
+
end
|
322
|
+
#
|
323
|
+
# custom_class
|
324
|
+
#-----------------------------------------------------------------------
|
325
|
+
|
326
|
+
|
327
|
+
#-----------------------------------------------------------------------
|
328
|
+
# debug
|
329
|
+
#
|
330
|
+
@dbh.create_function('debug', 1) do |func, msg|
|
331
|
+
puts msg
|
332
|
+
func.result = 1
|
333
|
+
end
|
334
|
+
#
|
335
|
+
# debug
|
336
|
+
#-----------------------------------------------------------------------
|
337
|
+
end
|
338
|
+
#
|
339
|
+
# custom_functions
|
340
|
+
#---------------------------------------------------------------------------
|
341
|
+
|
342
|
+
|
343
|
+
#---------------------------------------------------------------------------
|
344
|
+
# purge
|
345
|
+
#
|
346
|
+
def purge
|
347
|
+
# $tm.hrm
|
348
|
+
|
349
|
+
if @mode.write
|
350
|
+
sql = 'delete from objects where pk in (select pk from fco_not_traced)'
|
351
|
+
@dbh.execute sql
|
352
|
+
end
|
353
|
+
end
|
354
|
+
#
|
355
|
+
# purge
|
356
|
+
#---------------------------------------------------------------------------
|
357
|
+
|
358
|
+
|
359
|
+
#---------------------------------------------------------------------------
|
360
|
+
# root_pk
|
361
|
+
#
|
362
|
+
def root_pk
|
363
|
+
sql = 'select pk from objects where root and partition=:partition'
|
364
|
+
return @dbh.get_first_value(sql, 'partition'=>@partition)
|
365
|
+
end
|
366
|
+
#
|
367
|
+
# root_pk
|
368
|
+
#---------------------------------------------------------------------------
|
369
|
+
|
370
|
+
|
371
|
+
#---------------------------------------------------------------------------
|
372
|
+
# insert_object
|
373
|
+
#
|
374
|
+
def insert_object(object, opts={})
|
375
|
+
# $tm.hrm
|
376
|
+
|
377
|
+
# pk
|
378
|
+
pk = opts['pk'] || Audrey::Util.uuid
|
379
|
+
|
380
|
+
# if scalar
|
381
|
+
if dfn = Audrey::Engine::SQLite3::RUBY_SCALAR_TO_SQLITE[object.class]
|
382
|
+
if not @preps['insert_scalar']
|
383
|
+
sql = <<~'SQL'
|
384
|
+
insert into objects(pk, partition, bclass, fclass, scalar)
|
385
|
+
values(:pk, :partition, :bclass, :fclass, :scalar)
|
386
|
+
SQL
|
387
|
+
|
388
|
+
@preps['insert_scalar'] = @dbh.prepare(sql)
|
389
|
+
end
|
390
|
+
|
391
|
+
# insert record
|
392
|
+
simple_exec(
|
393
|
+
@preps['insert_scalar'],
|
394
|
+
'pk' => pk,
|
395
|
+
'partition' => @partition,
|
396
|
+
'bclass' => dfn['fclass'].bclass.to_s,
|
397
|
+
'fclass' => dfn['fclass'].to_s,
|
398
|
+
'scalar' => dfn['to_db'].call(object)
|
399
|
+
);
|
400
|
+
|
401
|
+
# if hash or array
|
402
|
+
elsif dfn = Audrey::RUBY_OBJECT_TO_AUDREY[object.class] and dfn['nclass']
|
403
|
+
simple_exec(
|
404
|
+
prepare_insert_collection(),
|
405
|
+
'pk' => pk,
|
406
|
+
'partition' => @partition,
|
407
|
+
'bclass' => dfn['fclass'].bclass.to_s,
|
408
|
+
'fclass' => dfn['fclass'].to_s
|
409
|
+
);
|
410
|
+
|
411
|
+
# if custom object
|
412
|
+
elsif object.is_a?(Audrey::Object::Custom)
|
413
|
+
simple_exec(
|
414
|
+
prepare_insert_collection(),
|
415
|
+
'pk' => pk,
|
416
|
+
'partition' => @partition,
|
417
|
+
'bclass' => object.class.bclass.to_s,
|
418
|
+
'fclass' => object.class.to_s,
|
419
|
+
);
|
420
|
+
|
421
|
+
# else unknown
|
422
|
+
else
|
423
|
+
raise 'unknown-object-class: ' + object.class.to_s
|
424
|
+
end
|
425
|
+
|
426
|
+
# return pk
|
427
|
+
return pk
|
428
|
+
end
|
429
|
+
#
|
430
|
+
# insert_object
|
431
|
+
#---------------------------------------------------------------------------
|
432
|
+
|
433
|
+
|
434
|
+
#---------------------------------------------------------------------------
|
435
|
+
# delete_object
|
436
|
+
#
|
437
|
+
def delete_object(pk)
|
438
|
+
# $tm.hrm
|
439
|
+
|
440
|
+
# ensure statement handle
|
441
|
+
if not @preps['delete_object']
|
442
|
+
sql = <<~'SQL'
|
443
|
+
delete from objects
|
444
|
+
where pk=:pk and partition=:partition
|
445
|
+
SQL
|
446
|
+
|
447
|
+
@preps['delete_object'] = @dbh.prepare(sql)
|
448
|
+
end
|
449
|
+
|
450
|
+
# execute
|
451
|
+
simple_exec @preps['delete_object'], 'pk' => pk, 'partition' => @partition
|
452
|
+
end
|
453
|
+
#
|
454
|
+
# delete_object
|
455
|
+
#---------------------------------------------------------------------------
|
456
|
+
|
457
|
+
|
458
|
+
#---------------------------------------------------------------------------
|
459
|
+
# add_xt
|
460
|
+
#
|
461
|
+
def add_xt(obj_pk, xt_pk)
|
462
|
+
# $tm.hrm
|
463
|
+
|
464
|
+
# prepare insert statement if necessary
|
465
|
+
if not @preps['insert_xt']
|
466
|
+
sql = <<~SQL
|
467
|
+
insert into objects(pk, partition, bclass, fclass)
|
468
|
+
values(?, (select partition from objects where pk=?), 'Audrey::Object::Hash', 'xt')
|
469
|
+
SQL
|
470
|
+
|
471
|
+
@preps['insert_xt'] ||= @dbh.prepare(sql)
|
472
|
+
end
|
473
|
+
|
474
|
+
# create xt
|
475
|
+
simple_exec @preps['insert_xt'], xt_pk, obj_pk
|
476
|
+
|
477
|
+
# associate record
|
478
|
+
@preps['add_xt'] ||= @dbh.prepare('update objects set xt=:xtpk where pk=:pk and partition=:partition')
|
479
|
+
simple_exec @preps['add_xt'], 'xtpk'=>xt_pk, 'pk'=>obj_pk, 'partition'=>@partition
|
480
|
+
end
|
481
|
+
#
|
482
|
+
# add_xt
|
483
|
+
#---------------------------------------------------------------------------
|
484
|
+
|
485
|
+
|
486
|
+
#---------------------------------------------------------------------------
|
487
|
+
# get_xt
|
488
|
+
#
|
489
|
+
def get_xt(obj_pk)
|
490
|
+
# prepare statement if necessary
|
491
|
+
@preps['existing_xt'] ||= @dbh.prepare('select xt from objects where pk=:pk and partition=:partition')
|
492
|
+
|
493
|
+
# search
|
494
|
+
@preps['existing_xt'].execute('pk'=>obj_pk, 'partition'=>@partition).each do |row|
|
495
|
+
return row['xt']
|
496
|
+
end
|
497
|
+
|
498
|
+
# create
|
499
|
+
xt_pk = Audrey::Util.uuid
|
500
|
+
add_xt obj_pk, xt_pk
|
501
|
+
|
502
|
+
# return
|
503
|
+
return xt_pk
|
504
|
+
ensure
|
505
|
+
@preps['existing_xt'] and @preps['existing_xt'].reset!
|
506
|
+
end
|
507
|
+
#
|
508
|
+
# get_xt
|
509
|
+
#---------------------------------------------------------------------------
|
510
|
+
|
511
|
+
|
512
|
+
#---------------------------------------------------------------------------
|
513
|
+
# add_relationship
|
514
|
+
#
|
515
|
+
def add_relationship(parent_pk, child_pk, opts={})
|
516
|
+
# ensure prepared statement
|
517
|
+
if not @preps['add_relationship']
|
518
|
+
sql = <<~SQL
|
519
|
+
insert into relationships(pk, parent, child, ord, hkey)
|
520
|
+
values(
|
521
|
+
:pk,
|
522
|
+
:parent,
|
523
|
+
:child,
|
524
|
+
|
525
|
+
case
|
526
|
+
when :ord is null then
|
527
|
+
coalesce((select max(ord) from relationships where parent=parent), 0) + 1
|
528
|
+
else :ord
|
529
|
+
end,
|
530
|
+
:hkey)
|
531
|
+
SQL
|
532
|
+
|
533
|
+
@preps['add_relationship'] = @dbh.prepare(sql)
|
534
|
+
end
|
535
|
+
|
536
|
+
# uuid for new relationship record
|
537
|
+
pk = Audrey::Util.uuid
|
538
|
+
|
539
|
+
# execute
|
540
|
+
begin
|
541
|
+
@preps['add_relationship'].execute(
|
542
|
+
sql,
|
543
|
+
'pk'=>pk,
|
544
|
+
'parent'=>parent_pk,
|
545
|
+
'child'=>child_pk,
|
546
|
+
'ord'=>opts['ord'],
|
547
|
+
'hkey'=>opts['hkey']
|
548
|
+
);
|
549
|
+
ensure
|
550
|
+
@preps['add_relationship'].reset!
|
551
|
+
end
|
552
|
+
|
553
|
+
# return
|
554
|
+
return pk
|
555
|
+
end
|
556
|
+
#
|
557
|
+
# add_relationship
|
558
|
+
#---------------------------------------------------------------------------
|
559
|
+
|
560
|
+
|
561
|
+
#---------------------------------------------------------------------------
|
562
|
+
# hash_has_key?
|
563
|
+
#
|
564
|
+
def hash_has_key?(parent_pk, key)
|
565
|
+
# $tm.hrm
|
566
|
+
# puts "parent_pk: #{parent_pk}"
|
567
|
+
# puts "key: #{key}"
|
568
|
+
|
569
|
+
# prepare
|
570
|
+
if not @preps['hash_has_key']
|
571
|
+
sql = <<~'SQL'
|
572
|
+
select count(*) as count
|
573
|
+
from relationships
|
574
|
+
where parent=:parent and hkey=:hkey
|
575
|
+
SQL
|
576
|
+
|
577
|
+
@preps['hash_has_key'] ||= @dbh.prepare(sql)
|
578
|
+
end
|
579
|
+
|
580
|
+
# get count
|
581
|
+
@preps['hash_has_key'].execute('parent'=>parent_pk, 'hkey'=>key).each_hash do |row|
|
582
|
+
return row['count'] > 0
|
583
|
+
end
|
584
|
+
ensure
|
585
|
+
@preps['hash_has_key'] and @preps['hash_has_key'].reset!
|
586
|
+
end
|
587
|
+
#
|
588
|
+
# hash_has_key?
|
589
|
+
#---------------------------------------------------------------------------
|
590
|
+
|
591
|
+
|
592
|
+
|
593
|
+
#---------------------------------------------------------------------------
|
594
|
+
# row_hash_by_key
|
595
|
+
#
|
596
|
+
def row_hash_by_key(parent_pk, hkey)
|
597
|
+
# $tm.hrm
|
598
|
+
|
599
|
+
# ensure statement handle
|
600
|
+
if not @preps['row_hash_by_key']
|
601
|
+
sql = <<~SQL
|
602
|
+
select * from objects
|
603
|
+
where pk =
|
604
|
+
(select child from relationships where parent=:parent and hkey=:hkey) and
|
605
|
+
partition=:partition
|
606
|
+
SQL
|
607
|
+
|
608
|
+
@preps['row_hash_by_key'] = @dbh.prepare(sql)
|
609
|
+
end
|
610
|
+
|
611
|
+
# params
|
612
|
+
params = {}
|
613
|
+
params['parent'] = parent_pk
|
614
|
+
params['hkey'] = hkey
|
615
|
+
params['partition'] = @partition
|
616
|
+
|
617
|
+
# get record
|
618
|
+
@preps['row_hash_by_key'].execute(params).each_hash do |row|
|
619
|
+
return self.class.set_row_scalar(row.to_h)
|
620
|
+
end
|
621
|
+
|
622
|
+
ensure
|
623
|
+
@preps['row_hash_by_key'] and @preps['row_hash_by_key'].reset!
|
624
|
+
end
|
625
|
+
#
|
626
|
+
# row_hash_by_key
|
627
|
+
#---------------------------------------------------------------------------
|
628
|
+
|
629
|
+
|
630
|
+
|
631
|
+
#---------------------------------------------------------------------------
|
632
|
+
# hash_element_delete
|
633
|
+
#
|
634
|
+
def hash_element_delete(parent_pk, key)
|
635
|
+
# $tm.hrm
|
636
|
+
|
637
|
+
# get record
|
638
|
+
record = row_hash_by_key(parent_pk, key)
|
639
|
+
|
640
|
+
# execute
|
641
|
+
if record
|
642
|
+
if not @preps['delete_hash_key']
|
643
|
+
sql = <<~'SQL'
|
644
|
+
delete from relationships
|
645
|
+
where parent=:parent and hkey=:hkey
|
646
|
+
SQL
|
647
|
+
|
648
|
+
@preps['delete_hash_key'] = @dbh.prepare(sql)
|
649
|
+
end
|
650
|
+
|
651
|
+
simple_exec @preps['delete_hash_key'], 'parent'=>parent_pk, 'hkey'=>key
|
652
|
+
end
|
653
|
+
|
654
|
+
# return
|
655
|
+
return record
|
656
|
+
end
|
657
|
+
#
|
658
|
+
# hash_element_delete
|
659
|
+
#---------------------------------------------------------------------------
|
660
|
+
|
661
|
+
|
662
|
+
#---------------------------------------------------------------------------
|
663
|
+
# row_by_pk
|
664
|
+
#
|
665
|
+
def row_by_pk(pk)
|
666
|
+
# $tm.hrm
|
667
|
+
|
668
|
+
# ensure prepared statement
|
669
|
+
@preps['row_by_pk'] ||= @dbh.prepare('select * from objects where pk=:pk and partition=:partition')
|
670
|
+
|
671
|
+
# get record
|
672
|
+
@preps['row_by_pk'].execute('pk'=>pk, 'partition'=>@partition).each_hash do |row|
|
673
|
+
return self.class.set_row_scalar(row.to_h)
|
674
|
+
end
|
675
|
+
|
676
|
+
# didn't find the object, so return nil
|
677
|
+
return nil
|
678
|
+
|
679
|
+
ensure
|
680
|
+
@preps['row_by_pk'] and @preps['row_by_pk'].reset!
|
681
|
+
end
|
682
|
+
#
|
683
|
+
# row_by_pk
|
684
|
+
#---------------------------------------------------------------------------
|
685
|
+
|
686
|
+
|
687
|
+
#---------------------------------------------------------------------------
|
688
|
+
# row_by_array_index
|
689
|
+
#
|
690
|
+
def row_by_array_index(parent_pk, idx)
|
691
|
+
# $tm.hrm
|
692
|
+
|
693
|
+
# ensure statement handle
|
694
|
+
if not @preps['row_by_array_index']
|
695
|
+
sql = <<~SQL
|
696
|
+
select r.ord as ord, o.*
|
697
|
+
from relationships r, objects o
|
698
|
+
where r.parent=:parent and o.pk=r.child
|
699
|
+
order by r.ord
|
700
|
+
limit 1
|
701
|
+
offset :offset
|
702
|
+
SQL
|
703
|
+
|
704
|
+
@preps['row_by_array_index'] = @dbh.prepare(sql)
|
705
|
+
end
|
706
|
+
|
707
|
+
# get record
|
708
|
+
@preps['row_by_array_index'].execute('parent'=>parent_pk, 'offset'=>idx).each_hash do |row|
|
709
|
+
return self.class.set_row_scalar(row.to_h)
|
710
|
+
end
|
711
|
+
|
712
|
+
# else return nil
|
713
|
+
return nil
|
714
|
+
ensure
|
715
|
+
@preps['row_by_array_index'] and @preps['row_by_array_index'].reset!
|
716
|
+
end
|
717
|
+
#
|
718
|
+
# row_by_array_index
|
719
|
+
#---------------------------------------------------------------------------
|
720
|
+
|
721
|
+
|
722
|
+
#---------------------------------------------------------------------------
|
723
|
+
# hash_each
|
724
|
+
#
|
725
|
+
def hash_each(parent_pk)
|
726
|
+
# $tm.hrm
|
727
|
+
rv = nil
|
728
|
+
|
729
|
+
# loop through keys
|
730
|
+
hash_keys(parent_pk).each do |key|
|
731
|
+
if block_given?
|
732
|
+
yield key, row_hash_by_key(parent_pk, key)
|
733
|
+
else
|
734
|
+
rv ||= []
|
735
|
+
rv.push [key, row_hash_by_key(parent_pk, key)]
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
# return
|
740
|
+
return rv
|
741
|
+
end
|
742
|
+
#
|
743
|
+
# hash_each
|
744
|
+
#---------------------------------------------------------------------------
|
745
|
+
|
746
|
+
|
747
|
+
|
748
|
+
#---------------------------------------------------------------------------
|
749
|
+
# hash_keys
|
750
|
+
#
|
751
|
+
def hash_keys(parent_pk)
|
752
|
+
# $tm.hrm
|
753
|
+
rv = []
|
754
|
+
|
755
|
+
# prepare
|
756
|
+
@preps['hash_keys'] ||= @dbh.prepare('select hkey from relationships where parent=:parent order by ord')
|
757
|
+
|
758
|
+
# loop through records
|
759
|
+
@preps['hash_keys'].execute('parent'=>parent_pk).each_hash do |row|
|
760
|
+
rv.push row['hkey']
|
761
|
+
end
|
762
|
+
|
763
|
+
# return
|
764
|
+
return rv
|
765
|
+
|
766
|
+
ensure
|
767
|
+
@preps['hash_keys'] and @preps['hash_keys'].reset!
|
768
|
+
end
|
769
|
+
#
|
770
|
+
# hash_keys
|
771
|
+
#---------------------------------------------------------------------------
|
772
|
+
|
773
|
+
|
774
|
+
#---------------------------------------------------------------------------
|
775
|
+
# clear_collection
|
776
|
+
#
|
777
|
+
def clear_collection(parent_pk)
|
778
|
+
# $tm.hrm
|
779
|
+
|
780
|
+
# prepare
|
781
|
+
@preps['clear_collection'] ||= @dbh.prepare('delete from relationships where parent=:parent')
|
782
|
+
|
783
|
+
# execute
|
784
|
+
simple_exec @preps['clear_collection'], 'parent'=>parent_pk
|
785
|
+
end
|
786
|
+
#
|
787
|
+
# clear_collection
|
788
|
+
#---------------------------------------------------------------------------
|
789
|
+
|
790
|
+
|
791
|
+
#---------------------------------------------------------------------------
|
792
|
+
# changed_get
|
793
|
+
#
|
794
|
+
def changed_get(pk)
|
795
|
+
# $tm.hrm
|
796
|
+
|
797
|
+
# prepare
|
798
|
+
@preps['changed_get'] ||= @dbh.prepare('select changed from objects where pk=:pk')
|
799
|
+
|
800
|
+
# execute
|
801
|
+
@preps['changed_get'].execute('pk'=>pk).each_hash do |row|
|
802
|
+
return row['changed']
|
803
|
+
end
|
804
|
+
ensure
|
805
|
+
@preps['changed_get'] and @preps['changed_get'].reset!
|
806
|
+
end
|
807
|
+
#
|
808
|
+
# changed_get
|
809
|
+
#---------------------------------------------------------------------------
|
810
|
+
|
811
|
+
|
812
|
+
#---------------------------------------------------------------------------
|
813
|
+
# changed_set
|
814
|
+
#
|
815
|
+
def changed_set(pk, bool)
|
816
|
+
# $tm.hrm
|
817
|
+
|
818
|
+
# set to changed
|
819
|
+
if bool
|
820
|
+
@preps['changed_set_true'] ||= @dbh.prepare('update base_objects set changed=current_timestamp where pk=:pk')
|
821
|
+
simple_exec @preps['changed_set_true'], 'pk'=>pk
|
822
|
+
else
|
823
|
+
@preps['changed_set_false'] ||= @dbh.prepare('update base_objects set changed=null where pk=:pk')
|
824
|
+
simple_exec @preps['changed_set_false'], 'pk'=>pk
|
825
|
+
end
|
826
|
+
end
|
827
|
+
#
|
828
|
+
# changed_set
|
829
|
+
#---------------------------------------------------------------------------
|
830
|
+
|
831
|
+
|
832
|
+
#---------------------------------------------------------------------------
|
833
|
+
# collection_child_pks
|
834
|
+
#
|
835
|
+
def collection_child_pks(parent_pk)
|
836
|
+
# $tm.hrm
|
837
|
+
|
838
|
+
# prepare
|
839
|
+
if not @preps['collection_child_pks']
|
840
|
+
sql = <<~'SQL'
|
841
|
+
select child
|
842
|
+
from relationships
|
843
|
+
where parent=:parent
|
844
|
+
SQL
|
845
|
+
|
846
|
+
@preps['collection_child_pks'] ||= @dbh.prepare(sql)
|
847
|
+
end
|
848
|
+
|
849
|
+
# init
|
850
|
+
rv = []
|
851
|
+
|
852
|
+
# get count
|
853
|
+
@preps['collection_child_pks'].execute(parent_pk).each_hash do |row|
|
854
|
+
rv.push row['child']
|
855
|
+
end
|
856
|
+
|
857
|
+
# return
|
858
|
+
return rv
|
859
|
+
ensure
|
860
|
+
@preps['collection_child_pks'] and @preps['collection_child_pks'].reset!
|
861
|
+
end
|
862
|
+
#
|
863
|
+
# collection_child_pks
|
864
|
+
#---------------------------------------------------------------------------
|
865
|
+
|
866
|
+
|
867
|
+
#---------------------------------------------------------------------------
|
868
|
+
# collection_length
|
869
|
+
#
|
870
|
+
def collection_length(parent_pk)
|
871
|
+
# $tm.hrm
|
872
|
+
|
873
|
+
# prepare
|
874
|
+
if not @preps['collection_length']
|
875
|
+
sql = <<~'SQL'
|
876
|
+
select count(*) as count
|
877
|
+
from relationships
|
878
|
+
where parent=:parent
|
879
|
+
SQL
|
880
|
+
|
881
|
+
@preps['collection_length'] ||= @dbh.prepare(sql)
|
882
|
+
end
|
883
|
+
|
884
|
+
# get count
|
885
|
+
@preps['collection_length'].execute(parent_pk).each_hash do |row|
|
886
|
+
return row['count']
|
887
|
+
end
|
888
|
+
ensure
|
889
|
+
@preps['collection_length'] and @preps['collection_length'].reset!
|
890
|
+
end
|
891
|
+
#
|
892
|
+
# collection_length
|
893
|
+
#---------------------------------------------------------------------------
|
894
|
+
|
895
|
+
|
896
|
+
#---------------------------------------------------------------------------
|
897
|
+
# array_each
|
898
|
+
#
|
899
|
+
def array_each(parent_pk)
|
900
|
+
# $tm.hrm
|
901
|
+
|
902
|
+
# ensure statement handle
|
903
|
+
if not @preps['array_each']
|
904
|
+
sql = <<~SQL
|
905
|
+
select r.ord as ord, o.*
|
906
|
+
from relationships r, objects o
|
907
|
+
where r.parent=:parent and o.pk=r.child
|
908
|
+
order by r.ord
|
909
|
+
SQL
|
910
|
+
|
911
|
+
@preps['array_each'] = @dbh.prepare(sql)
|
912
|
+
end
|
913
|
+
|
914
|
+
# loop through records
|
915
|
+
@preps['array_each'].execute('parent'=>parent_pk).each_hash do |row|
|
916
|
+
yield row
|
917
|
+
end
|
918
|
+
ensure
|
919
|
+
@preps['array_each'] and @preps['array_each'].reset!
|
920
|
+
end
|
921
|
+
#
|
922
|
+
# array_each
|
923
|
+
#---------------------------------------------------------------------------
|
924
|
+
|
925
|
+
|
926
|
+
#---------------------------------------------------------------------------
|
927
|
+
# object_exists?
|
928
|
+
#
|
929
|
+
def object_exists?(pk)
|
930
|
+
# $tm.hrm
|
931
|
+
|
932
|
+
# ensure statement handle
|
933
|
+
if not @preps['object_exists']
|
934
|
+
sql = <<~'SQL'
|
935
|
+
select count(*) as count
|
936
|
+
from objects
|
937
|
+
where
|
938
|
+
pk=:pk and
|
939
|
+
partition=:partition
|
940
|
+
SQL
|
941
|
+
|
942
|
+
@preps['object_exists'] = @dbh.prepare(sql)
|
943
|
+
end
|
944
|
+
|
945
|
+
@preps['object_exists'].execute('pk'=>pk, 'partition'=>@partition).each do |row|
|
946
|
+
return row['count'] > 0
|
947
|
+
end
|
948
|
+
ensure
|
949
|
+
@preps['object_exists'] and @preps['object_exists'].reset!
|
950
|
+
end
|
951
|
+
#
|
952
|
+
# object_exists?
|
953
|
+
#---------------------------------------------------------------------------
|
954
|
+
|
955
|
+
|
956
|
+
#---------------------------------------------------------------------------
|
957
|
+
# ancestors
|
958
|
+
#
|
959
|
+
def ancestors(child_pk)
|
960
|
+
# sql to add parents
|
961
|
+
if not @preps['ancestors']
|
962
|
+
sql = <<~'SQL'
|
963
|
+
select *
|
964
|
+
from objects where pk in (
|
965
|
+
with recursive
|
966
|
+
ancestors(c) as (
|
967
|
+
values(:child_pk)
|
968
|
+
union
|
969
|
+
select parent from relationships, ancestors
|
970
|
+
where relationships.child=ancestors.c
|
971
|
+
)
|
972
|
+
select parent from relationships
|
973
|
+
where relationships.child in ancestors
|
974
|
+
);
|
975
|
+
SQL
|
976
|
+
|
977
|
+
@preps['ancestors'] = @dbh.prepare(sql)
|
978
|
+
end
|
979
|
+
|
980
|
+
# loop through results
|
981
|
+
@preps['ancestors'].execute('child_pk'=>child_pk).each do |row|
|
982
|
+
yield row
|
983
|
+
end
|
984
|
+
ensure
|
985
|
+
@preps['ancestors'] and @preps['ancestors'].reset!
|
986
|
+
end
|
987
|
+
#
|
988
|
+
# ancestors
|
989
|
+
#---------------------------------------------------------------------------
|
990
|
+
|
991
|
+
|
992
|
+
#---------------------------------------------------------------------------
|
993
|
+
# q0
|
994
|
+
#
|
995
|
+
def q0(p_fquery)
|
996
|
+
require 'audrey/engine/sqlite3/query/q0'
|
997
|
+
return Audrey::Engine::SQLite3::Query::Q0.new(self, p_fquery)
|
998
|
+
end
|
999
|
+
#
|
1000
|
+
# q0
|
1001
|
+
#---------------------------------------------------------------------------
|
1002
|
+
|
1003
|
+
|
1004
|
+
#---------------------------------------------------------------------------
|
1005
|
+
# import_csv
|
1006
|
+
#
|
1007
|
+
def import_csv(fclass, path, opts={})
|
1008
|
+
# $tm.hrm
|
1009
|
+
return Audrey::Engine::SQLite3::Importer::CSV.new(self, fclass, path, opts={})
|
1010
|
+
end
|
1011
|
+
#
|
1012
|
+
# import_csv
|
1013
|
+
#---------------------------------------------------------------------------
|
1014
|
+
|
1015
|
+
|
1016
|
+
#---------------------------------------------------------------------------
|
1017
|
+
# changed_objects
|
1018
|
+
#
|
1019
|
+
def changed_objects
|
1020
|
+
# $tm.hrm
|
1021
|
+
|
1022
|
+
# sql to add parents
|
1023
|
+
if not @preps['changed_objects']
|
1024
|
+
sql = <<~'SQL'
|
1025
|
+
select *
|
1026
|
+
from objects
|
1027
|
+
where
|
1028
|
+
changed and
|
1029
|
+
custom_class(fclass)
|
1030
|
+
order by
|
1031
|
+
changed
|
1032
|
+
SQL
|
1033
|
+
|
1034
|
+
@preps['changed_objects'] = @dbh.prepare(sql)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# loop through results
|
1038
|
+
@preps['changed_objects'].execute().each do |row|
|
1039
|
+
yield row
|
1040
|
+
end
|
1041
|
+
ensure
|
1042
|
+
@preps['changed_objects'] and @preps['changed_objects'].reset!
|
1043
|
+
end
|
1044
|
+
#
|
1045
|
+
# changed_objects
|
1046
|
+
#---------------------------------------------------------------------------
|
1047
|
+
|
1048
|
+
|
1049
|
+
# private
|
1050
|
+
private
|
1051
|
+
|
1052
|
+
|
1053
|
+
#---------------------------------------------------------------------------
|
1054
|
+
# simple_exec
|
1055
|
+
#
|
1056
|
+
def simple_exec(stmt, *opts)
|
1057
|
+
begin
|
1058
|
+
stmt.execute(*opts)
|
1059
|
+
ensure
|
1060
|
+
stmt.reset!
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
#
|
1064
|
+
# simple_exec
|
1065
|
+
#---------------------------------------------------------------------------
|
1066
|
+
|
1067
|
+
|
1068
|
+
#---------------------------------------------------------------------------
|
1069
|
+
# RUBY_SCALAR_TO_SQLITE
|
1070
|
+
#
|
1071
|
+
RUBY_SCALAR_TO_SQLITE = {}
|
1072
|
+
|
1073
|
+
RUBY_SCALAR_TO_SQLITE[::String] = {
|
1074
|
+
'fclass' => Audrey::Object::Scalar::String,
|
1075
|
+
'to_db' => Proc.new {|obj| obj}
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
RUBY_SCALAR_TO_SQLITE[::Integer] = {
|
1079
|
+
'fclass' => Audrey::Object::Scalar::Number,
|
1080
|
+
'to_db' => Proc.new {|obj| obj.to_s}
|
1081
|
+
}
|
1082
|
+
|
1083
|
+
RUBY_SCALAR_TO_SQLITE[::Float] = RUBY_SCALAR_TO_SQLITE[::Integer]
|
1084
|
+
|
1085
|
+
RUBY_SCALAR_TO_SQLITE[::TrueClass] = {
|
1086
|
+
'fclass' => Audrey::Object::Scalar::Boolean,
|
1087
|
+
'to_db' => Proc.new {|obj| obj ? 1 : 0}
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
RUBY_SCALAR_TO_SQLITE[::FalseClass] = RUBY_SCALAR_TO_SQLITE[::TrueClass]
|
1091
|
+
|
1092
|
+
RUBY_SCALAR_TO_SQLITE[::NilClass] = {
|
1093
|
+
'fclass' => Audrey::Object::Scalar::Null,
|
1094
|
+
'to_db' => Proc.new {|obj| nil}
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
RUBY_SCALAR_TO_SQLITE.freeze
|
1098
|
+
#
|
1099
|
+
# RUBY_SCALAR_TO_SQLITE
|
1100
|
+
#---------------------------------------------------------------------------
|
1101
|
+
|
1102
|
+
|
1103
|
+
#---------------------------------------------------------------------------
|
1104
|
+
# prepare_insert_collection
|
1105
|
+
#
|
1106
|
+
def prepare_insert_collection
|
1107
|
+
if not @preps['insert_collection']
|
1108
|
+
sql = <<~'SQL'
|
1109
|
+
insert into objects(pk, partition, bclass, fclass)
|
1110
|
+
values(:pk, :partition, :bclass, :fclass)
|
1111
|
+
SQL
|
1112
|
+
|
1113
|
+
@preps['insert_collection'] = @dbh.prepare(sql)
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
return @preps['insert_collection']
|
1117
|
+
end
|
1118
|
+
#
|
1119
|
+
# prepare_insert_collection
|
1120
|
+
#---------------------------------------------------------------------------
|
1121
|
+
|
1122
|
+
|
1123
|
+
#---------------------------------------------------------------------------
|
1124
|
+
# set_row_scalar
|
1125
|
+
#
|
1126
|
+
def self.set_row_scalar(row)
|
1127
|
+
# fix scalar if necessary
|
1128
|
+
if converter = Audrey::AUDREY_SCALAR_TO_RUBY[row['fclass']]
|
1129
|
+
row['scalar'] = converter.call(row['scalar'])
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# return
|
1133
|
+
return row
|
1134
|
+
end
|
1135
|
+
#
|
1136
|
+
# set_row_scalar
|
1137
|
+
#---------------------------------------------------------------------------
|
1138
|
+
|
1139
|
+
|
1140
|
+
#---------------------------------------------------------------------------
|
1141
|
+
# session_tables
|
1142
|
+
#
|
1143
|
+
def self.session_tables(dbh)
|
1144
|
+
# create temp tables
|
1145
|
+
# KLUDGE: query_rows references objects, but apparently you can't reference
|
1146
|
+
# from a temp table to a main table. For now just not enforcing referential
|
1147
|
+
# integrity.
|
1148
|
+
sql = <<~"SQL"
|
1149
|
+
create temp table connection(key varchar(5) primary key, val varchar(64));
|
1150
|
+
insert into connection(key, val) values('partition', null);
|
1151
|
+
|
1152
|
+
create temp table queries (pk varchar(5) primary key);
|
1153
|
+
create temp table subqueries (pk varchar(5) primary key, query not null references queries(pk));
|
1154
|
+
|
1155
|
+
create temp table query_rows (
|
1156
|
+
subquery varchar(5) not null references subqueries(pk) on delete cascade,
|
1157
|
+
parent varchar(36) not null, -- references objects(pk),
|
1158
|
+
unique(subquery, parent)
|
1159
|
+
);
|
1160
|
+
SQL
|
1161
|
+
|
1162
|
+
# execute
|
1163
|
+
dbh.execute_batch sql
|
1164
|
+
end
|
1165
|
+
#
|
1166
|
+
# session_tables
|
1167
|
+
#---------------------------------------------------------------------------
|
1168
|
+
|
1169
|
+
|
1170
|
+
#---------------------------------------------------------------------------
|
1171
|
+
# set_lock
|
1172
|
+
#
|
1173
|
+
def set_lock()
|
1174
|
+
# $tm.hrm
|
1175
|
+
|
1176
|
+
# open lock file
|
1177
|
+
@lock = File.open(@path + '.lock', File::RDWR|File::CREAT)
|
1178
|
+
|
1179
|
+
# exclusive
|
1180
|
+
if @mode.write
|
1181
|
+
@lock.flock File::LOCK_EX
|
1182
|
+
|
1183
|
+
# shared
|
1184
|
+
else
|
1185
|
+
@lock.flock File::LOCK_SH
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
#
|
1189
|
+
# set_lock
|
1190
|
+
#---------------------------------------------------------------------------
|
1191
|
+
end
|
1192
|
+
#
|
1193
|
+
# Audrey::Engine::SQLite3
|
1194
|
+
#===============================================================================
|
1195
|
+
|
1196
|
+
|
1197
|
+
#===============================================================================
|
1198
|
+
# Audrey::Engine::SQLite3::Importer
|
1199
|
+
#
|
1200
|
+
class Audrey::Engine::SQLite3::Importer
|
1201
|
+
end
|
1202
|
+
#
|
1203
|
+
# Audrey::Engine::SQLite3::Importer
|
1204
|
+
#===============================================================================
|
1205
|
+
|
1206
|
+
|
1207
|
+
#===============================================================================
|
1208
|
+
# Audrey::Engine::SQLite3::Importer::CSV
|
1209
|
+
#
|
1210
|
+
class Audrey::Engine::SQLite3::Importer::CSV < Audrey::Engine::SQLite3::Importer
|
1211
|
+
attr_reader :fields
|
1212
|
+
attr_accessor :verbose
|
1213
|
+
|
1214
|
+
#---------------------------------------------------------------------------
|
1215
|
+
# initialize
|
1216
|
+
#
|
1217
|
+
def initialize(engine, fclass, path, opts={})
|
1218
|
+
# $tm.hrm
|
1219
|
+
require 'csv'
|
1220
|
+
@engine = engine
|
1221
|
+
@dbh = @engine.dbh
|
1222
|
+
@fclass = fclass.to_s
|
1223
|
+
@path = path
|
1224
|
+
@partition = 'm'
|
1225
|
+
@fields = {}
|
1226
|
+
@verbose = false
|
1227
|
+
end
|
1228
|
+
#
|
1229
|
+
# initialize
|
1230
|
+
#---------------------------------------------------------------------------
|
1231
|
+
|
1232
|
+
|
1233
|
+
#---------------------------------------------------------------------------
|
1234
|
+
# run
|
1235
|
+
#
|
1236
|
+
def run
|
1237
|
+
# $tm.hrm
|
1238
|
+
converters = {}
|
1239
|
+
count = 0
|
1240
|
+
|
1241
|
+
# set fclass primary keys
|
1242
|
+
fclass_pks()
|
1243
|
+
|
1244
|
+
# prepare statements
|
1245
|
+
prepare_statements()
|
1246
|
+
|
1247
|
+
# build converters
|
1248
|
+
@fields.each do |k, dfn|
|
1249
|
+
converters[k] = Audrey::AUDREY_SCALAR_TO_RUBY[dfn.to_s]
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
# begin transaction
|
1253
|
+
@dbh.execute 'begin transaction'
|
1254
|
+
|
1255
|
+
# TESTING
|
1256
|
+
$tm.timer('import') do
|
1257
|
+
# loop through CSV
|
1258
|
+
::CSV.foreach(@path, headers: true) do |row|
|
1259
|
+
# verbosify
|
1260
|
+
if @verbose
|
1261
|
+
count += 1
|
1262
|
+
puts count.to_s + ': ' + row.to_s
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
# init collection ord
|
1266
|
+
ord = 0
|
1267
|
+
|
1268
|
+
# convert
|
1269
|
+
converters.each do |k, converter|
|
1270
|
+
if row.has_key?(k)
|
1271
|
+
row[k] = converter.call(row[k])
|
1272
|
+
end
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
# object pk
|
1276
|
+
obj_pk = Audrey::Util.uuid
|
1277
|
+
|
1278
|
+
# add hash object
|
1279
|
+
@insert_hash.execute(
|
1280
|
+
'pk'=>obj_pk,
|
1281
|
+
'partition'=>@partition,
|
1282
|
+
'bclass'=>@obj_bclass_pk,
|
1283
|
+
'fclass'=>@obj_fclass_pk
|
1284
|
+
);
|
1285
|
+
|
1286
|
+
# add scalars and relationships
|
1287
|
+
row.each do |k,v|
|
1288
|
+
ord += 1
|
1289
|
+
scalar_uuid = Audrey::Util.uuid
|
1290
|
+
|
1291
|
+
# add scalar object
|
1292
|
+
@insert_scalar.execute(
|
1293
|
+
'pk'=>scalar_uuid,
|
1294
|
+
'partition'=>@partition,
|
1295
|
+
'bclass'=>@scalar_bclass_pk,
|
1296
|
+
'fclass'=>@bclasses[v.class],
|
1297
|
+
'scalar'=>v
|
1298
|
+
);
|
1299
|
+
|
1300
|
+
# add relationship
|
1301
|
+
@insert_relationship.execute(
|
1302
|
+
'pk'=>Audrey::Util.uuid,
|
1303
|
+
'parent'=>obj_pk,
|
1304
|
+
'child'=>scalar_uuid,
|
1305
|
+
'hkey'=>k,
|
1306
|
+
'ord'=>ord
|
1307
|
+
);
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
# commit transaction
|
1313
|
+
$tm.timer('commit') do
|
1314
|
+
@dbh.execute 'commit'
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
# close prepared statements
|
1318
|
+
ensure
|
1319
|
+
$tm.puts 'close prepared statements'
|
1320
|
+
|
1321
|
+
$tm.timer('close prepared statements') do
|
1322
|
+
@insert_hash and @insert_hash.close
|
1323
|
+
@insert_scalar and @insert_scalar.close
|
1324
|
+
@insert_relationship and @insert_relationship.close
|
1325
|
+
end
|
1326
|
+
end
|
1327
|
+
#
|
1328
|
+
# run
|
1329
|
+
#---------------------------------------------------------------------------
|
1330
|
+
|
1331
|
+
|
1332
|
+
#---------------------------------------------------------------------------
|
1333
|
+
# prepare_statements
|
1334
|
+
#
|
1335
|
+
def prepare_statements
|
1336
|
+
# $tm.hrm
|
1337
|
+
|
1338
|
+
# hash
|
1339
|
+
sql = <<~'SQL'
|
1340
|
+
insert into
|
1341
|
+
base_objects(
|
1342
|
+
pk,
|
1343
|
+
partition,
|
1344
|
+
bclass_pk,
|
1345
|
+
fclass_pk
|
1346
|
+
)
|
1347
|
+
|
1348
|
+
values(
|
1349
|
+
:pk,
|
1350
|
+
:partition,
|
1351
|
+
:bclass,
|
1352
|
+
:fclass
|
1353
|
+
);
|
1354
|
+
SQL
|
1355
|
+
|
1356
|
+
# prepare
|
1357
|
+
@insert_hash = @dbh.prepare(sql)
|
1358
|
+
|
1359
|
+
# scalar
|
1360
|
+
sql = <<~'SQL'
|
1361
|
+
insert into
|
1362
|
+
base_objects(
|
1363
|
+
pk,
|
1364
|
+
partition,
|
1365
|
+
bclass_pk,
|
1366
|
+
fclass_pk,
|
1367
|
+
scalar
|
1368
|
+
)
|
1369
|
+
|
1370
|
+
values(
|
1371
|
+
:pk,
|
1372
|
+
:partition,
|
1373
|
+
:bclass,
|
1374
|
+
:fclass,
|
1375
|
+
:scalar
|
1376
|
+
);
|
1377
|
+
SQL
|
1378
|
+
|
1379
|
+
# prepare
|
1380
|
+
@insert_scalar = @dbh.prepare(sql)
|
1381
|
+
|
1382
|
+
# relationship
|
1383
|
+
sql = <<~'SQL'
|
1384
|
+
insert into relationships(
|
1385
|
+
pk,
|
1386
|
+
parent,
|
1387
|
+
child,
|
1388
|
+
hkey,
|
1389
|
+
ord
|
1390
|
+
)
|
1391
|
+
|
1392
|
+
values(
|
1393
|
+
:pk,
|
1394
|
+
:parent,
|
1395
|
+
:child,
|
1396
|
+
:hkey,
|
1397
|
+
:ord
|
1398
|
+
)
|
1399
|
+
SQL
|
1400
|
+
|
1401
|
+
# prepare
|
1402
|
+
@insert_relationship = @dbh.prepare(sql)
|
1403
|
+
end
|
1404
|
+
#
|
1405
|
+
# prepare_statements
|
1406
|
+
#---------------------------------------------------------------------------
|
1407
|
+
|
1408
|
+
|
1409
|
+
#---------------------------------------------------------------------------
|
1410
|
+
# fclass_pks
|
1411
|
+
#
|
1412
|
+
def fclass_pks
|
1413
|
+
@bclasses = {}
|
1414
|
+
|
1415
|
+
# object bclass primary key
|
1416
|
+
@obj_bclass_pk = set_fclass_pk('Audrey::Object::Hash')
|
1417
|
+
|
1418
|
+
# object fclass primary key
|
1419
|
+
@obj_fclass_pk = set_fclass_pk(@fclass)
|
1420
|
+
|
1421
|
+
# scalar base class
|
1422
|
+
@scalar_bclass_pk = set_fclass_pk('Audrey::Object::Scalar')
|
1423
|
+
|
1424
|
+
# loop through scalar classes
|
1425
|
+
[String, Integer, Float, NilClass].each do |clss|
|
1426
|
+
@bclasses[clss] = set_fclass_pk(Audrey::RUBY_OBJECT_TO_AUDREY[clss]['fclass'].to_s)
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
#
|
1430
|
+
# fclass_pks
|
1431
|
+
#---------------------------------------------------------------------------
|
1432
|
+
|
1433
|
+
|
1434
|
+
#---------------------------------------------------------------------------
|
1435
|
+
# set_fclass_pk
|
1436
|
+
#
|
1437
|
+
def set_fclass_pk(set_fclass)
|
1438
|
+
# $tm.hrm
|
1439
|
+
|
1440
|
+
# ensure fclass record
|
1441
|
+
sql = <<~'SQL'
|
1442
|
+
insert or ignore into
|
1443
|
+
fclasses(name)
|
1444
|
+
values(:fclass);
|
1445
|
+
SQL
|
1446
|
+
|
1447
|
+
# execute
|
1448
|
+
@dbh.execute sql, 'fclass'=>set_fclass
|
1449
|
+
|
1450
|
+
# get fclass pk
|
1451
|
+
sql = 'select pk from fclasses where name=:fclass'
|
1452
|
+
return @dbh.get_first_value(sql, 'fclass'=>set_fclass)
|
1453
|
+
end
|
1454
|
+
#
|
1455
|
+
# set_fclass_pk
|
1456
|
+
#---------------------------------------------------------------------------
|
1457
|
+
|
1458
|
+
|
1459
|
+
#---------------------------------------------------------------------------
|
1460
|
+
# set_bclass_pks
|
1461
|
+
#
|
1462
|
+
# def set_bclass_pks
|
1463
|
+
# $tm.hrm
|
1464
|
+
# end
|
1465
|
+
#
|
1466
|
+
# set_bclass_pks
|
1467
|
+
#---------------------------------------------------------------------------
|
1468
|
+
end
|
1469
|
+
#
|
1470
|
+
# Audrey::Engine::SQLite3::Importer::CSV
|
1471
|
+
#===============================================================================
|
1472
|
+
|
1473
|
+
|
1474
|
+
#===============================================================================
|
1475
|
+
# Audrey::Engine::SQLite3::Query
|
1476
|
+
#
|
1477
|
+
class Audrey::Engine::SQLite3::Query
|
1478
|
+
end
|
1479
|
+
#
|
1480
|
+
# Audrey::Engine::SQLite3::Query
|
1481
|
+
#===============================================================================
|
1482
|
+
|
1483
|
+
|
1484
|
+
#===============================================================================
|
1485
|
+
# Audrey::Engine::SQLite3::Transaction
|
1486
|
+
#
|
1487
|
+
class Audrey::Engine::SQLite3::Transaction < Audrey::Transaction
|
1488
|
+
#---------------------------------------------------------------------------
|
1489
|
+
# initialize
|
1490
|
+
#
|
1491
|
+
def initialize(p_engine)
|
1492
|
+
@engine = p_engine
|
1493
|
+
end
|
1494
|
+
#
|
1495
|
+
# initialize
|
1496
|
+
#---------------------------------------------------------------------------
|
1497
|
+
end
|
1498
|
+
#
|
1499
|
+
# Audrey::Engine::SQLite3::Transaction
|
1500
|
+
#===============================================================================
|
1501
|
+
|
1502
|
+
|
1503
|
+
#===============================================================================
|
1504
|
+
# Audrey::Engine::SQLite3::Transaction::AutoCommit
|
1505
|
+
#
|
1506
|
+
class Audrey::Engine::SQLite3::Transaction::AutoCommit < Audrey::Engine::SQLite3::Transaction
|
1507
|
+
def commit
|
1508
|
+
@engine.reset_savepoints self
|
1509
|
+
end
|
1510
|
+
|
1511
|
+
def start
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
def rollback
|
1515
|
+
end
|
1516
|
+
|
1517
|
+
def release
|
1518
|
+
end
|
1519
|
+
end
|
1520
|
+
#
|
1521
|
+
# Audrey::Engine::SQLite3::Transaction::AutoCommit
|
1522
|
+
#===============================================================================
|
1523
|
+
|
1524
|
+
|
1525
|
+
#===============================================================================
|
1526
|
+
# Audrey::Engine::SQLite3::Transaction::RC
|
1527
|
+
# An object of this class represents a transaction that can be rolled back or
|
1528
|
+
# committed.
|
1529
|
+
#
|
1530
|
+
class Audrey::Engine::SQLite3::Transaction::RC < Audrey::Engine::SQLite3::Transaction
|
1531
|
+
attr_reader :savepoint
|
1532
|
+
|
1533
|
+
#---------------------------------------------------------------------------
|
1534
|
+
# initialize
|
1535
|
+
#
|
1536
|
+
def initialize(*opts)
|
1537
|
+
super(*opts)
|
1538
|
+
start()
|
1539
|
+
end
|
1540
|
+
#
|
1541
|
+
# initialize
|
1542
|
+
#---------------------------------------------------------------------------
|
1543
|
+
|
1544
|
+
|
1545
|
+
#---------------------------------------------------------------------------
|
1546
|
+
# start
|
1547
|
+
#
|
1548
|
+
def start
|
1549
|
+
@savepoint = Audrey::Util.randstr()
|
1550
|
+
@engine.dbh.execute "savepoint #{@savepoint}"
|
1551
|
+
end
|
1552
|
+
#
|
1553
|
+
# start
|
1554
|
+
#---------------------------------------------------------------------------
|
1555
|
+
|
1556
|
+
|
1557
|
+
#---------------------------------------------------------------------------
|
1558
|
+
# commit
|
1559
|
+
#
|
1560
|
+
def commit
|
1561
|
+
release()
|
1562
|
+
@engine.reset_savepoints self
|
1563
|
+
end
|
1564
|
+
#
|
1565
|
+
# commit
|
1566
|
+
#---------------------------------------------------------------------------
|
1567
|
+
|
1568
|
+
|
1569
|
+
#---------------------------------------------------------------------------
|
1570
|
+
# rollback
|
1571
|
+
#
|
1572
|
+
def rollback
|
1573
|
+
@engine.dbh.execute "rollback to #{@savepoint}"
|
1574
|
+
end
|
1575
|
+
#
|
1576
|
+
# rollback
|
1577
|
+
#---------------------------------------------------------------------------
|
1578
|
+
|
1579
|
+
|
1580
|
+
#---------------------------------------------------------------------------
|
1581
|
+
# terminate
|
1582
|
+
#
|
1583
|
+
def terminate
|
1584
|
+
rollback()
|
1585
|
+
release()
|
1586
|
+
end
|
1587
|
+
#
|
1588
|
+
# terminate
|
1589
|
+
#---------------------------------------------------------------------------
|
1590
|
+
|
1591
|
+
|
1592
|
+
# private
|
1593
|
+
private
|
1594
|
+
|
1595
|
+
|
1596
|
+
#---------------------------------------------------------------------------
|
1597
|
+
# release
|
1598
|
+
#
|
1599
|
+
def release
|
1600
|
+
@engine.dbh.execute "release #{@savepoint}"
|
1601
|
+
@savepoint = nil
|
1602
|
+
end
|
1603
|
+
#
|
1604
|
+
# release
|
1605
|
+
#---------------------------------------------------------------------------
|
1606
|
+
end
|
1607
|
+
#
|
1608
|
+
# Audrey::Engine::SQLite3::Transaction::RC
|
1609
|
+
#===============================================================================
|