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.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +60 -0
- data/HISTORY.md +6 -0
- data/LICENSE +31 -0
- data/Manifest.txt +96 -0
- data/README.md +59 -0
- data/Rakefile +28 -0
- data/TODO.md +57 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +106 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/fts5.rb +152 -0
- data/examples/gem-db.rb +94 -0
- data/examples/schema-info.rb +34 -0
- data/ext/libsql/c/extconf.rb +86 -0
- data/ext/libsql/c/gen_constants.rb +353 -0
- data/ext/libsql/c/libsql_blob.c +240 -0
- data/ext/libsql/c/libsql_constants.c +1518 -0
- data/ext/libsql/c/libsql_database.c +1188 -0
- data/ext/libsql/c/libsql_ext.c +383 -0
- data/ext/libsql/c/libsql_ext.h +149 -0
- data/ext/libsql/c/libsql_statement.c +649 -0
- data/ext/libsql/c/notes.txt +134 -0
- data/ext/libsql/c/sqlite3.c +247030 -0
- data/ext/libsql/c/sqlite3.h +13436 -0
- data/lib/libsql/3.0/libsql_ext.so +0 -0
- data/lib/libsql/3.1/libsql_ext.so +0 -0
- data/lib/libsql/3.2/libsql_ext.so +0 -0
- data/lib/libsql/aggregate.rb +73 -0
- data/lib/libsql/blob.rb +186 -0
- data/lib/libsql/boolean.rb +42 -0
- data/lib/libsql/busy_timeout.rb +47 -0
- data/lib/libsql/column.rb +99 -0
- data/lib/libsql/csv_table_importer.rb +75 -0
- data/lib/libsql/database.rb +933 -0
- data/lib/libsql/function.rb +61 -0
- data/lib/libsql/index.rb +43 -0
- data/lib/libsql/memory_database.rb +15 -0
- data/lib/libsql/paths.rb +80 -0
- data/lib/libsql/profile_tap.rb +131 -0
- data/lib/libsql/progress_handler.rb +21 -0
- data/lib/libsql/schema.rb +225 -0
- data/lib/libsql/sqlite3/constants.rb +95 -0
- data/lib/libsql/sqlite3/database/function.rb +48 -0
- data/lib/libsql/sqlite3/database/status.rb +68 -0
- data/lib/libsql/sqlite3/libsql_version.rb +32 -0
- data/lib/libsql/sqlite3/status.rb +60 -0
- data/lib/libsql/sqlite3/version.rb +55 -0
- data/lib/libsql/sqlite3.rb +7 -0
- data/lib/libsql/statement.rb +421 -0
- data/lib/libsql/table.rb +91 -0
- data/lib/libsql/taps/console.rb +27 -0
- data/lib/libsql/taps/io.rb +74 -0
- data/lib/libsql/taps.rb +2 -0
- data/lib/libsql/trace_tap.rb +35 -0
- data/lib/libsql/type_map.rb +63 -0
- data/lib/libsql/type_maps/default_map.rb +166 -0
- data/lib/libsql/type_maps/storage_map.rb +38 -0
- data/lib/libsql/type_maps/text_map.rb +21 -0
- data/lib/libsql/version.rb +8 -0
- data/lib/libsql/view.rb +26 -0
- data/lib/libsql-ruby.rb +1 -0
- data/lib/libsql.rb +51 -0
- data/spec/aggregate_spec.rb +158 -0
- data/spec/blob_spec.rb +78 -0
- data/spec/boolean_spec.rb +24 -0
- data/spec/busy_handler.rb +157 -0
- data/spec/data/iso-3166-country.txt +242 -0
- data/spec/data/iso-3166-schema.sql +22 -0
- data/spec/data/iso-3166-subcountry.txt +3995 -0
- data/spec/data/make-iso-db.sh +12 -0
- data/spec/database_spec.rb +505 -0
- data/spec/default_map_spec.rb +92 -0
- data/spec/function_spec.rb +78 -0
- data/spec/integeration_spec.rb +97 -0
- data/spec/iso_3166_database.rb +58 -0
- data/spec/json_spec.rb +24 -0
- data/spec/libsql_spec.rb +4 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +91 -0
- data/spec/rtree_spec.rb +66 -0
- data/spec/schema_spec.rb +131 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/sqlite3/constants_spec.rb +108 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/libsql_version_spec.rb +16 -0
- data/spec/sqlite3/status_spec.rb +22 -0
- data/spec/sqlite3/version_spec.rb +28 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +168 -0
- data/spec/storage_map_spec.rb +38 -0
- data/spec/tap_spec.rb +57 -0
- data/spec/text_map_spec.rb +20 -0
- data/spec/type_map_spec.rb +14 -0
- data/spec/version_spec.rb +8 -0
- data/tasks/custom.rake +134 -0
- data/tasks/default.rake +257 -0
- data/tasks/extension.rake +29 -0
- data/tasks/this.rb +208 -0
- metadata +330 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
module ::Libsql::SQLite3
|
2
|
+
class Database
|
3
|
+
##
|
4
|
+
# A wrapper around a proc for use as an SQLite Ddatabase fuction
|
5
|
+
#
|
6
|
+
# f = Function.new( 'md5', lambda { |x| Digest::MD5.hexdigest( x.to_s ) } )
|
7
|
+
#
|
8
|
+
class Function
|
9
|
+
|
10
|
+
# the name of the function, and how it will be called in SQL
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# The unique signature of this function. This is used to determin if the
|
14
|
+
# function is already registered or not
|
15
|
+
#
|
16
|
+
def self.signature( name, arity )
|
17
|
+
"#{name}/#{arity}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Initialize with the name and the Proc
|
21
|
+
#
|
22
|
+
def initialize( name, _proc )
|
23
|
+
@name = name
|
24
|
+
@function = _proc
|
25
|
+
end
|
26
|
+
|
27
|
+
# The unique signature of this function
|
28
|
+
#
|
29
|
+
def signature
|
30
|
+
@signature ||= Function.signature( name, arity )
|
31
|
+
end
|
32
|
+
alias :to_s :signature
|
33
|
+
|
34
|
+
# The arity of SQL function, -1 means it is takes a variable number of
|
35
|
+
# arguments.
|
36
|
+
#
|
37
|
+
def arity
|
38
|
+
@function.arity
|
39
|
+
end
|
40
|
+
|
41
|
+
# Invoke the proc
|
42
|
+
#
|
43
|
+
def call( *args )
|
44
|
+
@function.call( *args )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'libsql/sqlite3/constants'
|
2
|
+
module ::Libsql::SQLite3
|
3
|
+
class Database
|
4
|
+
#
|
5
|
+
# A Stat represents a single Database Status code and its current highwater mark.
|
6
|
+
#
|
7
|
+
# Some stats may not have a current or a highwater value, in those cases
|
8
|
+
# the associated _has_current?_ or _has_highwater?_ method returns false and the
|
9
|
+
# _current_ or _highwater_ method also returns +nil+.
|
10
|
+
#
|
11
|
+
class Stat
|
12
|
+
attr_reader :name
|
13
|
+
attr_reader :code
|
14
|
+
|
15
|
+
def initialize( api_db, name )
|
16
|
+
@name = name
|
17
|
+
@code = ::Libsql::SQLite3::Constants::DBStatus.value_from_name( name )
|
18
|
+
@current = nil
|
19
|
+
@highwater = nil
|
20
|
+
@api_db = api_db
|
21
|
+
end
|
22
|
+
|
23
|
+
def current
|
24
|
+
update!
|
25
|
+
return @current
|
26
|
+
end
|
27
|
+
|
28
|
+
def highwater
|
29
|
+
update!
|
30
|
+
return @highwater
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# reset the given stat's highwater mark. This will also populate the
|
35
|
+
# _@current_ and _@highwater_ instance variables
|
36
|
+
#
|
37
|
+
def reset!
|
38
|
+
update!( true )
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Top level Status object holding all the Stat objects indicating the DBStatus
|
44
|
+
# of the SQLite3 C library.
|
45
|
+
#
|
46
|
+
class DBStatus
|
47
|
+
::Libsql::SQLite3::Constants::DBStatus.constants.each do |const_name|
|
48
|
+
method_name = const_name.downcase
|
49
|
+
module_eval( <<-code, __FILE__, __LINE__ )
|
50
|
+
def #{method_name}
|
51
|
+
@#{method_name} ||= ::Libsql::SQLite3::Database::Stat.new( self.api_db, '#{method_name}' )
|
52
|
+
end
|
53
|
+
code
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :api_db
|
57
|
+
|
58
|
+
def initialize( api_db )
|
59
|
+
@api_db = api_db
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# return the DBstatus object for the sqlite database
|
64
|
+
def status
|
65
|
+
@status ||= DBStatus.new( self )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2023 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module ::Libsql
|
6
|
+
module SQLite3
|
7
|
+
module LibsqlVersion
|
8
|
+
def self.compiled_matches_runtime?
|
9
|
+
self.compiled_version == self.runtime_version
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Version of libsql that ships with
|
14
|
+
LIBSQL_VERSION = LibsqlVersion.to_s.freeze
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
unless ::Libsql::SQLite3::LibsqlVersion.compiled_matches_runtime? then
|
19
|
+
warn <<eom
|
20
|
+
You are seeing something odd. The compiled version of libsql that
|
21
|
+
is embedded in this extension is for some reason, not being used.
|
22
|
+
The version in the extension is #{::Libsql::SQLite3::LibsqlVersion.compiled_version} and the version that
|
23
|
+
as been loaded as a shared library is #{::Libsql::SQLite3::LibsqlVersion.runtime_version}. These versions
|
24
|
+
should be the same, but they are not.
|
25
|
+
|
26
|
+
One known issue is if you are using this libary in conjunction with
|
27
|
+
Hitimes on Mac OS X. You should make sure that "require 'libsql'"
|
28
|
+
appears before "require 'hitimes'" in your ruby code.
|
29
|
+
|
30
|
+
This is a non-trivial problem, and I am working on it.
|
31
|
+
eom
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'libsql/sqlite3/constants'
|
2
|
+
module ::Libsql::SQLite3
|
3
|
+
|
4
|
+
#
|
5
|
+
# A Stat represents a single Status code and its current highwater mark.
|
6
|
+
#
|
7
|
+
# Some stats may not have a current or a highwater value, in those cases
|
8
|
+
# the associated _has_current?_ or _has_highwater?_ method returns false and the
|
9
|
+
# _current_ or _highwater_ method also returns +nil+.
|
10
|
+
#
|
11
|
+
class Stat
|
12
|
+
attr_reader :name
|
13
|
+
attr_reader :code
|
14
|
+
|
15
|
+
def initialize( name )
|
16
|
+
@name = name
|
17
|
+
@code = ::Libsql::SQLite3::Constants::Status.value_from_name( name )
|
18
|
+
@current = nil
|
19
|
+
@highwater = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def current
|
23
|
+
update!
|
24
|
+
return @current
|
25
|
+
end
|
26
|
+
|
27
|
+
def highwater
|
28
|
+
update!
|
29
|
+
return @highwater
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# reset the given stat's highwater mark. This will also populate the
|
34
|
+
# _@current_ and _@highwater_ instance variables
|
35
|
+
#
|
36
|
+
def reset!
|
37
|
+
update!( true )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Top level Status object holding all the Stat objects indicating the Status
|
43
|
+
# of the SQLite3 C library.
|
44
|
+
#
|
45
|
+
class Status
|
46
|
+
::Libsql::SQLite3::Constants::Status.constants.each do |const_name|
|
47
|
+
method_name = const_name.downcase
|
48
|
+
module_eval( <<-code, __FILE__, __LINE__ )
|
49
|
+
def #{method_name}
|
50
|
+
@#{method_name} ||= ::Libsql::SQLite3::Stat.new( '#{method_name}' )
|
51
|
+
end
|
52
|
+
code
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# return the status object for the sqlite database
|
57
|
+
def self.status
|
58
|
+
@status ||= Status.new
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2023 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module ::Libsql
|
6
|
+
module SQLite3
|
7
|
+
module Version
|
8
|
+
# Sqlite3 version number is equal to
|
9
|
+
# MAJOR * 1_000_000 + MINOR * 1_000 + RELEASE
|
10
|
+
|
11
|
+
# major version number of the SQLite C library
|
12
|
+
MAJOR = (to_i / 1_000_000).freeze
|
13
|
+
|
14
|
+
# minor version number of the SQLite C library
|
15
|
+
MINOR = ((to_i % 1_000_000) / 1_000).freeze
|
16
|
+
|
17
|
+
# release version number of the SQLite C library
|
18
|
+
RELEASE = (to_i % 1_000).freeze
|
19
|
+
|
20
|
+
#
|
21
|
+
# call-seq:
|
22
|
+
# ::Libsql::SQLite3::Version.to_a -> [ MAJOR, MINOR, RELEASE ]
|
23
|
+
#
|
24
|
+
# Return the SQLite C library version number as an array of MAJOR, MINOR,
|
25
|
+
# RELEASE
|
26
|
+
#
|
27
|
+
def self.to_a
|
28
|
+
[ MAJOR, MINOR, RELEASE ]
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.compiled_matches_runtime?
|
32
|
+
self.compiled_version == self.runtime_version
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Version of SQLite that ships with ::Libsql
|
37
|
+
VERSION = Version.to_s.freeze
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
unless ::Libsql::SQLite3::Version.compiled_matches_runtime? then
|
42
|
+
warn <<eom
|
43
|
+
You are seeing something odd. The compiled version of SQLite that
|
44
|
+
is embedded in this extension is for some reason, not being used.
|
45
|
+
The version in the extension is #{::Libsql::SQLite3::Version.compiled_version} and the version that
|
46
|
+
as been loaded as a shared library is #{::Libsql::SQLite3::Version.runtime_version}. These versions
|
47
|
+
should be the same, but they are not.
|
48
|
+
|
49
|
+
One known issue is if you are using this libary in conjunction with
|
50
|
+
Hitimes on Mac OS X. You should make sure that "require 'libsql'"
|
51
|
+
appears before "require 'hitimes'" in your ruby code.
|
52
|
+
|
53
|
+
This is a non-trivial problem, and I am working on it.
|
54
|
+
eom
|
55
|
+
end
|
@@ -0,0 +1,421 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2023 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
#
|
6
|
+
require 'date'
|
7
|
+
require 'arrayfields'
|
8
|
+
require 'ostruct'
|
9
|
+
|
10
|
+
module ::Libsql
|
11
|
+
class Statement
|
12
|
+
|
13
|
+
include ::Libsql::SQLite3::Constants
|
14
|
+
|
15
|
+
attr_reader :db
|
16
|
+
attr_reader :api
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# special column names that indicate that indicate the column is a rowid
|
20
|
+
def rowid_column_names
|
21
|
+
@rowid_column_names ||= %w[ ROWID OID _ROWID_ ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Initialize a new statement on the database.
|
27
|
+
#
|
28
|
+
def initialize( db, sql )
|
29
|
+
@db = db
|
30
|
+
#prepare_method = @db.utf16? ? :prepare16 : :prepare
|
31
|
+
prepare_method = :prepare
|
32
|
+
@param_positions = {}
|
33
|
+
@stmt_api = @db.api.send( prepare_method, sql )
|
34
|
+
@blobs_to_write = []
|
35
|
+
@rowid_index = nil
|
36
|
+
@result_meta = nil
|
37
|
+
@open = true
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# is the statement open for business
|
42
|
+
#
|
43
|
+
def open?
|
44
|
+
@open
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Is the special column "ROWID", "OID", or "_ROWID_" used?
|
49
|
+
#
|
50
|
+
def using_rowid_column?
|
51
|
+
not @rowid_index.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# reset the Statement back to it state right after the constructor returned,
|
56
|
+
# except if any variables have been bound to parameters, those are still
|
57
|
+
# bound.
|
58
|
+
#
|
59
|
+
def reset!
|
60
|
+
@stmt_api.reset!
|
61
|
+
@param_positions = {}
|
62
|
+
@blobs_to_write.clear
|
63
|
+
@rowid_index = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# reset the Statement back to it state right after the constructor returned,
|
68
|
+
# AND clear all parameter bindings.
|
69
|
+
#
|
70
|
+
def reset_and_clear_bindings!
|
71
|
+
reset!
|
72
|
+
@stmt_api.clear_bindings!
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# reset the statment in preparation for executing it again
|
77
|
+
#
|
78
|
+
def reset_for_next_execute!
|
79
|
+
@stmt_api.reset!
|
80
|
+
@stmt_api.clear_bindings!
|
81
|
+
@blobs_to_write.clear
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Execute the statement with the given parameters
|
86
|
+
#
|
87
|
+
# If a block is given, then yield each returned row to the block. If no
|
88
|
+
# block is given then return all rows from the result. No matter what the
|
89
|
+
# prepared statement should be reset before returning the final time.
|
90
|
+
#
|
91
|
+
def execute( *params )
|
92
|
+
bind( *params )
|
93
|
+
begin
|
94
|
+
# save the error state at the beginning of the execution. We only want to
|
95
|
+
# reraise the error if it was raised during this execution.
|
96
|
+
s_before = $!
|
97
|
+
if block_given? then
|
98
|
+
while row = next_row
|
99
|
+
yield row
|
100
|
+
end
|
101
|
+
else
|
102
|
+
all_rows
|
103
|
+
end
|
104
|
+
ensure
|
105
|
+
s = $!
|
106
|
+
begin
|
107
|
+
reset_for_next_execute!
|
108
|
+
rescue
|
109
|
+
# rescuing nothing on purpose
|
110
|
+
end
|
111
|
+
raise s if s != s_before
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Bind parameters to the sql statement.
|
117
|
+
#
|
118
|
+
# Bindings in SQLite can have a number of formats:
|
119
|
+
#
|
120
|
+
# ?
|
121
|
+
# ?num
|
122
|
+
# :var
|
123
|
+
# @var
|
124
|
+
# $var
|
125
|
+
#
|
126
|
+
# Where 'num' is an Integer and 'var'is an alphanumerical variable.
|
127
|
+
# They may exist in the SQL for which this Statement was created.
|
128
|
+
#
|
129
|
+
# ::Libsql binds parameters to these variables in the following manner:
|
130
|
+
#
|
131
|
+
# If bind is passed in an Array, either as +bind( "foo", "bar", "baz")+ or
|
132
|
+
# as bind( ["foo", "bar", "baz"] ) then each of the params is assumed to be
|
133
|
+
# positionally bound to the statement( ?, ?num ).
|
134
|
+
#
|
135
|
+
# If bind is passed a Hash, either as +bind( :foo => 1, :bar => 'sqlite' )+
|
136
|
+
# or as bind( { :foo => 1, 'bar' => 'sqlite' }) then it is assumed that each
|
137
|
+
# parameter should be bound as a named parameter (:var, @var, $var).
|
138
|
+
#
|
139
|
+
# If bind is not passed any parameters, or nil, then nothing happens.
|
140
|
+
#
|
141
|
+
def bind( *params )
|
142
|
+
if params.nil? or params.empty? then
|
143
|
+
check_parameter_count!( 0 )
|
144
|
+
return nil
|
145
|
+
end
|
146
|
+
|
147
|
+
if params.first.instance_of?( Hash ) then
|
148
|
+
bind_named_parameters( params.first )
|
149
|
+
elsif params.first.instance_of?( Array ) then
|
150
|
+
bind_positional_parameters( *params )
|
151
|
+
else
|
152
|
+
bind_positional_parameters( params )
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Bind parameters to the statement based upon named parameters
|
158
|
+
#
|
159
|
+
def bind_named_parameters( params )
|
160
|
+
check_parameter_count!( params.size )
|
161
|
+
params.each_pair do | param, value |
|
162
|
+
position = param_position_of( param )
|
163
|
+
if position > 0 then
|
164
|
+
bind_parameter_to( position, value )
|
165
|
+
else
|
166
|
+
raise ::Libsql::Error, "Unable to find parameter '#{param}' in SQL statement [#{sql}]"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
##
|
172
|
+
# Bind parameters to the statements based upon positions.
|
173
|
+
#
|
174
|
+
def bind_positional_parameters( params )
|
175
|
+
check_parameter_count!( params.size )
|
176
|
+
params.each_with_index do |value, index|
|
177
|
+
position = index + 1
|
178
|
+
bind_parameter_to( position, value )
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# bind a single parameter to a particular position
|
184
|
+
#
|
185
|
+
def bind_parameter_to( position, value )
|
186
|
+
bind_type = db.type_map.bind_type_of( value )
|
187
|
+
case bind_type
|
188
|
+
when DataType::FLOAT
|
189
|
+
@stmt_api.bind_double( position, value )
|
190
|
+
when DataType::INTEGER
|
191
|
+
@stmt_api.bind_int64( position, value )
|
192
|
+
when DataType::NULL
|
193
|
+
@stmt_api.bind_null( position )
|
194
|
+
when DataType::TEXT
|
195
|
+
@stmt_api.bind_text( position, value.to_s )
|
196
|
+
when DataType::BLOB
|
197
|
+
if value.incremental? then
|
198
|
+
@stmt_api.bind_zeroblob( position, value.length )
|
199
|
+
@blobs_to_write << value
|
200
|
+
else
|
201
|
+
@stmt_api.bind_blob( position, value.source )
|
202
|
+
end
|
203
|
+
else
|
204
|
+
raise ::Libsql::Error, "Unknown binding type of #{bind_type} from #{db.type_map.class.name}.bind_type_of"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
##
|
210
|
+
# Find and cache the binding parameter indexes
|
211
|
+
#
|
212
|
+
def param_position_of( name )
|
213
|
+
ns = name.to_s
|
214
|
+
unless pos = @param_positions[ns]
|
215
|
+
pos = @param_positions[ns] = @stmt_api.parameter_index( ns )
|
216
|
+
end
|
217
|
+
return pos
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# Check and make sure that the number of parameters aligns with the number
|
222
|
+
# that sqlite expects
|
223
|
+
#
|
224
|
+
def check_parameter_count!( num )
|
225
|
+
expected = @stmt_api.parameter_count
|
226
|
+
if num != expected then
|
227
|
+
raise ::Libsql::Error, "#{sql} has #{expected} parameters, but #{num} were passed to bind."
|
228
|
+
end
|
229
|
+
return expected
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# Write any blobs that have been bound to parameters to the database. This
|
234
|
+
# assumes that the blobs go into the last inserted row
|
235
|
+
#
|
236
|
+
def write_blobs
|
237
|
+
unless @blobs_to_write.empty?
|
238
|
+
@blobs_to_write.each do |blob|
|
239
|
+
blob.write_to_column!
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
##
|
245
|
+
# Iterate over the results of the statement returning each row of results
|
246
|
+
# as a hash by +column_name+. The column names are the value after an
|
247
|
+
# 'AS' in the query or default chosen by sqlite.
|
248
|
+
#
|
249
|
+
def each
|
250
|
+
while row = next_row
|
251
|
+
yield row
|
252
|
+
end
|
253
|
+
return self
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# Return the next row of data, with type conversion as indicated by the
|
258
|
+
# Database#type_map
|
259
|
+
#
|
260
|
+
def next_row
|
261
|
+
row = []
|
262
|
+
case rc = @stmt_api.step
|
263
|
+
when ResultCode::ROW
|
264
|
+
result_meta.each_with_index do |col, idx|
|
265
|
+
value = nil
|
266
|
+
column_type = @stmt_api.column_type( idx )
|
267
|
+
case column_type
|
268
|
+
when DataType::TEXT
|
269
|
+
value = @stmt_api.column_text( idx )
|
270
|
+
when DataType::FLOAT
|
271
|
+
value = @stmt_api.column_double( idx )
|
272
|
+
when DataType::INTEGER
|
273
|
+
value = @stmt_api.column_int64( idx )
|
274
|
+
when DataType::NULL
|
275
|
+
value = nil
|
276
|
+
when DataType::BLOB
|
277
|
+
# if the rowid column is encountered, then we can use an incremental
|
278
|
+
# blob api, otherwise we have to use the all at once version.
|
279
|
+
if using_rowid_column? then
|
280
|
+
value = ::Libsql::Blob.new( :db_blob => SQLite3::Blob.new( db.api,
|
281
|
+
col.schema.db,
|
282
|
+
col.schema.table,
|
283
|
+
col.schema.name,
|
284
|
+
@stmt_api.column_int64( @rowid_index ),
|
285
|
+
"r"),
|
286
|
+
:column => col.schema)
|
287
|
+
else
|
288
|
+
value = ::Libsql::Blob.new( :string => @stmt_api.column_blob( idx ), :column => col.schema )
|
289
|
+
end
|
290
|
+
else
|
291
|
+
raise ::Libsql::Error, "BUG! : Unknown SQLite column type of #{column_type}"
|
292
|
+
end
|
293
|
+
|
294
|
+
row << db.type_map.result_value_of( col.schema.declared_data_type, value )
|
295
|
+
end
|
296
|
+
row.fields = result_fields
|
297
|
+
when ResultCode::DONE
|
298
|
+
row = nil
|
299
|
+
write_blobs
|
300
|
+
else
|
301
|
+
self.close # must close so that the error message is guaranteed to be pushed into the database handler
|
302
|
+
# and we can can call last_error_message on it
|
303
|
+
msg = "SQLITE ERROR #{rc} (#{::Libsql::SQLite3::Constants::ResultCode.name_from_value( rc )}) : #{@db.api.last_error_message}"
|
304
|
+
raise ::Libsql::SQLite3::Error, msg
|
305
|
+
end
|
306
|
+
return row
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# Return all rows from the statement as one array
|
311
|
+
#
|
312
|
+
def all_rows
|
313
|
+
rows = []
|
314
|
+
while row = next_row
|
315
|
+
rows << row
|
316
|
+
end
|
317
|
+
return rows
|
318
|
+
end
|
319
|
+
|
320
|
+
##
|
321
|
+
# Inspect the statement and gather all the meta information about the
|
322
|
+
# results, include the name of the column result column and the origin
|
323
|
+
# column. The origin column is the original database.table.column the value
|
324
|
+
# comes from.
|
325
|
+
#
|
326
|
+
# The full meta information from the origin column is also obtained for help
|
327
|
+
# in doing type conversion.
|
328
|
+
#
|
329
|
+
# As iteration over the row meta informatio happens, record if the special
|
330
|
+
# "ROWID", "OID", or "_ROWID_" column is encountered. If that column is
|
331
|
+
# encountered then we make note of it.
|
332
|
+
#
|
333
|
+
def result_meta
|
334
|
+
unless @result_meta
|
335
|
+
meta = []
|
336
|
+
column_count.times do |idx|
|
337
|
+
column_meta = ::OpenStruct.new
|
338
|
+
column_meta.name = @stmt_api.column_name( idx )
|
339
|
+
|
340
|
+
db_name = @stmt_api.column_database_name( idx )
|
341
|
+
tbl_name = @stmt_api.column_table_name( idx )
|
342
|
+
col_name = @stmt_api.column_origin_name( idx )
|
343
|
+
|
344
|
+
column_meta.schema = ::Libsql::Column.new( db_name, tbl_name, col_name, idx )
|
345
|
+
column_meta.schema.declared_data_type = @stmt_api.column_declared_type( idx )
|
346
|
+
|
347
|
+
# only check for rowid if we have a table name and it is not one of the
|
348
|
+
# sqlite_master tables. We could get recursion in those cases.
|
349
|
+
if not using_rowid_column? and tbl_name and
|
350
|
+
not %w[ sqlite_master sqlite_temp_master ].include?( tbl_name ) and is_column_rowid?( tbl_name, col_name ) then
|
351
|
+
@rowid_index = idx
|
352
|
+
end
|
353
|
+
|
354
|
+
meta << column_meta
|
355
|
+
end
|
356
|
+
|
357
|
+
@result_meta = meta
|
358
|
+
end
|
359
|
+
return @result_meta
|
360
|
+
end
|
361
|
+
|
362
|
+
##
|
363
|
+
# is the column indicated by the Column a 'rowid' column
|
364
|
+
#
|
365
|
+
def is_column_rowid?( table_name, column_name )
|
366
|
+
table_schema = @db.schema.tables[table_name]
|
367
|
+
return false unless table_schema
|
368
|
+
|
369
|
+
column_schema = table_schema.columns[column_name]
|
370
|
+
if column_schema then
|
371
|
+
if column_schema.primary_key? and column_schema.declared_data_type and column_schema.declared_data_type.upcase == "INTEGER" then
|
372
|
+
return true
|
373
|
+
end
|
374
|
+
else
|
375
|
+
return true if Statement.rowid_column_names.include?( column_name.upcase )
|
376
|
+
end
|
377
|
+
return false
|
378
|
+
end
|
379
|
+
|
380
|
+
##
|
381
|
+
# Return the array of field names for the result set, the field names are
|
382
|
+
# all strings
|
383
|
+
#
|
384
|
+
def result_fields
|
385
|
+
@fields ||= result_meta.collect { |m| m.name }
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# Return any unsued SQL from the statement
|
390
|
+
#
|
391
|
+
def remaining_sql
|
392
|
+
@stmt_api.remaining_sql
|
393
|
+
end
|
394
|
+
|
395
|
+
|
396
|
+
##
|
397
|
+
# return the number of columns in the result of this query
|
398
|
+
#
|
399
|
+
def column_count
|
400
|
+
@stmt_api.column_count
|
401
|
+
end
|
402
|
+
|
403
|
+
##
|
404
|
+
# return the raw sql that was originally used to prepare the statement
|
405
|
+
#
|
406
|
+
def sql
|
407
|
+
@stmt_api.sql
|
408
|
+
end
|
409
|
+
|
410
|
+
##
|
411
|
+
# Close the statement. The statement is no longer valid for use after it
|
412
|
+
# has been closed.
|
413
|
+
#
|
414
|
+
def close
|
415
|
+
if open? then
|
416
|
+
@stmt_api.close
|
417
|
+
@open = false
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|