ruby-ladybug 0.1.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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/History.md +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +250 -0
- data/ext/ladybug_ext/config.c +318 -0
- data/ext/ladybug_ext/connection.c +331 -0
- data/ext/ladybug_ext/database.c +197 -0
- data/ext/ladybug_ext/extconf.rb +20 -0
- data/ext/ladybug_ext/ladybug_ext.c +158 -0
- data/ext/ladybug_ext/ladybug_ext.h +132 -0
- data/ext/ladybug_ext/node.c +24 -0
- data/ext/ladybug_ext/prepared_statement.c +396 -0
- data/ext/ladybug_ext/query_summary.c +140 -0
- data/ext/ladybug_ext/recursive_rel.c +24 -0
- data/ext/ladybug_ext/rel.c +24 -0
- data/ext/ladybug_ext/result.c +514 -0
- data/ext/ladybug_ext/types.c +619 -0
- data/lib/ladybug/config.rb +70 -0
- data/lib/ladybug/connection.rb +51 -0
- data/lib/ladybug/database.rb +53 -0
- data/lib/ladybug/node.rb +46 -0
- data/lib/ladybug/prepared_statement.rb +44 -0
- data/lib/ladybug/query_summary.rb +28 -0
- data/lib/ladybug/recursive_rel.rb +37 -0
- data/lib/ladybug/rel.rb +57 -0
- data/lib/ladybug/result.rb +196 -0
- data/lib/ladybug.rb +89 -0
- data/spec/ladybug/config_spec.rb +98 -0
- data/spec/ladybug/connection_spec.rb +36 -0
- data/spec/ladybug/database_spec.rb +57 -0
- data/spec/ladybug/prepared_statement_spec.rb +91 -0
- data/spec/ladybug/query_summary_spec.rb +30 -0
- data/spec/ladybug/result_spec.rb +225 -0
- data/spec/ladybug/types_spec.rb +285 -0
- data/spec/ladybug_spec.rb +83 -0
- data/spec/spec_helper.rb +101 -0
- data.tar.gz.sig +0 -0
- metadata +177 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'loggability'
|
|
4
|
+
|
|
5
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Kùzu connection class
|
|
9
|
+
class Ladybug::Connection
|
|
10
|
+
extend Loggability
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Loggability API -- log to Ladybug's logger
|
|
14
|
+
log_to :ladybug
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Execute the given +query_string+ via the connection and return the
|
|
18
|
+
### Ladybug::Result. If a block is given, the result will instead be yielded to it,
|
|
19
|
+
### finished when it returns, and the return value of the block will be returned
|
|
20
|
+
### instead.
|
|
21
|
+
def query( query_string, &block )
|
|
22
|
+
result = self._query( query_string )
|
|
23
|
+
return Ladybug::Result.wrap_block_result( result, &block )
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Create a new Ladybug::PreparedStatement for the specified +query_string+.
|
|
28
|
+
def prepare( query_string )
|
|
29
|
+
return Ladybug::PreparedStatement.new( self, query_string )
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Executes the given +statement+ (a Ladybug::PreparedStatement) after binding
|
|
34
|
+
### the given +bound_variables+ to it.
|
|
35
|
+
def execute( statement, **bound_variables, &block )
|
|
36
|
+
statement.bind( **bound_variables )
|
|
37
|
+
return statement.execute
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Return a string representation of the receiver suitable for debugging.
|
|
42
|
+
def inspect
|
|
43
|
+
details = " threads:%d" % [
|
|
44
|
+
self.max_num_threads_for_exec,
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
default = super
|
|
48
|
+
return default.sub( />/, details + '>' )
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end # class Ladybug::Connection
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'loggability'
|
|
4
|
+
|
|
5
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Main Kùzu database class
|
|
9
|
+
class Ladybug::Database
|
|
10
|
+
extend Loggability
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Loggability API -- log to Ladybug's logger
|
|
14
|
+
log_to :ladybug
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Return a connection to this database.
|
|
18
|
+
def connect
|
|
19
|
+
return Ladybug::Connection.new( self )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Return +true+ if this database was created in read-only mode.
|
|
24
|
+
def read_only?
|
|
25
|
+
return self.config.read_only
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Returns +true+ if this database will automatically checkpoint when the size of
|
|
30
|
+
### the WAL file exceeds the `checkpoint_threshold`.
|
|
31
|
+
def auto_checkpointing?
|
|
32
|
+
return self.config.auto_checkpoint
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Returns +true+ if this database uses compression for data on disk.
|
|
37
|
+
def compression_enabled?
|
|
38
|
+
return self.config.enable_compression
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Return a string representation of the receiver suitable for debugging.
|
|
43
|
+
def inspect
|
|
44
|
+
details = " path:%p read-only:%p" % [
|
|
45
|
+
self.path,
|
|
46
|
+
self.read_only?,
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
default = super
|
|
50
|
+
return default.sub( />/, details + '>' )
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end # class Ladybug::Database
|
data/lib/ladybug/node.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'loggability'
|
|
5
|
+
|
|
6
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Ladybug node class
|
|
10
|
+
class Ladybug::Node
|
|
11
|
+
extend Loggability,
|
|
12
|
+
Forwardable
|
|
13
|
+
|
|
14
|
+
# Loggability API -- log to Ladybug's logger
|
|
15
|
+
log_to :ladybug
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Create a new Node with the given +id+, +label+, and +properties+.
|
|
19
|
+
def initialize( id, label, **properties )
|
|
20
|
+
@id = id
|
|
21
|
+
@label = label
|
|
22
|
+
@properties = properties
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
######
|
|
27
|
+
public
|
|
28
|
+
######
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# The internal id value of the given node
|
|
32
|
+
attr_reader :id
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# The label value of the given node
|
|
36
|
+
attr_reader :label
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# The Hash of the Node's properties, keyed by name as a Symbol
|
|
40
|
+
attr_reader :properties
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Allow direct access to properties
|
|
44
|
+
def_delegators :@properties, :[], :[]=, :dig
|
|
45
|
+
|
|
46
|
+
end # class Ladybug::Node
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'loggability'
|
|
4
|
+
|
|
5
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# A parameterized query which can avoid planning the same query for repeated execution
|
|
9
|
+
class Ladybug::PreparedStatement
|
|
10
|
+
extend Loggability
|
|
11
|
+
|
|
12
|
+
# Loggability API -- Use Ladybug's logger
|
|
13
|
+
log_to :ladybug
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Execute the statement against its connection and return a Ladybug::Result.
|
|
17
|
+
### If a +block+ is supplied, the result will be passed to it instead,
|
|
18
|
+
### then finished automatically, and the return value of the block returned
|
|
19
|
+
### instead.
|
|
20
|
+
def execute( **bound_variables, &block )
|
|
21
|
+
self.log.debug "Executing statement:\n%s\nwith variables:\n%p" %
|
|
22
|
+
[ self.query, bound_variables ]
|
|
23
|
+
self.bind( **bound_variables )
|
|
24
|
+
result = self._execute
|
|
25
|
+
return Ladybug::Result.wrap_block_result( result, &block )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Execute the statement against its connection and return `true` if it
|
|
30
|
+
### succeeded.
|
|
31
|
+
def execute!( **bound_variables )
|
|
32
|
+
self.bind( **bound_variables )
|
|
33
|
+
return self._execute!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Bind the variables in the specified +variable_map+ to the statement.
|
|
38
|
+
def bind( **variable_map )
|
|
39
|
+
variable_map.each do |name, value|
|
|
40
|
+
self.bind_variable( name, value )
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end # class Ladybug::PreparedStatement
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'loggability'
|
|
4
|
+
|
|
5
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Kùzu query summary class
|
|
9
|
+
class Ladybug::QuerySummary
|
|
10
|
+
extend Loggability
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Loggability API -- log to Ladybug's logger
|
|
14
|
+
log_to :ladybug
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Return a string representation of the receiver suitable for debugging.
|
|
18
|
+
def inspect
|
|
19
|
+
details = " compiling: %0.3fs execution: %0.3fs" % [
|
|
20
|
+
self.compiling_time,
|
|
21
|
+
self.execution_time,
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
default = super
|
|
25
|
+
return default.sub( />/, details + '>' )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end # class Ladybug::QuerySummary
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'loggability'
|
|
5
|
+
|
|
6
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Ladybug recursive relationship class
|
|
10
|
+
class Ladybug::RecursiveRel
|
|
11
|
+
extend Loggability,
|
|
12
|
+
Forwardable
|
|
13
|
+
|
|
14
|
+
# Loggability API -- log to Ladybug's logger
|
|
15
|
+
log_to :ladybug
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Create a new RecursiveRel with the given +nodes+ and +rels+.
|
|
19
|
+
def initialize( nodes, rels )
|
|
20
|
+
@nodes = nodes
|
|
21
|
+
@rels = rels
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
######
|
|
26
|
+
public
|
|
27
|
+
######
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# The Array of Ladybug::Nodes in the chain
|
|
31
|
+
attr_reader :nodes
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# The Array of Ladybug::Rels connecting the Nodes in the chain
|
|
35
|
+
attr_reader :rels
|
|
36
|
+
|
|
37
|
+
end # class Ladybug::RecursiveRel
|
data/lib/ladybug/rel.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require 'loggability'
|
|
5
|
+
|
|
6
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Ladybug rel (relationship) class
|
|
10
|
+
class Ladybug::Rel
|
|
11
|
+
extend Loggability,
|
|
12
|
+
Forwardable
|
|
13
|
+
|
|
14
|
+
# Loggability API -- log to Ladybug's logger
|
|
15
|
+
log_to :ladybug
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Create a new Rel with the given +id+, +src_id+, +dst_id+,
|
|
19
|
+
### +label+, and +properties+.
|
|
20
|
+
def initialize( id, src_id, dst_id, label, **properties )
|
|
21
|
+
@id = id
|
|
22
|
+
@src_id = src_id
|
|
23
|
+
@dst_id = dst_id
|
|
24
|
+
@label = label
|
|
25
|
+
@properties = properties
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
######
|
|
30
|
+
public
|
|
31
|
+
######
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# The internal id value of the given rel
|
|
35
|
+
attr_reader :id
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# The internal id value of the source node
|
|
39
|
+
attr_reader :src_id
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# The internal id value of the destination node
|
|
43
|
+
attr_reader :dst_id
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# The label value of the given node
|
|
47
|
+
attr_reader :label
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# The Hash of the Rel's properties, keyed by name as a Symbol
|
|
51
|
+
attr_reader :properties
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Allow direct access to properties
|
|
55
|
+
def_delegators :@properties, :[], :[]=, :dig
|
|
56
|
+
|
|
57
|
+
end # class Ladybug::Rel
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'loggability'
|
|
4
|
+
|
|
5
|
+
require 'ladybug' unless defined?( Ladybug )
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Kùzu query result class
|
|
9
|
+
#
|
|
10
|
+
# These objects contain one result set from either a Ladybug::Connection#query call
|
|
11
|
+
# or Ladybug::PreparedStatement#execute. If there are multiple result sets, you can
|
|
12
|
+
# fetch the next one by calling Ladybug::Result#next_set. You can use #has_next_set?
|
|
13
|
+
# to test for a following set.
|
|
14
|
+
#
|
|
15
|
+
# Tuple values are converted to corresponding Ruby objects:
|
|
16
|
+
#
|
|
17
|
+
# | Ladybug Type | Ruby Type |
|
|
18
|
+
# | --------------- | ----------------------------------------------- |
|
|
19
|
+
# | +INT8+ | +Integer+ |
|
|
20
|
+
# | +INT16+ | +Integer+ |
|
|
21
|
+
# | +INT32+ | +Integer+ |
|
|
22
|
+
# | +INT64+ | +Integer+ |
|
|
23
|
+
# | +INT128+ | +Integer+ |
|
|
24
|
+
# | +UINT8+ | +Integer+ |
|
|
25
|
+
# | +UINT16+ | +Integer+ |
|
|
26
|
+
# | +UINT32+ | +Integer+ |
|
|
27
|
+
# | +UINT64+ | +Integer+ |
|
|
28
|
+
# | +FLOAT+ | +Float+ |
|
|
29
|
+
# | +DOUBLE+ | +Float+ |
|
|
30
|
+
# | +DECIMAL+ | +Float+ |
|
|
31
|
+
# | +BOOLEAN+ | +TrueClass+ or +FalseClass+ |
|
|
32
|
+
# | +UUID+ | +String+ (UTF-8 encoding) |
|
|
33
|
+
# | +STRING+ | +String+ (UTF-8 encoding) |
|
|
34
|
+
# | +NULL+ | +NilClass+ |
|
|
35
|
+
# | +DATE+ | +Date+ |
|
|
36
|
+
# | +TIMESTAMP+ | +Time+ |
|
|
37
|
+
# | +INTERVAL+ | +Float+ (interval in seconds) |
|
|
38
|
+
# | +STRUCT+ | +OpenStruct+ via the +ostruct+ standard library |
|
|
39
|
+
# | +MAP+ | +Hash+ |
|
|
40
|
+
# | +UNION+ | (not yet handled) |
|
|
41
|
+
# | +BLOB+ | +String+ (+ASCII_8BIT+ encoding) |
|
|
42
|
+
# | +SERIAL+ | +Integer+ |
|
|
43
|
+
# | +NODE+ | Ladybug::Node |
|
|
44
|
+
# | +REL+ | Ladybug::Rel |
|
|
45
|
+
# | +RECURSIVE_REL+ | Ladybug::RecursiveRel |
|
|
46
|
+
# | +LIST+ | +Array+ |
|
|
47
|
+
# | +ARRAY+ | +Array+ |
|
|
48
|
+
#
|
|
49
|
+
#
|
|
50
|
+
class Ladybug::Result
|
|
51
|
+
extend Loggability
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Loggability API -- log to Ladybug's logger
|
|
55
|
+
log_to :ladybug
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### Execute the given +query+ via the specified +connection+ and return the
|
|
59
|
+
### Ladybug::Result. If a block is given, the result will instead be yielded to it,
|
|
60
|
+
### finished when it returns, and the return value of the block will be returned
|
|
61
|
+
### instead.
|
|
62
|
+
def self::from_query( connection, query, &block )
|
|
63
|
+
return connection.query( query, &block )
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Execute the given +statement+ and return the Ladybug::Result. If a block is given,
|
|
68
|
+
### the result will instead be yielded to it, finished when it returns, and the
|
|
69
|
+
### return value of the block will be returned instead.
|
|
70
|
+
def self::from_prepared_statement( statement, &block )
|
|
71
|
+
return statement.execute( &block )
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### If the +block+ is provided, yield +result+ to it and then call #finish on it,
|
|
76
|
+
### returning the +block+ result. If +block+ is not given, just return +result+.
|
|
77
|
+
def self::wrap_block_result( result, &block )
|
|
78
|
+
return result unless block
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
rval = block.call( result )
|
|
82
|
+
ensure
|
|
83
|
+
result.finish
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
return rval
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
### Fetch the names of the columns in the result as an Array of Strings.
|
|
91
|
+
def column_names
|
|
92
|
+
return @column_names ||= self.get_column_names
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Return a Ladybug::QuerySummary for the query that generated the Result.
|
|
97
|
+
def query_summary
|
|
98
|
+
return Ladybug::QuerySummary.from_result( self )
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### Get the next tuple of the result as a Hash.
|
|
103
|
+
def next
|
|
104
|
+
values = self.get_next_values or return nil
|
|
105
|
+
pairs = self.column_names.zip( values )
|
|
106
|
+
return Hash[ pairs ]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Iterate over each tuple of the result, yielding it to the +block+. If no
|
|
111
|
+
### +block+ is given, return an Enumerator that will yield them instead.
|
|
112
|
+
def each( &block )
|
|
113
|
+
enum = self.tuple_enum
|
|
114
|
+
return enum.each( &block ) if block
|
|
115
|
+
return enum
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### Return the tuples from the current result set. This method is memoized
|
|
120
|
+
### for efficiency.
|
|
121
|
+
def tuples
|
|
122
|
+
return @_tuples ||= self.to_a
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
### Index operator: fetch the tuple at +index+ of the current result set.
|
|
127
|
+
def []( index )
|
|
128
|
+
return self.tuples[ index ]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
### Return the next result set after this one as a Ladybug::Result, or `nil`if
|
|
133
|
+
### there is no next set.
|
|
134
|
+
def next_set
|
|
135
|
+
return nil unless self.has_next_set?
|
|
136
|
+
return self.class.from_next_set( self )
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
### Iterate over each result set in the results, yielding it to the block. If
|
|
141
|
+
### no +block+ is given, return an Enumerator tht will yield each set as its own
|
|
142
|
+
### Result.
|
|
143
|
+
def each_set( &block )
|
|
144
|
+
enum = self.next_set_enum
|
|
145
|
+
return enum.each( &block ) if block
|
|
146
|
+
return enum
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
### Return a string representation of the receiver suitable for debugging.
|
|
151
|
+
def inspect
|
|
152
|
+
if self.finished?
|
|
153
|
+
details = " (finished)"
|
|
154
|
+
else
|
|
155
|
+
details = " success: %p (%d tuples of %d columns)" % [
|
|
156
|
+
self.success?,
|
|
157
|
+
self.num_tuples,
|
|
158
|
+
self.num_columns,
|
|
159
|
+
]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
default = super
|
|
163
|
+
return default.sub( />/, details + '>' )
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
#########
|
|
168
|
+
protected
|
|
169
|
+
#########
|
|
170
|
+
|
|
171
|
+
### Return an Enumerator that yields result tuples as Hashes.
|
|
172
|
+
def tuple_enum
|
|
173
|
+
self.log.debug "Fetching a tuple Enumerator"
|
|
174
|
+
return Enumerator.new do |yielder|
|
|
175
|
+
self.reset_iterator
|
|
176
|
+
while self.has_next?
|
|
177
|
+
tuple = self.next
|
|
178
|
+
yielder.yield( tuple )
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
### Return an Enumerator that yields a Result for each set.
|
|
185
|
+
def next_set_enum
|
|
186
|
+
self.log.debug "Fetching a result set Enumerator"
|
|
187
|
+
result = self
|
|
188
|
+
return Enumerator.new do |yielder|
|
|
189
|
+
while result
|
|
190
|
+
yielder.yield( result )
|
|
191
|
+
result = result.next_set
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
end # class Ladybug::Result
|
data/lib/ladybug.rb
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'loggability'
|
|
6
|
+
|
|
7
|
+
require_relative 'ladybug_ext'
|
|
8
|
+
|
|
9
|
+
#--
|
|
10
|
+
# See also: ext/ladybug_ext/ladybug_ext.c
|
|
11
|
+
module Ladybug
|
|
12
|
+
extend Loggability
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library version
|
|
16
|
+
VERSION = '0.1.0'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Set up a logger for Ladybug classes
|
|
20
|
+
log_as :ladybug
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Create and return a Ladybug::Database. If +path+ is +nil+, an empty string, or
|
|
24
|
+
### the Symbol :memory, creates an in-memory database. Valid options are:
|
|
25
|
+
###
|
|
26
|
+
### `:buffer_pool_size`
|
|
27
|
+
### : Max size of the buffer pool in bytes.
|
|
28
|
+
###
|
|
29
|
+
### `:max_num_threads`
|
|
30
|
+
### : The maximum number of threads to use during query execution.
|
|
31
|
+
###
|
|
32
|
+
### `:enable_compression`
|
|
33
|
+
### : Whether or not to compress data on-disk for supported types
|
|
34
|
+
###
|
|
35
|
+
### `:read_only`
|
|
36
|
+
### : If true, open the database in read-only mode. No write transaction is allowed on the
|
|
37
|
+
### Database object. If false, open the database read-write.
|
|
38
|
+
###
|
|
39
|
+
### `:max_db_size`
|
|
40
|
+
### : The maximum size of the database in bytes.
|
|
41
|
+
###
|
|
42
|
+
### `:auto_checkpoint`
|
|
43
|
+
### : If true, the database will automatically checkpoint when the size of
|
|
44
|
+
### the WAL file exceeds the checkpoint threshold.
|
|
45
|
+
###
|
|
46
|
+
### `:checkpoint_threshold`
|
|
47
|
+
### : The threshold of the WAL file size in bytes. When the size of the
|
|
48
|
+
### WAL file exceeds this threshold, the database will checkpoint if
|
|
49
|
+
### `auto_checkpoint` is true.
|
|
50
|
+
def self::database( path='', **config )
|
|
51
|
+
path = '' if path.nil? || path == :memory
|
|
52
|
+
self.log.info "Opening database %p" % [ path ]
|
|
53
|
+
return Ladybug::Database.new( path.to_s, **config )
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Returns +true+ if the specified +pathname+ appears to be a valid Ladybug database
|
|
58
|
+
### for the current version of the storage format.
|
|
59
|
+
def self::is_database?( pathname )
|
|
60
|
+
pathname = Pathname( pathname )
|
|
61
|
+
return false unless pathname.file?
|
|
62
|
+
magic = pathname.read( 5 )
|
|
63
|
+
return magic[0, 4] == 'LBUG' && magic[4, 1].ord == Ladybug.storage_version
|
|
64
|
+
end
|
|
65
|
+
singleton_class.alias_method( :is_ladybug_database?, :is_database? )
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Return a Time object from the given +milliseconds+ epoch time.
|
|
69
|
+
def self::timestamp_from_timestamp_ms( milliseconds )
|
|
70
|
+
seconds, subsec = milliseconds.divmod( 1_000 )
|
|
71
|
+
return Time.at( seconds, subsec, :millisecond )
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### Return a Time object from the given +microseconds+ epoch time and
|
|
76
|
+
### optional timezone offset in seconds via the +zone+ argument.
|
|
77
|
+
def self::timestamp_from_timestamp_us( microseconds, zone=nil )
|
|
78
|
+
seconds, subsec = microseconds.divmod( 1_000_000 )
|
|
79
|
+
return Time.at( seconds, subsec, :microsecond, in: zone )
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Return a Time object from the given +nanoseconds+ epoch time.
|
|
84
|
+
def self::timestamp_from_timestamp_ns( nanoseconds )
|
|
85
|
+
seconds, subsec = nanoseconds.divmod( 1_000_000_000 )
|
|
86
|
+
return Time.at( seconds, subsec, :nanosecond )
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end # module Ladybug
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require_relative '../spec_helper'
|
|
4
|
+
|
|
5
|
+
require 'ladybug/config'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
RSpec.describe( Ladybug::Config ) do
|
|
9
|
+
|
|
10
|
+
it "can be created with defaults" do
|
|
11
|
+
result = described_class.new
|
|
12
|
+
|
|
13
|
+
expect( result ).to be_a( described_class )
|
|
14
|
+
|
|
15
|
+
expect( result.buffer_pool_size ).to be_a( Integer )
|
|
16
|
+
expect( result.max_num_threads ).to be_a( Integer )
|
|
17
|
+
expect( result.enable_compression ).to eq( true )
|
|
18
|
+
expect( result.read_only ).to eq( false )
|
|
19
|
+
expect( result.max_db_size ).to be_a( Integer )
|
|
20
|
+
expect( result.auto_checkpoint ).to eq( true )
|
|
21
|
+
expect( result.checkpoint_threshold ).to be_a( Integer )
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
it "can set its buffer_pool_size" do
|
|
26
|
+
instance = described_class.new
|
|
27
|
+
|
|
28
|
+
expect {
|
|
29
|
+
instance.buffer_pool_size = 2 ** 11
|
|
30
|
+
}.to change { instance.buffer_pool_size }.to( 2 ** 11 )
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
it "can set its max_num_threads" do
|
|
35
|
+
instance = described_class.new
|
|
36
|
+
|
|
37
|
+
expect {
|
|
38
|
+
instance.max_num_threads = 4
|
|
39
|
+
}.to change { instance.max_num_threads }.to( 4 )
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
it "can disable compression" do
|
|
44
|
+
instance = described_class.new
|
|
45
|
+
|
|
46
|
+
expect {
|
|
47
|
+
instance.enable_compression = false
|
|
48
|
+
}.to change { instance.enable_compression }.to( false )
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
it "can set read-only mode" do
|
|
53
|
+
instance = described_class.new
|
|
54
|
+
|
|
55
|
+
expect {
|
|
56
|
+
instance.read_only = true
|
|
57
|
+
}.to change { instance.read_only }.to( true )
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
it "can set read-only mode using a truthy value" do
|
|
62
|
+
instance = described_class.new
|
|
63
|
+
|
|
64
|
+
expect {
|
|
65
|
+
instance.read_only = :yep
|
|
66
|
+
}.to change { instance.read_only }.to( true )
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
it "can set its max_db_size" do
|
|
71
|
+
instance = described_class.new
|
|
72
|
+
|
|
73
|
+
expect {
|
|
74
|
+
instance.max_db_size = 2 ** 21
|
|
75
|
+
}.to change { instance.max_db_size }.to( 2 ** 21 )
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
it "can disable auto-checkpointing" do
|
|
80
|
+
instance = described_class.new
|
|
81
|
+
|
|
82
|
+
expect {
|
|
83
|
+
instance.auto_checkpoint = false
|
|
84
|
+
}.to change { instance.auto_checkpoint }.to( false )
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
it "can set its checkpoint threshold" do
|
|
89
|
+
instance = described_class.new
|
|
90
|
+
|
|
91
|
+
expect {
|
|
92
|
+
instance.checkpoint_threshold = 2 ** 22
|
|
93
|
+
}.to change { instance.checkpoint_threshold }.to( 2 ** 22 )
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
|