sequel 5.106.0 → 5.107.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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '06079c7289ee0e9b243cd4b320275936464aa0d02e09831285f622dff88da509'
|
|
4
|
+
data.tar.gz: 80de7cac3306f9b07b21bcd7885bde29da87d9b668488a96024e55002cebeb45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c42946502241b5d56f7d6c25f39bc7e80e348340283809977a83ba1c66f1372d963cf5aa272452faafd027aee87ea9b4446b9ea31554ac0ab826ccb2fde7528
|
|
7
|
+
data.tar.gz: 9a26c4935906dc87b40d3a4b46c45c78faf51a0f4ca322d113dd34d3495f68540c7fe581f66a5d349cd7aecfd06ead1ae0137031eb9a0b8e146f13d68af4b68c
|
|
@@ -256,6 +256,564 @@ module Sequel
|
|
|
256
256
|
end
|
|
257
257
|
end
|
|
258
258
|
|
|
259
|
+
module PropertyGraph
|
|
260
|
+
# Base class for all Generator DSL classes. This uses a design where
|
|
261
|
+
# The DSL class is only used for the evaluation of the block, and new
|
|
262
|
+
# returns a frozen struct.
|
|
263
|
+
class Generator
|
|
264
|
+
# Instead of returning the Generator instance, return a frozen struct
|
|
265
|
+
# with data from the generator. This prevents accidentally calling the
|
|
266
|
+
# generator methods, and makes it possible for the generator class and
|
|
267
|
+
# result class to use the same method name in two different ways, with
|
|
268
|
+
# the generator setting data and the frozen struct method returning it.
|
|
269
|
+
# The frozen struct classes use the constant Data under each generator
|
|
270
|
+
# subclass.
|
|
271
|
+
def self.new(*args, &block)
|
|
272
|
+
super(*args, &block).data
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Base class for Vertex and Edge.
|
|
276
|
+
class Element < self
|
|
277
|
+
Data = Struct.new(:name, :key, :labels)
|
|
278
|
+
|
|
279
|
+
# +name+ specifies the name of the vertex or edge. It can be an
|
|
280
|
+
# SQL::AliasedExpression to use an alias. Options:
|
|
281
|
+
# :properties :: Specifies fixed properties for the vertex or edge.
|
|
282
|
+
# If this is given, you cannot use the label method
|
|
283
|
+
# inside the block.
|
|
284
|
+
def initialize(name, opts=OPTS, &block)
|
|
285
|
+
@name = name
|
|
286
|
+
@labels = []
|
|
287
|
+
if opts.key?(:properties)
|
|
288
|
+
@labels << [nil, opts[:properties]].freeze
|
|
289
|
+
@labels.freeze
|
|
290
|
+
end
|
|
291
|
+
instance_exec(&block) if block
|
|
292
|
+
@labels.freeze
|
|
293
|
+
freeze
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def data
|
|
297
|
+
Data.new(@name, @key, @labels).freeze
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Set the column(s) to use for the KEY clause, which are the columns
|
|
301
|
+
# that uniquely identify rows in the table:
|
|
302
|
+
#
|
|
303
|
+
# key(:id)
|
|
304
|
+
# # KEY (id)
|
|
305
|
+
#
|
|
306
|
+
# key([:id1, :id2])
|
|
307
|
+
# # KEY (id1, id2)
|
|
308
|
+
def key(columns)
|
|
309
|
+
@key = Array(columns)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Add a label and properties for the label for this vertex/edge.
|
|
313
|
+
# A vertex or edge can have multiple labels with separate properties,
|
|
314
|
+
# if it wasn't created with fixed properties. The +name+ argument
|
|
315
|
+
# specifies the label name. The +properties+ argument specifies the
|
|
316
|
+
# properties:
|
|
317
|
+
# nil, :all :: PROPERTIES ALL COLUMNS
|
|
318
|
+
# false, :none, [] :: NO PROPERTIES
|
|
319
|
+
# Array :: Array of specific properties. Each element should be a Symbol,
|
|
320
|
+
# SQL::Identifier, or SQL::AliasedExpression.
|
|
321
|
+
#
|
|
322
|
+
# label(:label_name)
|
|
323
|
+
# # LABEL label_name PROPERTIES ALL COLUMNS
|
|
324
|
+
#
|
|
325
|
+
# label(:label_name, [])
|
|
326
|
+
# # LABEL label_name NO PROPERTIES
|
|
327
|
+
#
|
|
328
|
+
# label(:label_name, [:c, Sequel[:b].as(:d)], Sequel[:e])
|
|
329
|
+
# # LABEL label_name PROPERTIES (c, b AS d, e)
|
|
330
|
+
def label(name, properties=:all)
|
|
331
|
+
if @labels.frozen?
|
|
332
|
+
raise Error, "cannot specify label for property graph vertex or edge with fixed properties"
|
|
333
|
+
end
|
|
334
|
+
@labels << [name, properties].freeze
|
|
335
|
+
nil
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Vertex is used for the block passed to Create#vertex, used to configure
|
|
340
|
+
# vertices in the property graph. It doesn't have any additional behavior
|
|
341
|
+
# compared to the Element class, so this is an alias instead of a subclass.
|
|
342
|
+
Vertex = Element
|
|
343
|
+
|
|
344
|
+
# Target is used for the block passed to Edge#source and Edge#destination,
|
|
345
|
+
# used to configure the source and destination of property graph edges.
|
|
346
|
+
class Target < self
|
|
347
|
+
Data = Struct.new(:name, :key, :references)
|
|
348
|
+
|
|
349
|
+
# +name+ specifies the name of the source or destination.
|
|
350
|
+
def initialize(name, &block)
|
|
351
|
+
@name = name
|
|
352
|
+
@key = nil
|
|
353
|
+
@references = nil
|
|
354
|
+
instance_exec(&block) if block
|
|
355
|
+
freeze
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def data
|
|
359
|
+
Data.new(@name, @key, @references).freeze
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Set the column(s) to use for the KEY clause, which are the columns
|
|
363
|
+
# in the edge table that reference columns in the source or destination.
|
|
364
|
+
# Should be combined with #references to specify the columns being
|
|
365
|
+
# referenced.
|
|
366
|
+
#
|
|
367
|
+
# key(:vertex_id)
|
|
368
|
+
# # KEY (vertex_id)
|
|
369
|
+
#
|
|
370
|
+
# key([:vertex_id1, :vertex_id2])
|
|
371
|
+
# # KEY (vertex_id1, vertex_id2)
|
|
372
|
+
def key(keys)
|
|
373
|
+
@key = Array(keys)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Set the column(s) to use for the REFERENCES clause, which are the columns
|
|
377
|
+
# in the source or destination table that are referenced by the edge table.
|
|
378
|
+
# Should be combined with #key to specify the columns doing the referencing.
|
|
379
|
+
#
|
|
380
|
+
# references(:id)
|
|
381
|
+
# # REFERENCES (id)
|
|
382
|
+
#
|
|
383
|
+
# references([:id1, :id2])
|
|
384
|
+
# # REFERENCES (id1, id2)
|
|
385
|
+
def references(refs)
|
|
386
|
+
@references = Array(refs)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Edge is used for block passed to Create#edge, used to configure edges
|
|
391
|
+
# in the property graph.
|
|
392
|
+
class Edge < Element
|
|
393
|
+
Data = Struct.new(:name, :key, :labels, :source, :destination)
|
|
394
|
+
|
|
395
|
+
# In addition to inherited behavior, raises an error if a block
|
|
396
|
+
# is not passed or source or destination is not called in the block.
|
|
397
|
+
def initialize(name, opts=OPTS, &block)
|
|
398
|
+
super
|
|
399
|
+
|
|
400
|
+
unless @source && @destination
|
|
401
|
+
raise Error, "source and/or destination not defined for property graph edge"
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def data
|
|
406
|
+
Data.new(@name, @key, @labels, @source, @destination).freeze
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Specify the source for the edge, with block evaluted by Target.
|
|
410
|
+
def source(name, &block)
|
|
411
|
+
raise Error, "cannot specify multiple sources for a property graph edge" if @source
|
|
412
|
+
@source = Target.new(name, &block)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Specify the destination for the edge, with block evaluted by Target.
|
|
416
|
+
def destination(name, &block)
|
|
417
|
+
raise Error, "cannot specify multiple destinations for a property graph edge" if @destination
|
|
418
|
+
@destination = Target.new(name, &block)
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Create is used to evaluate the block given to DatabaseMethods#create_property_graph,
|
|
423
|
+
# used to specify the vertices and edges in the property graph.
|
|
424
|
+
class Create < self
|
|
425
|
+
Data = Struct.new(:vertices, :edges)
|
|
426
|
+
|
|
427
|
+
def initialize(&block)
|
|
428
|
+
@vertices = []
|
|
429
|
+
@edges = []
|
|
430
|
+
instance_exec(&block)
|
|
431
|
+
@vertices.freeze
|
|
432
|
+
@edges.freeze
|
|
433
|
+
freeze
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def data
|
|
437
|
+
Data.new(@vertices, @edges).freeze
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Adds a vertex to the property graph, with the block evaluted by Vertex.
|
|
441
|
+
def vertex(name, opts=OPTS, &block)
|
|
442
|
+
@vertices << Vertex.new(name, opts, &block)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Adds an edge to the property graph, with the block evaluted by Edge.
|
|
446
|
+
def edge(name, opts=OPTS, &block)
|
|
447
|
+
@edges << Edge.new(name, opts, &block)
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# AlterElement is used to evaluate the block passed to
|
|
452
|
+
# Alter#alter_vertex_table and Alter#alter_edge_table.
|
|
453
|
+
class AlterElement < self
|
|
454
|
+
# +kind+ is +:vertex+ or +:edge+. +name+ is the alias of the
|
|
455
|
+
# vertex or edge table to alter.
|
|
456
|
+
def initialize(kind, name, &block)
|
|
457
|
+
@kind = kind
|
|
458
|
+
@name = name
|
|
459
|
+
@labels = []
|
|
460
|
+
@operations = []
|
|
461
|
+
instance_exec(&block)
|
|
462
|
+
|
|
463
|
+
# All labels added via #add_label are combined into a single
|
|
464
|
+
# ADD LABEL operation, as PostgreSQL supports adding multiple
|
|
465
|
+
# labels in a single ALTER ... ADD LABEL statement.
|
|
466
|
+
unless @labels.empty?
|
|
467
|
+
@operations << {:op=>:add_label, :kind=>kind, :name=>name, :labels=>@labels.freeze}
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
@operations.each(&:freeze)
|
|
471
|
+
@operations.freeze
|
|
472
|
+
freeze
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
def data
|
|
476
|
+
@operations
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Add a label (and optional properties) to the vertex/edge table.
|
|
480
|
+
# Takes the same arguments as Element#label. Can be called multiple
|
|
481
|
+
# times to add multiple labels.
|
|
482
|
+
#
|
|
483
|
+
# add_label(:l)
|
|
484
|
+
# # ADD LABEL l PROPERTIES ALL COLUMNS
|
|
485
|
+
def add_label(name, properties=:all)
|
|
486
|
+
@labels << [name, properties].freeze
|
|
487
|
+
nil
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Remove a label from the vertex/edge table. Options:
|
|
491
|
+
# :cascade :: Use CASCADE to drop dependent objects.
|
|
492
|
+
#
|
|
493
|
+
# drop_label(:l)
|
|
494
|
+
# # DROP LABEL l
|
|
495
|
+
def drop_label(name, opts=OPTS)
|
|
496
|
+
@operations << {:op=>:drop_label, :kind=>@kind, :name=>@name, :label=>name, :cascade=>opts[:cascade]}
|
|
497
|
+
nil
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Add properties to an existing label on the vertex/edge table.
|
|
501
|
+
# +properties+ is an expression, or array of expressions, the same
|
|
502
|
+
# as the explicit array form of the +properties+ argument to
|
|
503
|
+
# Element#label.
|
|
504
|
+
#
|
|
505
|
+
# add_properties(:l, [:c1, Sequel[:c2].as(:c3)])
|
|
506
|
+
# # ALTER LABEL l ADD PROPERTIES (c1, c2 AS c3)
|
|
507
|
+
def add_properties(label, properties)
|
|
508
|
+
@operations << {:op=>:add_properties, :kind=>@kind, :name=>@name, :label=>label, :properties=>Array(properties)}
|
|
509
|
+
nil
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Remove properties from an existing label on the vertex/edge table.
|
|
513
|
+
# +properties+ is a column name, or array of column names. Options:
|
|
514
|
+
# :cascade :: Use CASCADE to drop dependent objects.
|
|
515
|
+
#
|
|
516
|
+
# drop_properties(:l, [:c1])
|
|
517
|
+
# # ALTER LABEL l DROP PROPERTIES (c1)
|
|
518
|
+
def drop_properties(label, properties, opts=OPTS)
|
|
519
|
+
@operations << {:op=>:drop_properties, :kind=>@kind, :name=>@name, :label=>label, :properties=>Array(properties), :cascade=>opts[:cascade]}
|
|
520
|
+
nil
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Alter is used to evaluate the block given to DatabaseMethods#alter_property_graph,
|
|
525
|
+
# used to specify changes to an existing property graph.
|
|
526
|
+
class Alter < self
|
|
527
|
+
def initialize(&block)
|
|
528
|
+
@operations = []
|
|
529
|
+
instance_exec(&block)
|
|
530
|
+
|
|
531
|
+
@operations.each do |op|
|
|
532
|
+
case op[:op]
|
|
533
|
+
when :add_vertex_tables, :add_edge_tables
|
|
534
|
+
op[:tables].freeze
|
|
535
|
+
end
|
|
536
|
+
op.freeze
|
|
537
|
+
end
|
|
538
|
+
@operations.freeze
|
|
539
|
+
freeze
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def data
|
|
543
|
+
@operations
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Add a vertex to the property graph, with the block used to configure the
|
|
547
|
+
# vertex.
|
|
548
|
+
#
|
|
549
|
+
# alter_property_graph.add_vertex(:v)
|
|
550
|
+
# # ADD VERTEX TABLES (v)
|
|
551
|
+
def add_vertex(name, opts=OPTS, &block)
|
|
552
|
+
add_tables_operation(:add_vertex_tables) << Vertex.new(name, opts, &block)
|
|
553
|
+
nil
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# Add an edge to the property graph, with the block used to configure the edge.
|
|
557
|
+
#
|
|
558
|
+
# alter_property_graph.add_edge(:e){source :v1; destination :v2}
|
|
559
|
+
# # ADD EDGE TABLES (e SOURCE v1 DESTINATION v2)
|
|
560
|
+
def add_edge(name, opts=OPTS, &block)
|
|
561
|
+
add_tables_operation(:add_edge_tables) << Edge.new(name, opts, &block)
|
|
562
|
+
nil
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Remove vertex tables (referenced by their aliases) from the
|
|
566
|
+
# property graph. +aliases+ can be a single alias or an array.
|
|
567
|
+
# Options:
|
|
568
|
+
# :cascade :: Use CASCADE instead of the default RESTRICT.
|
|
569
|
+
#
|
|
570
|
+
# alter_property_graph.drop_vertex_tables([:v1, :v2])
|
|
571
|
+
# # DROP VERTEX TABLES (v1, v2)
|
|
572
|
+
def drop_vertex_tables(aliases, opts=OPTS)
|
|
573
|
+
@operations << {:op=>:drop_vertex_tables, :aliases=>Array(aliases), :cascade=>opts[:cascade]}
|
|
574
|
+
nil
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
# Remove edge tables (referenced by their aliases) from the property
|
|
578
|
+
# graph. See #drop_vertex_tables.
|
|
579
|
+
#
|
|
580
|
+
# alter_property_graph.drop_edge_tables([:e1, :e2])
|
|
581
|
+
# # DROP EDGE TABLES (e1, e2)
|
|
582
|
+
def drop_edge_tables(aliases, opts=OPTS)
|
|
583
|
+
@operations << {:op=>:drop_edge_tables, :aliases=>Array(aliases), :cascade=>opts[:cascade]}
|
|
584
|
+
nil
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# Modify an existing vertex table (referenced by its alias).
|
|
588
|
+
#
|
|
589
|
+
# alter_property_graph.alter_vertex_table(:v){add_label :l}
|
|
590
|
+
# # ALTER VERTEX TABLE v ADD LABEL l PROPERTIES ALL COLUMNS
|
|
591
|
+
def alter_vertex_table(name, &block)
|
|
592
|
+
@operations.concat(AlterElement.new(:vertex, name, &block))
|
|
593
|
+
nil
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Modify an existing edge table (referenced by its alias).
|
|
597
|
+
#
|
|
598
|
+
# alter_property_graph.alter_edge_table(:e, properties: :none){drop_label :l}
|
|
599
|
+
# # ALTER VERTEX TABLE e DROP LABEL l
|
|
600
|
+
def alter_edge_table(name, &block)
|
|
601
|
+
@operations.concat(AlterElement.new(:edge, name, &block))
|
|
602
|
+
nil
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Change the owner of the property graph. +new_owner+ is usually a
|
|
606
|
+
# Symbol or SQL::Identifier for the role name, but can be
|
|
607
|
+
# <tt>Sequel.lit('CURRENT_USER')</tt> or
|
|
608
|
+
# <tt>Sequel.lit('SESSION_USER')</tt>.
|
|
609
|
+
#
|
|
610
|
+
# alter_property_graph.owner_to(:new_owner)
|
|
611
|
+
# # OWNER TO new_owner
|
|
612
|
+
def set_owner(new_owner)
|
|
613
|
+
@operations << {:op=>:set_owner, :owner=>new_owner}
|
|
614
|
+
nil
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
private
|
|
618
|
+
|
|
619
|
+
# Internals of add_vertex and add_edge.
|
|
620
|
+
def add_tables_operation(op_name)
|
|
621
|
+
unless op = @operations.find{|o| o[:op] == op_name}
|
|
622
|
+
@operations << (op = {:op=>op_name, :tables=>[]})
|
|
623
|
+
end
|
|
624
|
+
op[:tables]
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# Represents a GRAPH_TABLE expression, used to query a property graph
|
|
630
|
+
# via graph pattern matching. This is used in place of a table name
|
|
631
|
+
# expression or dataset in a SELECT query. These are created by calling
|
|
632
|
+
# #graph_table on the related Database object.
|
|
633
|
+
#
|
|
634
|
+
# Table uses a method chaining design, similar to Dataset, where methods
|
|
635
|
+
# return modified frozen copies of the object.
|
|
636
|
+
class Table
|
|
637
|
+
include SQL::AliasMethods
|
|
638
|
+
|
|
639
|
+
# Internal struct for a single element (vertex or edge) in the graph pattern:
|
|
640
|
+
# +type+ :: Either :vertex or :edge.
|
|
641
|
+
# +marker+ :: Connector string to use for the element (empty for initial vertex).
|
|
642
|
+
# +label+ :: Label restriction symbol or SQL::Identifier for the element, if any.
|
|
643
|
+
# Can be an array or set to match multiple labels.
|
|
644
|
+
# +var+ :: Graph pattern variable symbol for the element, if any.
|
|
645
|
+
# +where+ :: WHERE condition for the element, if any.
|
|
646
|
+
Element = Struct.new(:type, :marker, :label, :var, :where) do
|
|
647
|
+
# Method used to create elements, used instead of new
|
|
648
|
+
# to ensure that the returned elements are frozen.
|
|
649
|
+
def self.create(type, marker, label, opts)
|
|
650
|
+
case label
|
|
651
|
+
when Array, Set
|
|
652
|
+
label = label.dup.freeze unless label.frozen?
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
case where = opts[:where]
|
|
656
|
+
when Hash, Array
|
|
657
|
+
where = SQL::BooleanExpression.from_value_pairs(where)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
new(type, marker, label, opts[:var], where).freeze
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
private_class_method :new
|
|
664
|
+
end
|
|
665
|
+
private_constant :Element
|
|
666
|
+
|
|
667
|
+
# The name of the property graph the table is querying.
|
|
668
|
+
attr_reader :name
|
|
669
|
+
|
|
670
|
+
# A frozen array of Element instances, representing the vertices and
|
|
671
|
+
# edges in the graph pattern.
|
|
672
|
+
attr_reader :elements
|
|
673
|
+
|
|
674
|
+
# A frozen array of the columns used in the COLUMNS clause (aliased
|
|
675
|
+
# as columns_used, as #columns is used to modify the columns).
|
|
676
|
+
attr_reader :columns
|
|
677
|
+
alias columns_used columns
|
|
678
|
+
|
|
679
|
+
# Create a new Table with the given +graph_name+, with +initial_vertex_label+
|
|
680
|
+
# and +initial_vertex_opts+ being used to create the initial vertex.
|
|
681
|
+
# See Table#link for which options are supported for the initial vertex.
|
|
682
|
+
def self.create(graph_name, initial_vertex_label, initial_vertex_opts)
|
|
683
|
+
vertex = Element.create(:vertex, "", initial_vertex_label, initial_vertex_opts)
|
|
684
|
+
new(graph_name, [vertex].freeze, [].freeze)
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
def initialize(name, elements, columns)
|
|
688
|
+
@name = name
|
|
689
|
+
@elements = elements
|
|
690
|
+
@columns = columns
|
|
691
|
+
freeze
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
# Return a modified copy with an element added using a bidirectional link
|
|
695
|
+
# (<tt>-</tt> in the graph pattern).
|
|
696
|
+
# +label+ specifies the label restriction for the element. This can be
|
|
697
|
+
# nil for no label restriction, or an array or set to restrict to the
|
|
698
|
+
# given labels.
|
|
699
|
+
#
|
|
700
|
+
# Options supported:
|
|
701
|
+
# +:var+ :: Specifies a graph pattern variable name for the element,
|
|
702
|
+
# usable in the WHERE or COLUMNS clauses.
|
|
703
|
+
# +:vertex+ :: Specifies that the element being linked to is a vertex.
|
|
704
|
+
# This allows for direct vertex<->vertex linking, instead of
|
|
705
|
+
# the default vertex<->edge<->vertex linking.
|
|
706
|
+
# +:where+ :: An expression to use for the WHERE clause for the element.
|
|
707
|
+
#
|
|
708
|
+
# DB.graph_table(:gn, :v).link(:e)
|
|
709
|
+
# # GRAPH_TABLE (gn MATCH (IS v)-[IS e])
|
|
710
|
+
def link(label, opts=OPTS)
|
|
711
|
+
append_element('-', label, opts)
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Similar to #link, but uses a directed link from the previous element
|
|
715
|
+
# to the new element (<tt>-></tt> in the graph pattern). Accepts same
|
|
716
|
+
# arguments and options as #link.
|
|
717
|
+
#
|
|
718
|
+
# DB.graph_table(:gn, :v).to(:e)
|
|
719
|
+
# # GRAPH_TABLE (gn MATCH (IS v)->[IS e])
|
|
720
|
+
def to(label, opts=OPTS)
|
|
721
|
+
append_element('->', label, opts)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
# Similar to #link, but uses a directed link from the new element
|
|
725
|
+
# to the previous element (<tt><-</tt> in the graph pattern). Accepts
|
|
726
|
+
# same arguments and options as #link.
|
|
727
|
+
#
|
|
728
|
+
# DB.graph_table(:gn, :v).from(:e)
|
|
729
|
+
# # GRAPH_TABLE (gn MATCH (IS v)<-[IS e])
|
|
730
|
+
def from(label, opts=OPTS)
|
|
731
|
+
append_element('<-', label, opts)
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# Return a modifies copy that uses the given columns. A graph table
|
|
735
|
+
# must have a least one column set before it is used in a query.
|
|
736
|
+
#
|
|
737
|
+
# DB.graph_table(:gn, :v).columns(:a, Sequel[:b].as(:c))
|
|
738
|
+
# # GRAPH_TABLE (gn MATCH (IS v) COLUMNS (a, b AS c))
|
|
739
|
+
def columns(*cols)
|
|
740
|
+
self.class.new(@name, @elements, cols.freeze)
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# Return a modified copy that adds the given columns to the existing
|
|
744
|
+
# list of columns for the graph table.
|
|
745
|
+
def add_columns(*cols)
|
|
746
|
+
columns(*@columns, *cols)
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
# Append the SQL for the GRAPH_TABLE expression to the given SQL string.
|
|
750
|
+
# Requires graph table have at least one column set.
|
|
751
|
+
def sql_literal_append(ds, sql)
|
|
752
|
+
if @columns.empty?
|
|
753
|
+
raise Error, "cannot use graph_table in a query if it does not return any columns"
|
|
754
|
+
end
|
|
755
|
+
if @elements.last.type == :edge
|
|
756
|
+
raise Error, "cannot use graph_table in a query if the last element is an edge"
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
sql << "GRAPH_TABLE ("
|
|
760
|
+
ds.literal_append(sql, @name)
|
|
761
|
+
sql << " MATCH "
|
|
762
|
+
|
|
763
|
+
@elements.each do |element|
|
|
764
|
+
marker = element.marker
|
|
765
|
+
var = element.var
|
|
766
|
+
label = element.label
|
|
767
|
+
where = element.where
|
|
768
|
+
vertex = element.type == :vertex
|
|
769
|
+
|
|
770
|
+
sql << marker
|
|
771
|
+
sql << (vertex ? '(' : '[')
|
|
772
|
+
|
|
773
|
+
ds.literal_append(sql, var) if var
|
|
774
|
+
if label
|
|
775
|
+
sql << (var ? " IS " : "IS ")
|
|
776
|
+
if label.is_a?(Array)
|
|
777
|
+
label_sep = ""
|
|
778
|
+
label.each do |l|
|
|
779
|
+
sql << label_sep
|
|
780
|
+
label_sep = "|" if label_sep.empty?
|
|
781
|
+
ds.literal_append(sql, l)
|
|
782
|
+
end
|
|
783
|
+
else
|
|
784
|
+
ds.literal_append(sql, label)
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
if where
|
|
789
|
+
sql << ((var || label) ? " WHERE " : "WHERE ")
|
|
790
|
+
ds.literal_append(sql, where)
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
sql << (vertex ? ')' : ']')
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
sql << " COLUMNS "
|
|
797
|
+
ds.literal_append(sql, @columns)
|
|
798
|
+
sql << ")"
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
private
|
|
802
|
+
|
|
803
|
+
# Internals of #link, #to, and #from.
|
|
804
|
+
def append_element(marker, label, opts)
|
|
805
|
+
node_type = if opts[:vertex]
|
|
806
|
+
:vertex
|
|
807
|
+
else
|
|
808
|
+
@elements.last.type == :vertex ? :edge : :vertex
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
element = Element.create(node_type, marker, label, opts)
|
|
812
|
+
self.class.new(@name, (@elements.dup << element).freeze, @columns)
|
|
813
|
+
end
|
|
814
|
+
end
|
|
815
|
+
end
|
|
816
|
+
|
|
259
817
|
# Error raised when Sequel determines a PostgreSQL exclusion constraint has been violated.
|
|
260
818
|
class ExclusionConstraintViolation < Sequel::ConstraintViolation; end
|
|
261
819
|
|
|
@@ -338,6 +896,64 @@ module Sequel
|
|
|
338
896
|
add_conversion_proc(oid, block)
|
|
339
897
|
end
|
|
340
898
|
|
|
899
|
+
# Alter the property graph with the given +name+, supported on PostgreSQL 19+.
|
|
900
|
+
# The block uses a DSL, evaluated by PropertyGraph::Generator::Alter. Example:
|
|
901
|
+
#
|
|
902
|
+
# DB.alter_property_graph(:my_graph) do
|
|
903
|
+
# # PropertyGraph::Generator::Alter
|
|
904
|
+
# add_vertex :companies2
|
|
905
|
+
# # ALTER PROPERTY GRAPH "my_graph" ADD VERTEX TABLES ("companies2")
|
|
906
|
+
#
|
|
907
|
+
# add_edge :works_at2 do
|
|
908
|
+
# # PropertyGraph::Generator::Edge
|
|
909
|
+
# source :people
|
|
910
|
+
# destination :companies2
|
|
911
|
+
# end
|
|
912
|
+
# # ALTER PROPERTY GRAPH "my_graph" ADD EDGE TABLES
|
|
913
|
+
# # ("works_at2" SOURCE "people" DESTINATION "companies2")
|
|
914
|
+
#
|
|
915
|
+
# drop_vertex_tables [:p2], cascade: true
|
|
916
|
+
# # ALTER PROPERTY GRAPH "my_graph" DROP VERTEX TABLES ("p2") CASCADE
|
|
917
|
+
#
|
|
918
|
+
# drop_edge_tables :e2
|
|
919
|
+
# # ALTER PROPERTY GRAPH "my_graph" DROP EDGE TABLES ("e2")
|
|
920
|
+
#
|
|
921
|
+
# alter_vertex_table :companies do
|
|
922
|
+
# # PropertyGraph::Generator::AlterElement
|
|
923
|
+
# add_label :public_company, [:name, :symbol]
|
|
924
|
+
# # ALTER PROPERTY GRAPH "my_graph" ALTER VERTEX TABLE "companies"
|
|
925
|
+
# # ADD LABEL "public_company" PROPERTIES ("name", "symbol")
|
|
926
|
+
#
|
|
927
|
+
# drop_label :private_company
|
|
928
|
+
# # ALTER PROPERTY GRAPH "my_graph" ALTER VERTEX TABLE "companies"
|
|
929
|
+
# # DROP LABEL "private_company"
|
|
930
|
+
#
|
|
931
|
+
# add_properties :company, :revenue
|
|
932
|
+
# # ALTER PROPERTY GRAPH "my_graph" ALTER VERTEX TABLE "companies"
|
|
933
|
+
# # ALTER LABEL "company" ADD PROPERTIES ("revenue")
|
|
934
|
+
#
|
|
935
|
+
# drop_properties :company, :internal_id, cascade: true
|
|
936
|
+
# # ALTER PROPERTY GRAPH "my_graph" ALTER VERTEX TABLE "companies"
|
|
937
|
+
# # ALTER LABEL "company" DROP PROPERTIES ("internal_id") CASCADE
|
|
938
|
+
# end
|
|
939
|
+
#
|
|
940
|
+
# alter_edge_table :works_at do
|
|
941
|
+
# # PropertyGraph::Generator::AlterElement
|
|
942
|
+
# add_label :employment
|
|
943
|
+
# end
|
|
944
|
+
# # ALTER PROPERTY GRAPH "my_graph" ALTER EDGE TABLE "works_at"
|
|
945
|
+
# # ADD LABEL "employment" PROPERTIES ALL COLUMNS
|
|
946
|
+
#
|
|
947
|
+
# owner_to :new_owner
|
|
948
|
+
# # ALTER PROPERTY GRAPH "my_graph" OWNER TO "new_owner"
|
|
949
|
+
# end
|
|
950
|
+
def alter_property_graph(name, &block)
|
|
951
|
+
PropertyGraph::Generator::Alter.new(&block).each do |op|
|
|
952
|
+
execute_ddl(alter_property_graph_op_sql(name, op).freeze)
|
|
953
|
+
end
|
|
954
|
+
nil
|
|
955
|
+
end
|
|
956
|
+
|
|
341
957
|
def commit_prepared_transaction(transaction_id, opts=OPTS)
|
|
342
958
|
run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
|
|
343
959
|
end
|
|
@@ -467,6 +1083,67 @@ module Sequel
|
|
|
467
1083
|
self << create_language_sql(name, opts).freeze
|
|
468
1084
|
end
|
|
469
1085
|
|
|
1086
|
+
# Create a property graph in the database, supported on PostgreSQL 19+.
|
|
1087
|
+
#
|
|
1088
|
+
# Arguments:
|
|
1089
|
+
# name :: Name of the property graph
|
|
1090
|
+
# opts :: options hash:
|
|
1091
|
+
# :temp :: Create the property graph as a temporary property graph.
|
|
1092
|
+
#
|
|
1093
|
+
# The block uses a DSL, with classes under PropertyGraph::Generator:
|
|
1094
|
+
#
|
|
1095
|
+
# DB.create_property_graph(:my_graph) do
|
|
1096
|
+
# # PropertyGraph::Generator::Create
|
|
1097
|
+
# vertex :people
|
|
1098
|
+
#
|
|
1099
|
+
# vertex Sequel.as(:people, :p), properties: []
|
|
1100
|
+
#
|
|
1101
|
+
# vertex Sequel.as(:companies, :c) do
|
|
1102
|
+
# # PropertyGraph::Generator::Vertex
|
|
1103
|
+
# key :id
|
|
1104
|
+
# label :company
|
|
1105
|
+
# label :c, [:name, (Sequel[:revenue] / 1000).as(:revenue_thousands)]
|
|
1106
|
+
# end
|
|
1107
|
+
#
|
|
1108
|
+
# edge :works_at do
|
|
1109
|
+
# # PropertyGraph::Generator::Edge
|
|
1110
|
+
# source :people
|
|
1111
|
+
# destination :c
|
|
1112
|
+
# end
|
|
1113
|
+
#
|
|
1114
|
+
# edge Sequel.as(:employment, :e) do
|
|
1115
|
+
# source :people do
|
|
1116
|
+
# # PropertyGraph::Generator::Target
|
|
1117
|
+
# key :person_id
|
|
1118
|
+
# references :id
|
|
1119
|
+
# end
|
|
1120
|
+
# destination :c do
|
|
1121
|
+
# # PropertyGraph::Generator::Target
|
|
1122
|
+
# key :company_id
|
|
1123
|
+
# references :id
|
|
1124
|
+
# end
|
|
1125
|
+
# label :employment
|
|
1126
|
+
# end
|
|
1127
|
+
# end
|
|
1128
|
+
# # CREATE PROPERTY GRAPH "my_graph"
|
|
1129
|
+
# # VERTEX TABLES (
|
|
1130
|
+
# # "people",
|
|
1131
|
+
# # "people" AS "p" NO PROPERTIES,
|
|
1132
|
+
# # "companies" AS "c" KEY ("id")
|
|
1133
|
+
# # LABEL "company" PROPERTIES ALL COLUMNS
|
|
1134
|
+
# # LABEL "c" PROPERTIES ("name", ("revenue" / 1000) AS "revenue_thousands"))
|
|
1135
|
+
# # EDGE TABLES (
|
|
1136
|
+
# # "works_at"
|
|
1137
|
+
# # SOURCE "people"
|
|
1138
|
+
# # DESTINATION "c",
|
|
1139
|
+
# # "employment" AS "e"
|
|
1140
|
+
# # SOURCE KEY ("person_id") REFERENCES "people" ("id")
|
|
1141
|
+
# # DESTINATION KEY ("company_id") REFERENCES "c" ("id")
|
|
1142
|
+
# # LABEL "employment" PROPERTIES ALL COLUMNS)
|
|
1143
|
+
def create_property_graph(name, opts=OPTS, &block)
|
|
1144
|
+
execute_ddl(create_property_graph_sql(name, PropertyGraph::Generator::Create.new(&block), opts))
|
|
1145
|
+
end
|
|
1146
|
+
|
|
470
1147
|
# Create a schema in the database. Arguments:
|
|
471
1148
|
# name :: Name of the schema (e.g. admin)
|
|
472
1149
|
# opts :: options hash:
|
|
@@ -564,6 +1241,15 @@ module Sequel
|
|
|
564
1241
|
self << drop_language_sql(name, opts).freeze
|
|
565
1242
|
end
|
|
566
1243
|
|
|
1244
|
+
# Drops a property graph from the database. Arguments:
|
|
1245
|
+
# name :: name of the property graph to drop
|
|
1246
|
+
# opts :: options hash:
|
|
1247
|
+
# :cascade :: Drop other objects depending on this property_graph.
|
|
1248
|
+
# :if_exists :: Don't raise an error if the property graph doesn't exist.
|
|
1249
|
+
def drop_property_graph(name, opts=OPTS)
|
|
1250
|
+
self << drop_property_graph_sql(name, opts).freeze
|
|
1251
|
+
end
|
|
1252
|
+
|
|
567
1253
|
# Drops a schema from the database. Arguments:
|
|
568
1254
|
# name :: name of the schema to drop
|
|
569
1255
|
# opts :: options hash:
|
|
@@ -651,6 +1337,82 @@ module Sequel
|
|
|
651
1337
|
super
|
|
652
1338
|
end
|
|
653
1339
|
|
|
1340
|
+
# Return a PropertyGraph::Table instance for a property graph search
|
|
1341
|
+
# (a GRAPH_TABLE clause for a SELECT query). Supported on PostgreSQL 19+.
|
|
1342
|
+
#
|
|
1343
|
+
# Arguments:
|
|
1344
|
+
# +property_graph_name+ :: The property graph to query
|
|
1345
|
+
# +initial_vertex_label+ :: The label restriction for the initial vertex for the
|
|
1346
|
+
# graph pattern (can be nil for no label, or an array
|
|
1347
|
+
# or set for restricting to one of multiple labels).
|
|
1348
|
+
# +initial_vertex_opts+ :: The options for the initial vertex, see
|
|
1349
|
+
# PropertyGraph::Table#link for available options.
|
|
1350
|
+
#
|
|
1351
|
+
# The returned instance should be further modified by calling methods on it,
|
|
1352
|
+
# using a similar approach to how datasets work, where the methods return a
|
|
1353
|
+
# modified copy of the receiver. The available methods:
|
|
1354
|
+
#
|
|
1355
|
+
# link :: Add a bidirectional link to a new element (vertex or edge)
|
|
1356
|
+
# to :: Add a directional link from the last element to the new element
|
|
1357
|
+
# from :: Add a direciton link from the new element to last element
|
|
1358
|
+
# columns :: Replace the columns the graph table returns
|
|
1359
|
+
# add_columns :: Append to the columns the graph table returns.
|
|
1360
|
+
#
|
|
1361
|
+
# See PropertyGraph::Table for the details of these methods and the arguments
|
|
1362
|
+
# and options they support. Note that for a graph table to be usable in a query,
|
|
1363
|
+
# it must return at least one column, and the last element in the graph pattern
|
|
1364
|
+
# must be a vertex.
|
|
1365
|
+
#
|
|
1366
|
+
# gt = DB.graph_table(:pgn, :iv)
|
|
1367
|
+
# # Not yet usable, does not return any columns
|
|
1368
|
+
#
|
|
1369
|
+
# # Set columns for graph table
|
|
1370
|
+
# gt = gt.columns(:c, Sequel[1].as(:d))
|
|
1371
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv") COLUMNS ("c", 1 AS "d"))
|
|
1372
|
+
#
|
|
1373
|
+
# # Adds directional link to edge, since last (initial) element was a vertex
|
|
1374
|
+
# gt = gt.link(:e1)
|
|
1375
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"] COLUMNS ("c", 1 AS "d"))
|
|
1376
|
+
#
|
|
1377
|
+
# # Adds directional link from edge to vertex, since last element was an edge
|
|
1378
|
+
# gt = gt.to(:v2)
|
|
1379
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2") COLUMNS ("c", 1 AS "d"))
|
|
1380
|
+
#
|
|
1381
|
+
# # Adds bidirection link from vertex to vertex (overriding the default)
|
|
1382
|
+
# gt = gt.link(:v3, vertex: true)
|
|
1383
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2")-(IS "v3") COLUMNS ("c", 1 AS "d"))
|
|
1384
|
+
#
|
|
1385
|
+
# # Adds directional link from new edge to last vertex, since last element was an vertex.
|
|
1386
|
+
# # Sets graph pattern variable name and uses it in a WHERE clause for the added element.
|
|
1387
|
+
# gt = gt.from(:e2, var: :a2, where: {Sequel[:a2][:c] => 1})
|
|
1388
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2")-(IS "v3")
|
|
1389
|
+
# # <-["a2" IS "e2" WHERE ("a2"."c" = 1)] COLUMNS ("c", 1 AS "d"))
|
|
1390
|
+
#
|
|
1391
|
+
# # Can use nil as a label for no label restriction, both with and without a variable name
|
|
1392
|
+
# gt = gt.to(nil).to(nil, var: :a3)
|
|
1393
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2")-(IS "v3")
|
|
1394
|
+
# # <-["a2" IS "e2" WHERE ("a2"."c" = 1)]->[]->("a3") COLUMNS ("c", 1 AS "d"))
|
|
1395
|
+
#
|
|
1396
|
+
# # Can restrict to a one of a set of labels
|
|
1397
|
+
# gt = gt.from([:x, :y], var: :a6)
|
|
1398
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2")-(IS "v3")
|
|
1399
|
+
# # <-["a2" IS "e2" WHERE ("a2"."c" = 1)]->[]->("a3")->["a6" IS "x"|"y"] COLUMNS ("c", 1 AS "d"))
|
|
1400
|
+
#
|
|
1401
|
+
# # Add column(s) to the graph table
|
|
1402
|
+
# gt = gt.add_columns(:y)
|
|
1403
|
+
# # GRAPH_TABLE ("pgn" MATCH (IS "iv")-[IS "e1"]->(IS "v2")-(IS "v3")
|
|
1404
|
+
# # <-["a2" IS "e2" WHERE ("a2"."c" = 1)]->[]->("a3")->["a6" IS "x"|"y"]
|
|
1405
|
+
# # COLUMNS ("c", 1 AS "d", "y"))
|
|
1406
|
+
#
|
|
1407
|
+
# DB.from(gt)
|
|
1408
|
+
# # SELECT * FROM GRAPH_TABLE (...)
|
|
1409
|
+
#
|
|
1410
|
+
# DB.from(:x).cross_join(gt)
|
|
1411
|
+
# # SELECT * FROM "x" CROSS JOIN GRAPH_TABLE (...)
|
|
1412
|
+
def graph_table(property_graph_name, initial_vertex_label, initial_vertex_opts=OPTS)
|
|
1413
|
+
PropertyGraph::Table.create(property_graph_name, initial_vertex_label, initial_vertex_opts)
|
|
1414
|
+
end
|
|
1415
|
+
|
|
654
1416
|
# Immediately apply deferrable constraints.
|
|
655
1417
|
#
|
|
656
1418
|
# :constraints :: An identifier of the constraint, or an array of
|
|
@@ -741,6 +1503,28 @@ module Sequel
|
|
|
741
1503
|
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
|
742
1504
|
end
|
|
743
1505
|
|
|
1506
|
+
# Array of symbols specifying property graphs in the current database.
|
|
1507
|
+
# The dataset used is yielded to the block if one is provided,
|
|
1508
|
+
# otherwise, an array of symbols of property graph names is returned.
|
|
1509
|
+
# Supported on PostgreSQL 19+, will be an empty array on lower versions.
|
|
1510
|
+
#
|
|
1511
|
+
# Options:
|
|
1512
|
+
# :qualify :: Return the property graph names as Sequel::SQL::QualifiedIdentifier
|
|
1513
|
+
# instances, using the schema the property graph is located in as the qualifier.
|
|
1514
|
+
# :schema :: The schema to search
|
|
1515
|
+
# :server :: The server to use
|
|
1516
|
+
def property_graphs(opts=OPTS, &block)
|
|
1517
|
+
pg_class_relname('g', opts, &block)
|
|
1518
|
+
end
|
|
1519
|
+
|
|
1520
|
+
# Rename a property graph.
|
|
1521
|
+
#
|
|
1522
|
+
# DB.rename_property_graph(:x, :y)
|
|
1523
|
+
# # ALTER PROPERTY GRAPH x RENAME TO y
|
|
1524
|
+
def rename_property_graph(old_name, new_name)
|
|
1525
|
+
execute_ddl("ALTER PROPERTY GRAPH #{literal(old_name)} RENAME TO #{literal(new_name)}".freeze)
|
|
1526
|
+
end
|
|
1527
|
+
|
|
744
1528
|
# Rename a schema in the database. Arguments:
|
|
745
1529
|
# name :: Current name of the schema
|
|
746
1530
|
# opts :: New name for the schema
|
|
@@ -804,6 +1588,16 @@ module Sequel
|
|
|
804
1588
|
@server_version = swallow_database_error{ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value} || 0
|
|
805
1589
|
end
|
|
806
1590
|
|
|
1591
|
+
# Change the schema for a property graph. Options:
|
|
1592
|
+
# :if_exists :: Use the IF EXISTS clause to not raise an error if the
|
|
1593
|
+
# property graph does not exist.
|
|
1594
|
+
#
|
|
1595
|
+
# DB.set_property_graph_schema(:x, :y)
|
|
1596
|
+
# # ALTER PROPERTY GRAPH x SET SCHEMA y
|
|
1597
|
+
def set_property_graph_schema(old_name, new_name, opts=OPTS)
|
|
1598
|
+
execute_ddl("ALTER PROPERTY GRAPH#{" IF EXISTS" if opts[:if_exists]} #{literal(old_name)} SET SCHEMA #{literal(new_name)}".freeze)
|
|
1599
|
+
end
|
|
1600
|
+
|
|
807
1601
|
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
808
1602
|
def supports_create_table_if_not_exists?
|
|
809
1603
|
server_version >= 90100
|
|
@@ -1204,6 +1998,62 @@ module Sequel
|
|
|
1204
1998
|
raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
|
|
1205
1999
|
end
|
|
1206
2000
|
|
|
2001
|
+
# SQL statement for a single ALTER PROPERTY GRAPH operation.
|
|
2002
|
+
def alter_property_graph_op_sql(name, op)
|
|
2003
|
+
sql = String.new << "ALTER PROPERTY GRAPH " << quote_schema_table(name) << " "
|
|
2004
|
+
|
|
2005
|
+
case op_type = op[:op]
|
|
2006
|
+
when :add_vertex_tables
|
|
2007
|
+
sql << "ADD VERTEX TABLES (" <<
|
|
2008
|
+
op[:tables].map do |vertex|
|
|
2009
|
+
create_property_graph_table_sql(vertex) <<
|
|
2010
|
+
create_property_graph_labels_sql(vertex.labels)
|
|
2011
|
+
end.join(', ') << ")"
|
|
2012
|
+
when :add_edge_tables
|
|
2013
|
+
sql << "ADD EDGE TABLES (" <<
|
|
2014
|
+
op[:tables].map do |edge|
|
|
2015
|
+
create_property_graph_table_sql(edge) <<
|
|
2016
|
+
" SOURCE " << create_property_graph_edge_side_sql(edge.source) <<
|
|
2017
|
+
" DESTINATION " << create_property_graph_edge_side_sql(edge.destination) <<
|
|
2018
|
+
create_property_graph_labels_sql(edge.labels)
|
|
2019
|
+
end.join(', ') << ")"
|
|
2020
|
+
when :drop_vertex_tables, :drop_edge_tables
|
|
2021
|
+
sql << (op_type == :drop_vertex_tables ? "DROP VERTEX TABLES " : "DROP EDGE TABLES ") <<
|
|
2022
|
+
literal(op[:aliases])
|
|
2023
|
+
when :add_label
|
|
2024
|
+
sql << alter_property_graph_element_table_sql(op)
|
|
2025
|
+
op[:labels].each do |label_name, properties|
|
|
2026
|
+
sql << " ADD LABEL " << quote_identifier(label_name) <<
|
|
2027
|
+
create_property_graph_properties_clause_sql(properties)
|
|
2028
|
+
end
|
|
2029
|
+
when :drop_label
|
|
2030
|
+
sql << alter_property_graph_element_table_sql(op) <<
|
|
2031
|
+
" DROP LABEL " << quote_identifier(op[:label])
|
|
2032
|
+
when :add_properties
|
|
2033
|
+
sql << alter_property_graph_element_table_sql(op) <<
|
|
2034
|
+
" ALTER LABEL " << quote_identifier(op[:label]) << " ADD PROPERTIES " <<
|
|
2035
|
+
literal(op[:properties])
|
|
2036
|
+
when :drop_properties
|
|
2037
|
+
sql << alter_property_graph_element_table_sql(op) <<
|
|
2038
|
+
" ALTER LABEL " << quote_identifier(op[:label]) << " DROP PROPERTIES " <<
|
|
2039
|
+
literal(op[:properties])
|
|
2040
|
+
else # when :set_owner
|
|
2041
|
+
sql << "OWNER TO " << literal(op[:owner])
|
|
2042
|
+
end
|
|
2043
|
+
|
|
2044
|
+
case op_type
|
|
2045
|
+
when :drop_vertex_tables, :drop_edge_tables, :drop_label, :drop_properties
|
|
2046
|
+
sql << " CASCADE" if op[:cascade]
|
|
2047
|
+
end
|
|
2048
|
+
|
|
2049
|
+
sql
|
|
2050
|
+
end
|
|
2051
|
+
|
|
2052
|
+
# SQL fragment for the ALTER PROPERTY GRAPH ALTER {VERTEX|EDGE} TABLE prefix
|
|
2053
|
+
def alter_property_graph_element_table_sql(op)
|
|
2054
|
+
"ALTER #{op[:kind] == :vertex ? 'VERTEX' : 'EDGE'} TABLE #{quote_identifier(op[:name])}"
|
|
2055
|
+
end
|
|
2056
|
+
|
|
1207
2057
|
def alter_table_add_column_sql(table, op)
|
|
1208
2058
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
|
1209
2059
|
end
|
|
@@ -1597,6 +2447,87 @@ module Sequel
|
|
|
1597
2447
|
sql
|
|
1598
2448
|
end
|
|
1599
2449
|
|
|
2450
|
+
# SQL statement for creating a property graph.
|
|
2451
|
+
def create_property_graph_sql(name, data, opts=OPTS)
|
|
2452
|
+
sql = String.new
|
|
2453
|
+
sql << "CREATE "
|
|
2454
|
+
sql << "TEMPORARY " if opts[:temp]
|
|
2455
|
+
sql << "PROPERTY GRAPH "
|
|
2456
|
+
sql << quote_schema_table(name)
|
|
2457
|
+
|
|
2458
|
+
unless data.vertices.empty?
|
|
2459
|
+
sql << " VERTEX TABLES ("
|
|
2460
|
+
sql << data.vertices.map do |vertex|
|
|
2461
|
+
create_property_graph_table_sql(vertex) <<
|
|
2462
|
+
create_property_graph_labels_sql(vertex.labels)
|
|
2463
|
+
end.join(', ')
|
|
2464
|
+
sql << ")"
|
|
2465
|
+
end
|
|
2466
|
+
|
|
2467
|
+
unless data.edges.empty?
|
|
2468
|
+
sql << " EDGE TABLES ("
|
|
2469
|
+
sql << data.edges.map do |edge|
|
|
2470
|
+
create_property_graph_table_sql(edge) <<
|
|
2471
|
+
" SOURCE " << create_property_graph_edge_side_sql(edge.source) <<
|
|
2472
|
+
" DESTINATION " << create_property_graph_edge_side_sql(edge.destination) <<
|
|
2473
|
+
create_property_graph_labels_sql(edge.labels)
|
|
2474
|
+
end.join(', ')
|
|
2475
|
+
sql << ")"
|
|
2476
|
+
end
|
|
2477
|
+
|
|
2478
|
+
sql
|
|
2479
|
+
end
|
|
2480
|
+
|
|
2481
|
+
# SQL fragment for the SOURCE or DESTINATION clause of an edge in a property graph.
|
|
2482
|
+
def create_property_graph_edge_side_sql(side)
|
|
2483
|
+
sql = String.new
|
|
2484
|
+
if side.key
|
|
2485
|
+
sql << "KEY " << literal(side.key) << " REFERENCES "
|
|
2486
|
+
end
|
|
2487
|
+
sql << quote_identifier(side.name)
|
|
2488
|
+
if side.references
|
|
2489
|
+
sql << " " << literal(side.references)
|
|
2490
|
+
end
|
|
2491
|
+
sql
|
|
2492
|
+
end
|
|
2493
|
+
|
|
2494
|
+
# SQL fragment for the table name and KEY clause used for vertices and edges in
|
|
2495
|
+
# a property graph.
|
|
2496
|
+
def create_property_graph_table_sql(element)
|
|
2497
|
+
sql = String.new
|
|
2498
|
+
sql << literal(element.name)
|
|
2499
|
+
|
|
2500
|
+
if key = element.key
|
|
2501
|
+
sql << " KEY " << literal(key)
|
|
2502
|
+
end
|
|
2503
|
+
|
|
2504
|
+
sql
|
|
2505
|
+
end
|
|
2506
|
+
|
|
2507
|
+
# SQL fragment for the LABEL/PROPERTIES clauses used for vertices and
|
|
2508
|
+
# edges in a property graph.
|
|
2509
|
+
def create_property_graph_labels_sql(labels)
|
|
2510
|
+
labels.map do |name, properties|
|
|
2511
|
+
sql = String.new
|
|
2512
|
+
sql << " LABEL " << quote_identifier(name) if name
|
|
2513
|
+
sql << create_property_graph_properties_clause_sql(properties)
|
|
2514
|
+
sql
|
|
2515
|
+
end.join
|
|
2516
|
+
end
|
|
2517
|
+
|
|
2518
|
+
# SQL fragment for the NO PROPERTIES, PROPERTIES ALL COLUMNS, or
|
|
2519
|
+
# PROPERTIES (...) clause for a property graph element or label.
|
|
2520
|
+
def create_property_graph_properties_clause_sql(properties)
|
|
2521
|
+
case properties
|
|
2522
|
+
when nil, :all
|
|
2523
|
+
" PROPERTIES ALL COLUMNS"
|
|
2524
|
+
when false, :none, [].freeze
|
|
2525
|
+
" NO PROPERTIES"
|
|
2526
|
+
else
|
|
2527
|
+
" PROPERTIES #{literal(properties)}"
|
|
2528
|
+
end
|
|
2529
|
+
end
|
|
2530
|
+
|
|
1600
2531
|
# SQL for creating a schema.
|
|
1601
2532
|
def create_schema_sql(name, opts=OPTS)
|
|
1602
2533
|
"CREATE SCHEMA #{'IF NOT EXISTS ' if opts[:if_not_exists]}#{quote_identifier(name)}#{" AUTHORIZATION #{literal(opts[:owner])}" if opts[:owner]}"
|
|
@@ -1710,6 +2641,11 @@ module Sequel
|
|
|
1710
2641
|
"DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
|
|
1711
2642
|
end
|
|
1712
2643
|
|
|
2644
|
+
# SQL for dropping a property graph from the database.
|
|
2645
|
+
def drop_property_graph_sql(name, opts=OPTS)
|
|
2646
|
+
"DROP PROPERTY GRAPH#{' IF EXISTS' if opts[:if_exists]} #{literal(name)}#{' CASCADE' if opts[:cascade]}"
|
|
2647
|
+
end
|
|
2648
|
+
|
|
1713
2649
|
# SQL for dropping a schema from the database.
|
|
1714
2650
|
def drop_schema_sql(name, opts=OPTS)
|
|
1715
2651
|
"DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
|
|
@@ -677,14 +677,15 @@ module Sequel
|
|
|
677
677
|
super
|
|
678
678
|
end
|
|
679
679
|
|
|
680
|
-
# Return
|
|
681
|
-
# current dataset.
|
|
682
|
-
#
|
|
680
|
+
# Return a string specifying a query explanation for a SELECT of the
|
|
681
|
+
# current dataset. Options:
|
|
682
|
+
# :query_plan :: Use EXPLAIN QUERY PLAN instead of EXPLAIN if true.
|
|
683
683
|
def explain(opts=nil)
|
|
684
684
|
# Load the PrettyTable class, needed for explain output
|
|
685
685
|
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
|
686
686
|
|
|
687
|
-
|
|
687
|
+
keyword = (opts && opts[:query_plan]) ? "EXPLAIN QUERY PLAN" : "EXPLAIN"
|
|
688
|
+
ds = db.send(:metadata_dataset).clone(:sql=>"#{keyword} #{select_sql}".freeze)
|
|
688
689
|
rows = ds.all
|
|
689
690
|
Sequel::PrettyTable.string(rows, ds.columns)
|
|
690
691
|
end
|
|
@@ -17,7 +17,9 @@ module Sequel
|
|
|
17
17
|
if RUBY_VERSION >= "3.2"
|
|
18
18
|
# :nocov:
|
|
19
19
|
caller_loc = Thread.each_caller_location do |loc|
|
|
20
|
-
|
|
20
|
+
# Skip core library methods implemented in Ruby
|
|
21
|
+
path = loc.path
|
|
22
|
+
break loc unless path == __FILE__ || (path && path.start_with?('<internal:'))
|
|
21
23
|
end
|
|
22
24
|
caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
|
|
23
25
|
end
|
data/lib/sequel/version.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Sequel
|
|
|
6
6
|
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
8
8
|
# release, generally around once a month.
|
|
9
|
-
MINOR =
|
|
9
|
+
MINOR = 107
|
|
10
10
|
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
12
12
|
# releases that fix regressions from previous versions.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.107.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
@@ -455,7 +455,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
455
455
|
- !ruby/object:Gem::Version
|
|
456
456
|
version: '0'
|
|
457
457
|
requirements: []
|
|
458
|
-
rubygems_version: 4.0.
|
|
458
|
+
rubygems_version: 4.0.16
|
|
459
459
|
specification_version: 4
|
|
460
460
|
summary: The Database Toolkit for Ruby
|
|
461
461
|
test_files: []
|