amalgalite 0.10.1-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.
- data/HISTORY +201 -0
- data/LICENSE +29 -0
- data/README +51 -0
- data/bin/amalgalite-pack +126 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +88 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/gem-db.rb +94 -0
- data/examples/gems.db +0 -0
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +42 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite/amalgalite3.c +290 -0
- data/ext/amalgalite/amalgalite3.h +151 -0
- data/ext/amalgalite/amalgalite3_blob.c +240 -0
- data/ext/amalgalite/amalgalite3_constants.c +221 -0
- data/ext/amalgalite/amalgalite3_database.c +1148 -0
- data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite/amalgalite3_statement.c +639 -0
- data/ext/amalgalite/extconf.rb +36 -0
- data/ext/amalgalite/gen_constants.rb +130 -0
- data/ext/amalgalite/sqlite3.c +106729 -0
- data/ext/amalgalite/sqlite3.h +5626 -0
- data/ext/amalgalite/sqlite3_options.h +4 -0
- data/ext/amalgalite/sqlite3ext.h +380 -0
- data/gemspec.rb +60 -0
- data/lib/amalgalite.rb +43 -0
- data/lib/amalgalite/1.8/amalgalite3.so +0 -0
- data/lib/amalgalite/1.9/amalgalite3.so +0 -0
- data/lib/amalgalite/aggregate.rb +67 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/busy_timeout.rb +47 -0
- data/lib/amalgalite/column.rb +97 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
- data/lib/amalgalite/database.rb +947 -0
- data/lib/amalgalite/function.rb +61 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/packer.rb +226 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +131 -0
- data/lib/amalgalite/progress_handler.rb +21 -0
- data/lib/amalgalite/requires.rb +120 -0
- data/lib/amalgalite/schema.rb +191 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +80 -0
- data/lib/amalgalite/sqlite3/database/function.rb +48 -0
- data/lib/amalgalite/sqlite3/database/status.rb +68 -0
- data/lib/amalgalite/sqlite3/status.rb +60 -0
- data/lib/amalgalite/sqlite3/version.rb +37 -0
- data/lib/amalgalite/statement.rb +414 -0
- data/lib/amalgalite/table.rb +90 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +40 -0
- data/lib/amalgalite/type_maps/text_map.rb +22 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/spec/aggregate_spec.rb +169 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/busy_handler.rb +165 -0
- data/spec/database_spec.rb +494 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/function_spec.rb +94 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/packer_spec.rb +60 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +105 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/rtree_spec.rb +71 -0
- data/spec/schema_spec.rb +120 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +161 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +15 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +77 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/extension.rake +141 -0
- data/tasks/rspec.rake +33 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +237 -0
Binary file
|
Binary file
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'amalgalite/sqlite3/database/function'
|
2
|
+
module Amalgalite
|
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
|
+
# * set the @arity data member
|
17
|
+
# * set the @name data member
|
18
|
+
# * implement _step_ with arity of +@arity+
|
19
|
+
# * implement _finalize_ with arity of 0
|
20
|
+
#
|
21
|
+
# For instance to implement a <i>unique_word_count(X)</i> aggregate function you could
|
22
|
+
# implement it as:
|
23
|
+
#
|
24
|
+
# class UniqueWordCount < ::Amalgalite::Aggregate
|
25
|
+
# attr_accessor :words
|
26
|
+
#
|
27
|
+
# def initialize
|
28
|
+
# @name = 'unique_word_count'
|
29
|
+
# @arity = 1
|
30
|
+
# @words = Hash.new { |h,k| h[k] = 0 }
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def step( str )
|
34
|
+
# str.split(/\W+/).each do |word|
|
35
|
+
# words[ word.downcase ] += 1
|
36
|
+
# end
|
37
|
+
# return nil
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# def finalize
|
41
|
+
# return words.size
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
#
|
46
|
+
class Aggregate
|
47
|
+
# The name of the SQL function
|
48
|
+
attr_accessor :name
|
49
|
+
|
50
|
+
# The arity of the SQL function
|
51
|
+
attr_accessor :arity
|
52
|
+
|
53
|
+
# finalize should return the final value of the aggregate function
|
54
|
+
def finalize
|
55
|
+
raise NotImplementedError, "Aggregate#finalize must be implemented"
|
56
|
+
end
|
57
|
+
|
58
|
+
# <b>Do Not Override</b>
|
59
|
+
#
|
60
|
+
# The function signature for use by the Amaglaite datase in tracking
|
61
|
+
# function creation.
|
62
|
+
#
|
63
|
+
def signature
|
64
|
+
@signature ||= ::Amalgalite::SQLite3::Database::Function.signature( self.name, self.arity )
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module Amalgalite
|
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" => Amalgalite::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" => Amalgalite::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' => Amalgalite::Blob.new( :string => "A string of data" ) })
|
47
|
+
#
|
48
|
+
class Blob
|
49
|
+
class Error < ::Amalgalite::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) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module Amalgalite
|
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 Amalgalite
|
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,97 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'amalgalite/boolean'
|
7
|
+
require 'amalgalite/blob'
|
8
|
+
|
9
|
+
module Amalgalite
|
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
|
20
|
+
attr_accessor :db
|
21
|
+
|
22
|
+
# the column name
|
23
|
+
attr_accessor :name
|
24
|
+
|
25
|
+
# the table to which this column belongs
|
26
|
+
attr_accessor :table
|
27
|
+
|
28
|
+
# the default value of the column. This may not have a value and that
|
29
|
+
# either means that there is no default value, or one could not be
|
30
|
+
# determined.
|
31
|
+
#
|
32
|
+
attr_accessor :default_value
|
33
|
+
|
34
|
+
# the declared data type of the column in the original sql that created the
|
35
|
+
# column
|
36
|
+
attr_accessor :declared_data_type
|
37
|
+
|
38
|
+
# the collation sequence name of the column
|
39
|
+
attr_accessor :collation_sequence_name
|
40
|
+
|
41
|
+
# The index (starting with 0) of this column in the table definition
|
42
|
+
# or result set
|
43
|
+
attr_accessor :order
|
44
|
+
|
45
|
+
##
|
46
|
+
# Create a column with its name and associated table
|
47
|
+
#
|
48
|
+
def initialize( db, table, name, order)
|
49
|
+
@db = db
|
50
|
+
@name = name
|
51
|
+
@table = table
|
52
|
+
@order = Float(order).to_i
|
53
|
+
@declared_data_type = nil
|
54
|
+
@default_value = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# true if the column has a default value
|
58
|
+
def has_default_value?
|
59
|
+
not default_value.nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
# true if the column may have a NULL value
|
63
|
+
def nullable?
|
64
|
+
@not_null_constraint == false
|
65
|
+
end
|
66
|
+
|
67
|
+
# set whether or not the column has a not null constraint
|
68
|
+
def not_null_constraint=( other )
|
69
|
+
@not_null_constraint = Boolean.to_bool( other )
|
70
|
+
end
|
71
|
+
|
72
|
+
# true if the column as a NOT NULL constraint
|
73
|
+
def not_null_constraint?
|
74
|
+
@not_null_constraint
|
75
|
+
end
|
76
|
+
|
77
|
+
# set whether or not the column is a primary key column
|
78
|
+
def primary_key=( other )
|
79
|
+
@primary_key = Boolean.to_bool( other )
|
80
|
+
end
|
81
|
+
|
82
|
+
# true if the column is a primary key column
|
83
|
+
def primary_key?
|
84
|
+
@primary_key
|
85
|
+
end
|
86
|
+
|
87
|
+
# set whether or not the column is auto increment
|
88
|
+
def auto_increment=( other )
|
89
|
+
@auto_increment = Boolean.to_bool( other )
|
90
|
+
end
|
91
|
+
|
92
|
+
# true if the column is auto increment
|
93
|
+
def auto_increment?
|
94
|
+
@auto_increment
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|