libsql 0.1.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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