libsql 0.1.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +60 -0
  3. data/HISTORY.md +6 -0
  4. data/LICENSE +31 -0
  5. data/Manifest.txt +96 -0
  6. data/README.md +59 -0
  7. data/Rakefile +28 -0
  8. data/TODO.md +57 -0
  9. data/examples/a.rb +9 -0
  10. data/examples/blob.rb +106 -0
  11. data/examples/define_aggregate.rb +75 -0
  12. data/examples/define_function.rb +104 -0
  13. data/examples/fts5.rb +152 -0
  14. data/examples/gem-db.rb +94 -0
  15. data/examples/schema-info.rb +34 -0
  16. data/ext/libsql/c/extconf.rb +86 -0
  17. data/ext/libsql/c/gen_constants.rb +353 -0
  18. data/ext/libsql/c/libsql_blob.c +240 -0
  19. data/ext/libsql/c/libsql_constants.c +1518 -0
  20. data/ext/libsql/c/libsql_database.c +1188 -0
  21. data/ext/libsql/c/libsql_ext.c +383 -0
  22. data/ext/libsql/c/libsql_ext.h +149 -0
  23. data/ext/libsql/c/libsql_statement.c +649 -0
  24. data/ext/libsql/c/notes.txt +134 -0
  25. data/ext/libsql/c/sqlite3.c +247030 -0
  26. data/ext/libsql/c/sqlite3.h +13436 -0
  27. data/lib/libsql/3.0/libsql_ext.so +0 -0
  28. data/lib/libsql/3.1/libsql_ext.so +0 -0
  29. data/lib/libsql/3.2/libsql_ext.so +0 -0
  30. data/lib/libsql/aggregate.rb +73 -0
  31. data/lib/libsql/blob.rb +186 -0
  32. data/lib/libsql/boolean.rb +42 -0
  33. data/lib/libsql/busy_timeout.rb +47 -0
  34. data/lib/libsql/column.rb +99 -0
  35. data/lib/libsql/csv_table_importer.rb +75 -0
  36. data/lib/libsql/database.rb +933 -0
  37. data/lib/libsql/function.rb +61 -0
  38. data/lib/libsql/index.rb +43 -0
  39. data/lib/libsql/memory_database.rb +15 -0
  40. data/lib/libsql/paths.rb +80 -0
  41. data/lib/libsql/profile_tap.rb +131 -0
  42. data/lib/libsql/progress_handler.rb +21 -0
  43. data/lib/libsql/schema.rb +225 -0
  44. data/lib/libsql/sqlite3/constants.rb +95 -0
  45. data/lib/libsql/sqlite3/database/function.rb +48 -0
  46. data/lib/libsql/sqlite3/database/status.rb +68 -0
  47. data/lib/libsql/sqlite3/libsql_version.rb +32 -0
  48. data/lib/libsql/sqlite3/status.rb +60 -0
  49. data/lib/libsql/sqlite3/version.rb +55 -0
  50. data/lib/libsql/sqlite3.rb +7 -0
  51. data/lib/libsql/statement.rb +421 -0
  52. data/lib/libsql/table.rb +91 -0
  53. data/lib/libsql/taps/console.rb +27 -0
  54. data/lib/libsql/taps/io.rb +74 -0
  55. data/lib/libsql/taps.rb +2 -0
  56. data/lib/libsql/trace_tap.rb +35 -0
  57. data/lib/libsql/type_map.rb +63 -0
  58. data/lib/libsql/type_maps/default_map.rb +166 -0
  59. data/lib/libsql/type_maps/storage_map.rb +38 -0
  60. data/lib/libsql/type_maps/text_map.rb +21 -0
  61. data/lib/libsql/version.rb +8 -0
  62. data/lib/libsql/view.rb +26 -0
  63. data/lib/libsql-ruby.rb +1 -0
  64. data/lib/libsql.rb +51 -0
  65. data/spec/aggregate_spec.rb +158 -0
  66. data/spec/blob_spec.rb +78 -0
  67. data/spec/boolean_spec.rb +24 -0
  68. data/spec/busy_handler.rb +157 -0
  69. data/spec/data/iso-3166-country.txt +242 -0
  70. data/spec/data/iso-3166-schema.sql +22 -0
  71. data/spec/data/iso-3166-subcountry.txt +3995 -0
  72. data/spec/data/make-iso-db.sh +12 -0
  73. data/spec/database_spec.rb +505 -0
  74. data/spec/default_map_spec.rb +92 -0
  75. data/spec/function_spec.rb +78 -0
  76. data/spec/integeration_spec.rb +97 -0
  77. data/spec/iso_3166_database.rb +58 -0
  78. data/spec/json_spec.rb +24 -0
  79. data/spec/libsql_spec.rb +4 -0
  80. data/spec/paths_spec.rb +28 -0
  81. data/spec/progress_handler_spec.rb +91 -0
  82. data/spec/rtree_spec.rb +66 -0
  83. data/spec/schema_spec.rb +131 -0
  84. data/spec/spec_helper.rb +48 -0
  85. data/spec/sqlite3/constants_spec.rb +108 -0
  86. data/spec/sqlite3/database_status_spec.rb +36 -0
  87. data/spec/sqlite3/libsql_version_spec.rb +16 -0
  88. data/spec/sqlite3/status_spec.rb +22 -0
  89. data/spec/sqlite3/version_spec.rb +28 -0
  90. data/spec/sqlite3_spec.rb +53 -0
  91. data/spec/statement_spec.rb +168 -0
  92. data/spec/storage_map_spec.rb +38 -0
  93. data/spec/tap_spec.rb +57 -0
  94. data/spec/text_map_spec.rb +20 -0
  95. data/spec/type_map_spec.rb +14 -0
  96. data/spec/version_spec.rb +8 -0
  97. data/tasks/custom.rake +134 -0
  98. data/tasks/default.rake +257 -0
  99. data/tasks/extension.rake +29 -0
  100. data/tasks/this.rb +208 -0
  101. metadata +330 -0
Binary file
Binary file
Binary file
@@ -0,0 +1,73 @@
1
+ require 'libsql/sqlite3/database/function'
2
+ module ::Libsql
3
+ #
4
+ # A Base class to inherit from for creating your own SQL aggregate functions
5
+ # in ruby.
6
+ #
7
+ # These are SQL functions similar to _max(X)_, _count(X)_, _avg(X)_. The built
8
+ # in SQLite aggregate functions are:
9
+ #
10
+ # * http://www.sqlite.org/lang_aggfunc.html
11
+ #
12
+ # If you choose to use Aggregate as a parent class of your SQL scalar function
13
+ # implementation you must:
14
+ #
15
+ # * implement _initalize_ with 0 arguments
16
+ # * call super() in your _initialize_ method
17
+ # * set the @arity data member
18
+ # * set the @name data member
19
+ # * implement _step_ with arity of +@arity+
20
+ # * implement _finalize_ with arity of 0
21
+ #
22
+ # For instance to implement a <i>unique_word_count(X)</i> aggregate function you could
23
+ # implement it as:
24
+ #
25
+ # class UniqueWordCount < ::Libsql::Aggregate
26
+ # attr_accessor :words
27
+ #
28
+ # def initialize
29
+ # super
30
+ # @name = 'unique_word_count'
31
+ # @arity = 1
32
+ # @words = Hash.new { |h,k| h[k] = 0 }
33
+ # end
34
+ #
35
+ # def step( str )
36
+ # str.split(/\W+/).each do |word|
37
+ # words[ word.downcase ] += 1
38
+ # end
39
+ # return nil
40
+ # end
41
+ #
42
+ # def finalize
43
+ # return words.size
44
+ # end
45
+ # end
46
+ #
47
+ #
48
+ class Aggregate
49
+ # The name of the SQL function
50
+ attr_accessor :name
51
+
52
+ # The arity of the SQL function
53
+ attr_accessor :arity
54
+
55
+ def initialize
56
+ @_exception = nil
57
+ end
58
+
59
+ # finalize should return the final value of the aggregate function
60
+ def finalize
61
+ raise NotImplementedError, "Aggregate#finalize must be implemented"
62
+ end
63
+
64
+ # <b>Do Not Override</b>
65
+ #
66
+ # The function signature for use by the Amaglaite datase in tracking
67
+ # function creation.
68
+ #
69
+ def signature
70
+ @signature ||= ::Libsql::SQLite3::Database::Function.signature( self.name, self.arity )
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,186 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ module ::Libsql
6
+ ##
7
+ # This is the interface to allow Blob objects to be written to and read from
8
+ # the SQLite database. When using statements, use a Blob object as
9
+ # the wrapper around the source to be written to the row, and a Blob object is
10
+ # returned if the the type mapping warrents during select queries.
11
+ #
12
+ # For instance during an insert:
13
+ #
14
+ # blob_column = db.schema.tables['blobs'].columns['data']
15
+ # db.execute("INSERT INTO blobs(name, data) VALUES ( $name, $blob )",
16
+ # { "$name" => "/path/to/file",
17
+ # "$blob" => ::Libsql::Blob.new( :file => '/path/to/file',
18
+ # :column => blob_column) } )
19
+ #
20
+ # db.execute("INSERT INTO blobs(id, data) VALUES ($id, $blob )",
21
+ # { "$name" => 'blobname',
22
+ # "$blob" => ::Libsql::Blob.new( :io => "something with .read and .length methods",
23
+ # :column => blob_column) } )
24
+ #
25
+ # On select the blob data needs to be read into an IO object
26
+ #
27
+ # all_rows = db.execute("SELECT name, blob FROM blobs WHERE name = '/path/to/file' ")
28
+ # blob_row = all_rows.first
29
+ # blob_row['blob'].write_to_file( blob_row['name'] )
30
+ #
31
+ # Or write to an IO object
32
+ #
33
+ # blob_results = {}
34
+ # db.execute("SELECT name, blob FROM blobs") do |row|
35
+ # io = StringIO.new
36
+ # row['blob'].write_to_io( io )
37
+ # blob_results[row['name']] = io
38
+ # # or use a shortcut
39
+ # # blob_results[row['name']] = row['blob'].to_string_io
40
+ # end
41
+ #
42
+ # If using a Blob as a conditional, for instance in a WHERE clause then the
43
+ # Blob must resolvable to a String.
44
+ #
45
+ # db.execute("SELECT FROM blobs(name, data) WHERE data = $blob",
46
+ # { "$blob' => ::Libsql::Blob.new( :string => "A string of data" ) })
47
+ #
48
+ class Blob
49
+ class Error < ::Libsql::Error; end
50
+ class << self
51
+ def valid_source_params
52
+ @valid_source_params ||= [ :file, :io, :string, :db_blob ]
53
+ end
54
+
55
+ def default_block_size
56
+ @default_block_size ||= 8192
57
+ end
58
+ end
59
+
60
+ # the object representing the source of the blob
61
+ attr_reader :source
62
+
63
+ # the size in bytes of the of the blob
64
+ attr_reader :length
65
+
66
+ # the size in bytes of the blocks of data to move from the source
67
+ attr_reader :block_size
68
+
69
+ # the column the blob is associated with
70
+ attr_reader :column
71
+
72
+ ##
73
+ # Initialize a new blob, it takes a single parameter, a hash which describes
74
+ # the source of the blob. The keys of the hash are one of:
75
+ #
76
+ # :file : the value is the path to a file on the file system
77
+ # :io : the value is an object that responds to the the methods +read+
78
+ # and +length+. +read+ should have the behavior of IO#read
79
+ # :db_blob : not normally used by an end user, used to initialize a blob
80
+ # object that is returned from an SQL query.
81
+ # :string : used when a Blob is part of a WHERE clause or result
82
+ #
83
+ # And additional key of :block_size may be used to indicate the maximum size
84
+ # of a single block of data to move from the source to the destination, this
85
+ # defaults ot 8192.
86
+ #
87
+ def initialize( params )
88
+ if (Blob.valid_source_params & params.keys).size > 1 then
89
+ raise Blob::Error, "Only a one of #{Blob.valid_source_params.join(', ')} is allowed to initialize a Blob. #{params.keys.join(', ')} were sent"
90
+ end
91
+
92
+ @source = nil
93
+ @source_length = 0
94
+ @close_source_after_read = false
95
+ @incremental = true
96
+ @block_size = params[:block_size] || Blob.default_block_size
97
+ @column = params[:column]
98
+
99
+ raise Blob::Error, "A :column parameter is required for a Blob" unless @column or params.has_key?( :string )
100
+
101
+ if params.has_key?( :file ) then
102
+ @source = File.open( params[:file], "r" )
103
+ @length = File.size( params[:file] )
104
+ @close_source_after_read = true
105
+ elsif params.has_key?( :io ) then
106
+ @source = params[:io]
107
+ @length = @source.length
108
+ elsif params.has_key?( :db_blob ) then
109
+ @source = params[:db_blob]
110
+ @length = @source.length
111
+ @close_source_after_read = true
112
+ elsif params.has_key?( :string ) then
113
+ @source = params[:string]
114
+ @length = @source.length
115
+ @incremental = false
116
+ end
117
+ end
118
+
119
+ ##
120
+ # close the source when done reading from it
121
+ #
122
+ def close_source_after_read?
123
+ @close_source_after_read
124
+ end
125
+
126
+ ##
127
+ # is this an incremental Blob or not
128
+ #
129
+ def incremental?
130
+ @incremental
131
+ end
132
+
133
+ ##
134
+ # Write the Blob to an IO object
135
+ #
136
+ def write_to_io( io )
137
+ if source.respond_to?( :read ) then
138
+ while buf = source.read( block_size ) do
139
+ io.write( buf )
140
+ end
141
+ else
142
+ io.write( source.to_s )
143
+ end
144
+
145
+ if close_source_after_read? then
146
+ source.close
147
+ end
148
+ end
149
+
150
+ ##
151
+ # conver the blob to a string
152
+ #
153
+ def to_s
154
+ to_string_io.string
155
+ end
156
+
157
+ ##
158
+ # write the Blob contents to a StringIO
159
+ #
160
+ def to_string_io
161
+ sio = StringIO.new
162
+ write_to_io( sio )
163
+ return sio
164
+ end
165
+
166
+ ##
167
+ # Write the Blob contents to a File.
168
+ #
169
+ def write_to_file( filename, modestring="w" )
170
+ File.open(filename, modestring) do |f|
171
+ write_to_io( f )
172
+ end
173
+ end
174
+
175
+ ##
176
+ # Write the Blob contents to the column. This assumes that the row_id to
177
+ # insert into is the last row that was inserted into the db
178
+ #
179
+ def write_to_column!
180
+ last_rowid = column.schema.db.last_insert_rowid
181
+ SQLite3::Blob.new( column.schema.db.api, column.db, column.table, column.name, last_rowid, "w" ) do |sqlite_blob|
182
+ write_to_io( sqlite_blob )
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ module ::Libsql
6
+ ##
7
+ # Do type conversion on values that could be boolen values into
8
+ # real 'true' or 'false'
9
+ #
10
+ # This is pulled from the possible boolean values from PostgreSQL
11
+ #
12
+ class Boolean
13
+ class << self
14
+ #
15
+ # list of downcased strings are potential true values
16
+ #
17
+ def true_values
18
+ @true_values ||= %w[ true t yes y 1 ]
19
+ end
20
+
21
+ #
22
+ # list of downcased strings are potential false values
23
+ #
24
+ def false_values
25
+ @false_values ||= %w[ false f no n 0 ]
26
+ end
27
+
28
+ #
29
+ # Convert +val+ to a string and attempt to convert it to +true+ or +false+
30
+ #
31
+ def to_bool( val )
32
+ return false if val.nil?
33
+ unless defined? @to_bool
34
+ @to_bool = {}
35
+ true_values.each { |t| @to_bool[t] = true }
36
+ false_values.each { |f| @to_bool[f] = false }
37
+ end
38
+ return @to_bool[val.to_s.downcase]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ module ::Libsql
2
+ ##
3
+ # A base class for use in creating your own busy handler classes
4
+ #
5
+ class BusyHandler
6
+ def to_proc
7
+ self
8
+ end
9
+
10
+ # the arity of the call method
11
+ def arity() 1 ; end
12
+
13
+ ##
14
+ # Override this method, returning +false+ if the SQLite should return
15
+ # SQLITE_BUSY for all parties involved in the lock, and anything else if the
16
+ # lock attempt should be tried again.
17
+ def call( count )
18
+ raise NotImplementedError, "The busy handler call(N) method must be implemented"
19
+ end
20
+ end
21
+
22
+ ##
23
+ # A busy time out class for use in Database#define_busy_handler
24
+ #
25
+ class BusyTimeout < BusyHandler
26
+ attr_reader :call_count
27
+ ##
28
+ # intialize by setting _count_ and _duration_ ( in milliseconds ).
29
+ #
30
+ def initialize( count = 20 , duration = 50 )
31
+ @count = count
32
+ @duration = duration.to_f / 1_000
33
+ @call_count = 0
34
+ end
35
+
36
+ ##
37
+ # return +false+ if _callcount_ is > _count_ otherwise sleep for _duration_
38
+ # milliseconds and then return +true+
39
+ #
40
+ def call( call_count )
41
+ @call_count = call_count
42
+ return false if ( call_count > @count )
43
+ sleep @duration
44
+ return true
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+
6
+ require 'libsql/boolean'
7
+ require 'libsql/blob'
8
+
9
+ module ::Libsql
10
+ ##
11
+ # a class representing the meta information about an SQLite column, this class
12
+ # serves both for general Schema level information, and for result set
13
+ # information from a SELECT query.
14
+ #
15
+ class Column
16
+ # the schema object this column is associated with
17
+ attr_accessor :schema
18
+
19
+ # the database name this column belongs to. This will be 'main' for the main
20
+ # database, 'temp' for the temp database and whatever an attached database
21
+ # was attached as.
22
+ attr_accessor :db
23
+
24
+ # the table to which this column belongs
25
+ attr_accessor :table
26
+
27
+ # the column name
28
+ attr_accessor :name
29
+
30
+ # the default value of the column. This may not have a value and that
31
+ # either means that there is no default value, or one could not be
32
+ # determined.
33
+ #
34
+ attr_accessor :default_value
35
+
36
+ # the declared data type of the column in the original sql that created the
37
+ # column
38
+ attr_accessor :declared_data_type
39
+
40
+ # the collation sequence name of the column
41
+ attr_accessor :collation_sequence_name
42
+
43
+ # The index (starting with 0) of this column in the table definition
44
+ # or result set
45
+ attr_accessor :order
46
+
47
+ ##
48
+ # Create a column with its name and associated table
49
+ #
50
+ def initialize( db, table, name, order)
51
+ @db = db
52
+ @table = table
53
+ @name = name
54
+ @order = Float(order).to_i
55
+ @declared_data_type = nil
56
+ @default_value = nil
57
+ end
58
+
59
+ # true if the column has a default value
60
+ def has_default_value?
61
+ not default_value.nil?
62
+ end
63
+
64
+ # true if the column may have a NULL value
65
+ def nullable?
66
+ @not_null_constraint == false
67
+ end
68
+
69
+ # set whether or not the column has a not null constraint
70
+ def not_null_constraint=( other )
71
+ @not_null_constraint = Boolean.to_bool( other )
72
+ end
73
+
74
+ # true if the column as a NOT NULL constraint
75
+ def not_null_constraint?
76
+ @not_null_constraint
77
+ end
78
+
79
+ # set whether or not the column is a primary key column
80
+ def primary_key=( other )
81
+ @primary_key = Boolean.to_bool( other )
82
+ end
83
+
84
+ # true if the column is a primary key column
85
+ def primary_key?
86
+ @primary_key
87
+ end
88
+
89
+ # set whether or not the column is auto increment
90
+ def auto_increment=( other )
91
+ @auto_increment = Boolean.to_bool( other )
92
+ end
93
+
94
+ # true if the column is auto increment
95
+ def auto_increment?
96
+ @auto_increment
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,75 @@
1
+ if RUBY_VERSION >= "1.9"
2
+ require 'csv'
3
+ else
4
+ require 'fastercsv'
5
+ ::CSV = ::FasterCSV
6
+ end
7
+ module ::Libsql
8
+ ##
9
+ # A class to deal with importing CSV data into a single table in the
10
+ # database.
11
+ #
12
+ class CSVTableImporter
13
+ def initialize( csv_path, database, table_name, options = {} )
14
+ @csv_path = File.expand_path( csv_path )
15
+ @database = database
16
+ @table_name = table_name
17
+ @table = @database.schema.tables[@table_name]
18
+ @options = options.dup
19
+ @encoding = options.delete("encoding") || "UTF-8"
20
+ validate
21
+ end
22
+
23
+ def run
24
+ @database.transaction do |db|
25
+ db.prepare( insert_sql ) do |stmt|
26
+ ::CSV.foreach( @csv_path, "r:#{@encoding}", **@options ) do |row|
27
+ stmt.execute( row )
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ ##
34
+ # The column names of the import table in definiation order
35
+ #
36
+ def column_names
37
+ @table.columns_in_order.collect { |c| c.name }
38
+ end
39
+
40
+ ##
41
+ # The columns used for the insertion. This is either #column_names
42
+ # or the value out of @options[:headers] if that value is an Array
43
+ #
44
+ def insert_column_list
45
+ column_list = self.column_names
46
+ if Array === @options[:headers] then
47
+ column_list = @options[:headers]
48
+ end
49
+ return column_list
50
+ end
51
+
52
+ ##
53
+ # The prepared statement SQL that is used for the import
54
+ #
55
+ def insert_sql
56
+ column_sql = insert_column_list.join(",")
57
+ vars = insert_column_list.collect { |x| "?" }.join(",")
58
+ return "INSERT INTO #{@table_name}(#{column_sql}) VALUES (#{vars})"
59
+ end
60
+
61
+ def table_list
62
+ @database.schema.tables.keys
63
+ end
64
+
65
+ ##
66
+ # validate that the arguments for initialization are valid and that the #run
67
+ # method will probably execute
68
+ #
69
+ def validate
70
+ raise ArgumentError, "CSV file #{@csv_path} does not exist" unless File.exist?( @csv_path )
71
+ raise ArgumentError, "CSV file #{@csv_path} is not readable" unless File.readable?( @csv_path )
72
+ raise ArgumentError, "The table '#{@table_name} is not found in the database. The known tables are #{table_list.sort.join(", ")}" unless @table
73
+ end
74
+ end
75
+ end