amalgalite 1.6.0-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +49 -0
  3. data/HISTORY.md +346 -0
  4. data/LICENSE +31 -0
  5. data/Manifest.txt +104 -0
  6. data/README.md +65 -0
  7. data/Rakefile +26 -0
  8. data/TODO.md +57 -0
  9. data/bin/amalgalite-pack +147 -0
  10. data/examples/a.rb +9 -0
  11. data/examples/blob.rb +88 -0
  12. data/examples/bootstrap.rb +36 -0
  13. data/examples/define_aggregate.rb +75 -0
  14. data/examples/define_function.rb +104 -0
  15. data/examples/fts5.rb +152 -0
  16. data/examples/gem-db.rb +94 -0
  17. data/examples/require_me.rb +11 -0
  18. data/examples/requires.rb +42 -0
  19. data/examples/schema-info.rb +34 -0
  20. data/ext/amalgalite/c/amalgalite.c +355 -0
  21. data/ext/amalgalite/c/amalgalite.h +151 -0
  22. data/ext/amalgalite/c/amalgalite_blob.c +240 -0
  23. data/ext/amalgalite/c/amalgalite_constants.c +1226 -0
  24. data/ext/amalgalite/c/amalgalite_database.c +1178 -0
  25. data/ext/amalgalite/c/amalgalite_requires_bootstrap.c +282 -0
  26. data/ext/amalgalite/c/amalgalite_statement.c +649 -0
  27. data/ext/amalgalite/c/extconf.rb +62 -0
  28. data/ext/amalgalite/c/gen_constants.rb +330 -0
  29. data/ext/amalgalite/c/notes.txt +134 -0
  30. data/ext/amalgalite/c/sqlite3.c +205352 -0
  31. data/ext/amalgalite/c/sqlite3.h +10727 -0
  32. data/ext/amalgalite/c/sqlite3_options.h +4 -0
  33. data/ext/amalgalite/c/sqlite3ext.h +578 -0
  34. data/lib/amalgalite.rb +51 -0
  35. data/lib/amalgalite/2.0/amalgalite.so +0 -0
  36. data/lib/amalgalite/2.1/amalgalite.so +0 -0
  37. data/lib/amalgalite/2.2/amalgalite.so +0 -0
  38. data/lib/amalgalite/2.3/amalgalite.so +0 -0
  39. data/lib/amalgalite/2.4/amalgalite.so +0 -0
  40. data/lib/amalgalite/aggregate.rb +67 -0
  41. data/lib/amalgalite/blob.rb +186 -0
  42. data/lib/amalgalite/boolean.rb +42 -0
  43. data/lib/amalgalite/busy_timeout.rb +47 -0
  44. data/lib/amalgalite/column.rb +99 -0
  45. data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
  46. data/lib/amalgalite/csv_table_importer.rb +74 -0
  47. data/lib/amalgalite/database.rb +984 -0
  48. data/lib/amalgalite/function.rb +61 -0
  49. data/lib/amalgalite/index.rb +43 -0
  50. data/lib/amalgalite/memory_database.rb +15 -0
  51. data/lib/amalgalite/packer.rb +231 -0
  52. data/lib/amalgalite/paths.rb +80 -0
  53. data/lib/amalgalite/profile_tap.rb +131 -0
  54. data/lib/amalgalite/progress_handler.rb +21 -0
  55. data/lib/amalgalite/requires.rb +151 -0
  56. data/lib/amalgalite/schema.rb +225 -0
  57. data/lib/amalgalite/sqlite3.rb +6 -0
  58. data/lib/amalgalite/sqlite3/constants.rb +95 -0
  59. data/lib/amalgalite/sqlite3/database/function.rb +48 -0
  60. data/lib/amalgalite/sqlite3/database/status.rb +68 -0
  61. data/lib/amalgalite/sqlite3/status.rb +60 -0
  62. data/lib/amalgalite/sqlite3/version.rb +55 -0
  63. data/lib/amalgalite/statement.rb +418 -0
  64. data/lib/amalgalite/table.rb +91 -0
  65. data/lib/amalgalite/taps.rb +2 -0
  66. data/lib/amalgalite/taps/console.rb +27 -0
  67. data/lib/amalgalite/taps/io.rb +71 -0
  68. data/lib/amalgalite/trace_tap.rb +35 -0
  69. data/lib/amalgalite/type_map.rb +63 -0
  70. data/lib/amalgalite/type_maps/default_map.rb +166 -0
  71. data/lib/amalgalite/type_maps/storage_map.rb +38 -0
  72. data/lib/amalgalite/type_maps/text_map.rb +21 -0
  73. data/lib/amalgalite/version.rb +8 -0
  74. data/lib/amalgalite/view.rb +26 -0
  75. data/spec/aggregate_spec.rb +154 -0
  76. data/spec/amalgalite_spec.rb +4 -0
  77. data/spec/blob_spec.rb +78 -0
  78. data/spec/boolean_spec.rb +24 -0
  79. data/spec/busy_handler.rb +157 -0
  80. data/spec/data/iso-3166-country.txt +242 -0
  81. data/spec/data/iso-3166-schema.sql +22 -0
  82. data/spec/data/iso-3166-subcountry.txt +3995 -0
  83. data/spec/data/make-iso-db.sh +12 -0
  84. data/spec/database_spec.rb +508 -0
  85. data/spec/default_map_spec.rb +92 -0
  86. data/spec/function_spec.rb +78 -0
  87. data/spec/integeration_spec.rb +97 -0
  88. data/spec/iso_3166_database.rb +58 -0
  89. data/spec/packer_spec.rb +60 -0
  90. data/spec/paths_spec.rb +28 -0
  91. data/spec/progress_handler_spec.rb +91 -0
  92. data/spec/requires_spec.rb +54 -0
  93. data/spec/rtree_spec.rb +66 -0
  94. data/spec/schema_spec.rb +131 -0
  95. data/spec/spec_helper.rb +48 -0
  96. data/spec/sqlite3/constants_spec.rb +108 -0
  97. data/spec/sqlite3/database_status_spec.rb +36 -0
  98. data/spec/sqlite3/status_spec.rb +22 -0
  99. data/spec/sqlite3/version_spec.rb +28 -0
  100. data/spec/sqlite3_spec.rb +53 -0
  101. data/spec/statement_spec.rb +168 -0
  102. data/spec/storage_map_spec.rb +38 -0
  103. data/spec/tap_spec.rb +57 -0
  104. data/spec/text_map_spec.rb +20 -0
  105. data/spec/type_map_spec.rb +14 -0
  106. data/spec/version_spec.rb +8 -0
  107. data/tasks/custom.rake +102 -0
  108. data/tasks/default.rake +240 -0
  109. data/tasks/extension.rake +38 -0
  110. data/tasks/this.rb +208 -0
  111. metadata +318 -0
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $: << "../lib"
5
+ $: << "../ext"
6
+ require 'amalgalite'
7
+
8
+ #--
9
+ # Create a database and a table to put some results from the functions in
10
+ #--
11
+ db = Amalgalite::Database.new( ":memory:" )
12
+ db.execute( "CREATE TABLE ftest( data, md5, sha1, sha2_bits, sha2)" )
13
+
14
+ #------------------------------------------------------------------------------
15
+ # Create an MD5 method using the block format of defining an sql fuction
16
+ #------------------------------------------------------------------------------
17
+ require 'digest/md5'
18
+ db.define_function( 'md5' ) do |x|
19
+ Digest::MD5.hexdigest( x.to_s )
20
+ end
21
+
22
+ #------------------------------------------------------------------------------
23
+ # Create a SHA1 method using the lambda format of defining an sql function
24
+ #------------------------------------------------------------------------------
25
+ require 'digest/sha1'
26
+ sha1 = lambda do |y|
27
+ Digest::SHA1.hexdigest( y.to_s )
28
+ end
29
+ db.define_function( "sha1", sha1 )
30
+
31
+ #------------------------------------------------------------------------------
32
+ # Create a SHA2 method using the class format for defining an sql function
33
+ # In this one we will allow any number of parameters, but we will only use the
34
+ # first two.
35
+ #------------------------------------------------------------------------------
36
+ require 'digest/sha2'
37
+ class SQLSha2
38
+ # track the number of invocations
39
+ attr_reader :call_count
40
+
41
+ def initialize
42
+ @call_count = 0
43
+ end
44
+
45
+ # the protocol that is used for sql function definition
46
+ def to_proc() self ; end
47
+
48
+ # say we take any number of parameters
49
+ def arity
50
+ -1
51
+ end
52
+
53
+ # The method that is called by SQLite, must be named 'call'
54
+ def call( *args )
55
+ text = args.shift.to_s
56
+ bitlength = (args.shift || 256).to_i
57
+ Digest::SHA2.new( bitlength ).hexdigest( text )
58
+ end
59
+ end
60
+ db.define_function('sha2', SQLSha2.new)
61
+
62
+
63
+ #------------------------------------------------------------------------------
64
+ # Now we have 3 new sql functions, each defined in one of the available methods
65
+ # to define sql functions in amalgalite. Lets insert some rows and look at the
66
+ # results
67
+ #------------------------------------------------------------------------------
68
+ possible_bits = [ 256, 384, 512 ]
69
+ sql = "INSERT INTO ftest( data, md5, sha1, sha2_bits, sha2 ) VALUES( @word , md5( @word ), sha1( @word ), @bits, sha2(@word,@bits) )"
70
+ db.prepare( sql ) do |stmt|
71
+ DATA.each do |word|
72
+ word.strip!
73
+ bits = possible_bits[ rand(3) ]
74
+ puts "Inserting #{word}, #{bits}"
75
+ stmt.execute( { '@word' => word, '@bits' => bits } )
76
+ end
77
+ end
78
+
79
+ #------------------------------------------------------------------------------
80
+ # And show the results
81
+ #------------------------------------------------------------------------------
82
+ puts
83
+ puts "Getting results..."
84
+ puts
85
+ columns = db.schema.tables['ftest'].columns.keys.sort
86
+ i = 0
87
+ db.execute("SELECT #{columns.join(',')} FROM ftest") do |row|
88
+ i += 1
89
+ puts "-----[ row #{i} ]-" + "-" * 42
90
+ columns.each do |col|
91
+ puts "#{col.ljust(10)} : #{row[col]}"
92
+ end
93
+ puts
94
+ end
95
+
96
+
97
+ __END__
98
+ some
99
+ random
100
+ words
101
+ with
102
+ which
103
+ to
104
+ play
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'amalgalite'
4
+ require 'benchmark'
5
+ require 'pathname'
6
+
7
+ begin
8
+ require 'json'
9
+ rescue LoadError
10
+ abort "'gem install json' to run this example"
11
+ end
12
+
13
+
14
+ README = <<_
15
+ This example program assumes that you have available the 'fortune' BSD command dataset.
16
+
17
+ execute `fortune -f` to see if you have this command available.
18
+ _
19
+
20
+ fortune_dir = %x[ fortune -f 2>&1 ].split("\n").first.split.last
21
+ abort README unless fortune_dir and File.directory?( fortune_dir )
22
+
23
+ fortune_path = Pathname.new(fortune_dir)
24
+
25
+ #
26
+ # Lets create a database that utilizes FTS5 http://www.sqlite.org/fts5.html
27
+ #
28
+ #
29
+
30
+ #
31
+ # Create a database, this will create the DB if it doesn't exist
32
+ #
33
+ puts "Opening database (version #{Amalgalite::VERSION})"
34
+ db = Amalgalite::Database.new("fts5.db")
35
+
36
+ #
37
+ # Create the schema unless it already exists in the table. The meta information
38
+ # about the database schema is available as the result of the db.schema method
39
+ #
40
+ schema = db.schema
41
+ unless schema.tables['search']
42
+ puts "Create schema"
43
+ db.execute_batch <<-SQL
44
+ CREATE VIRTUAL TABLE search USING fts5(
45
+ filename,
46
+ content
47
+ );
48
+
49
+ CREATE TABLE plain (
50
+ filename VARCHAR(128),
51
+ content TEXT
52
+ );
53
+ SQL
54
+ db.reload_schema!
55
+ end
56
+
57
+ def each_fortune(path,&block)
58
+ fortune = []
59
+ path.each_line do |line|
60
+ line.strip!
61
+ if line == "%" then
62
+ yield fortune.join("\n")
63
+ fortune.clear
64
+ else
65
+ fortune << line
66
+ end
67
+ end
68
+ end
69
+
70
+ #
71
+ # Only load the data if the db is empty
72
+ #
73
+ if db.first_value_from( "SELECT count(*) from search" ) == 0 then
74
+ before = Time.now
75
+ idx = 0
76
+
77
+ # Inserting bulk rows as a transaction is good practice with SQLite, it is
78
+ # MUCH faster.
79
+ db.transaction do |db_in_transaction|
80
+ # Iterate over the files in the fortunes dir and split on the fortunes, then
81
+
82
+ fortune_path.each_child do |fortune_file|
83
+ next if fortune_file.directory?
84
+ next if fortune_file.extname == ".dat"
85
+ $stdout.puts "Loading #{fortune_file}"
86
+
87
+ each_fortune(fortune_file) do |fortune|
88
+ insert_data = {
89
+ ':fname' => fortune_file.to_s,
90
+ ':content' => fortune
91
+ }
92
+
93
+ # insert into the FTS5 table
94
+ db_in_transaction.prepare("INSERT INTO search( filename, content ) VALUES( :fname, :content );") do |stmt|
95
+ stmt.execute( insert_data )
96
+ end
97
+
98
+ # insert into the normal table for comparison
99
+ db_in_transaction.prepare("INSERT INTO plain( filename, content ) VALUES( :fname, :content );") do |stmt|
100
+ stmt.execute( insert_data )
101
+ end
102
+
103
+ idx += 1
104
+ print "Processed #{idx}\r"
105
+ $stdout.flush
106
+ end
107
+ puts "\nFinalizing..."
108
+ end
109
+ end
110
+ puts "Took #{Time.now - before} seconds to insert #{idx} fortunes"
111
+ puts "Done Inserting"
112
+ end
113
+
114
+ doc_count = db.first_value_from( "SELECT count(*) from search" )
115
+
116
+ #
117
+ # Now lets do some searching for some various words
118
+ #
119
+
120
+ %w[ president salmon thanks ].each do |word|
121
+
122
+ count = 100
123
+ puts
124
+ puts "Searching for '#{word}' #{count} times across #{doc_count} fortunes"
125
+ puts "=" * 60
126
+
127
+ Benchmark.bm( 15 ) do |x|
128
+
129
+ #
130
+ # search using the fts search to get the cont of tweets with the given word
131
+ #
132
+ x.report('fts5: ') do
133
+ db.prepare( "SELECT count(filename) FROM search WHERE search MATCH 'content:#{word}'" ) do |stmt|
134
+ count.times do
135
+ stmt.execute
136
+ end
137
+ end
138
+ end
139
+
140
+ #
141
+ # search using basic string matching in sqlite.
142
+ #
143
+ x.report('plain: ') do
144
+ db.prepare( "SELECT count(filename) FROM plain WHERE content LIKE '% #{word} %'" ) do |stmt|
145
+ count.times do
146
+ stmt.execute
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # Basic amalgalite example creating a table, inserting rows and doing various
5
+ # selects and prepared statements
6
+ #
7
+ require 'rubygems'
8
+ require 'amalgalite'
9
+
10
+ #
11
+ # Create a database, this will create the DB if it doesn't exist
12
+ #
13
+ puts "Opening database (version #{Amalgalite::Version})"
14
+ db = Amalgalite::Database.new("gems.db")
15
+
16
+ #
17
+ # Setup taps into profile and trace information of sqlite, the profile tap will
18
+ # goto the profile_tap.log file and the trace information will go to the
19
+ # trace_tap.log file
20
+ #
21
+ puts "Establishing taps"
22
+ db.trace_tap = Amalgalite::Taps::IO.new( trace_tap_file = File.open("trace_tap.log", "w+") )
23
+ db.profile_tap = Amalgalite::Taps::IO.new( profile_tap_file = File.open("profile_tap.log", "w+") )
24
+
25
+ #
26
+ # Create the schema unless it already exists in the table. The meta information
27
+ # about the database schema is available as the result of the db.schema method
28
+ #
29
+ schema = db.schema
30
+ unless schema.tables['gems']
31
+ puts "Create schema"
32
+ db.execute <<-SQL
33
+ CREATE TABLE gems (
34
+ id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
35
+ name VARCHAR(128),
36
+ version VARCHAR(32),
37
+ author VARCHAR(128)
38
+ );
39
+ SQL
40
+ db.reload_schema!
41
+ end
42
+
43
+ #
44
+ # Get some data from the system to insert into the database. Since everyone
45
+ # probably has gems installed, that's a ready known piece of information. We'll
46
+ # just pull in the latest version of each installed gem and dump some meta
47
+ # information into a db for testing.
48
+ #
49
+ latest_specs = Gem.source_index.latest_specs
50
+
51
+ puts "Inserting #{latest_specs.size} rows of gem information..."
52
+ before = Time.now
53
+
54
+ # Inserting bulk rows as a transaction is good practice with SQLite, it is
55
+ # MUCH faster.
56
+ db.transaction do |db_in_transaction|
57
+ db_in_transaction.prepare("INSERT INTO gems(name, version, author) VALUES( :name, :version, :author );") do |stmt|
58
+ latest_specs.each do |spec|
59
+ insert_data = {}
60
+ insert_data[':name'] = spec.name.to_s
61
+ insert_data[':version'] = spec.version.to_s
62
+ insert_data[':author'] = spec.authors.join(' ')
63
+ #puts "Inserting #{insert_data.inspect}"
64
+ stmt.execute( insert_data )
65
+ end
66
+ end
67
+ end
68
+ puts "Took #{Time.now - before} seconds"
69
+ puts "Done Inserting"
70
+
71
+ authors_by_number = db.execute("SELECT author, count( name ) as num_gems FROM gems GROUP BY author ORDER BY num_gems DESC")
72
+ favorite_author = authors_by_number.first
73
+ puts "Your favorite gem author is <#{favorite_author['author']}>, with #{favorite_author['num_gems']} gems installed."
74
+
75
+ #
76
+ # Now we'll look at the profile sampler and see what information it traced about
77
+ # our behavoir.
78
+ #
79
+ db.profile_tap.samplers.each do |stat_name, stat_values|
80
+ puts "-" * 20
81
+ puts stat_values.to_s
82
+ end
83
+
84
+ #
85
+ # Clear out the taps (not really necessary, just cleaning up)
86
+ #
87
+ db.trace_tap = profile_tap = nil
88
+
89
+ #
90
+ # close things down
91
+ #
92
+ db.close
93
+ trace_tap_file.close
94
+ profile_tap_file.close
@@ -0,0 +1,11 @@
1
+ CONSTANT = "bad"
2
+ class RequireMe
3
+ def initialize( msg )
4
+ @msg = msg
5
+ puts "RequireMe initialized"
6
+ end
7
+
8
+ def foo
9
+ puts @msg
10
+ end
11
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # An Amalgalite example showing how to 'require' data in an amalgalite database
5
+ #
6
+ # We'll make a database with one table, that we store file contents in.
7
+ #
8
+
9
+ $: << "../lib"
10
+ $: << "../ext"
11
+ require 'rubygems'
12
+ require 'amalgalite'
13
+
14
+ style = ARGV.shift || "normal"
15
+
16
+ #
17
+ # create the database
18
+ #
19
+ dbfile = Amalgalite::Requires::Bootstrap::DEFAULT_DB
20
+ File.unlink( dbfile ) if File.exist?( dbfile )
21
+ require 'amalgalite/packer'
22
+ options = { :verbose => true }
23
+ if style == "compressed" then
24
+ options[:compressed] = true
25
+ end
26
+ p = Amalgalite::Packer.new( options )
27
+ p.pack( [ "require_me.rb" ] )
28
+
29
+ require 'amalgalite/requires'
30
+ begin
31
+ Amalgalite::Requires.new( :dbfile_name => p.dbfile )
32
+ FileUtils.mv 'require_me.rb', 'rm.rb', :verbose => true
33
+ require 'require_me'
34
+ e = RequireMe.new( "#{style} require style works!" )
35
+ e.foo
36
+ require 'require_me'
37
+ puts
38
+
39
+ ensure
40
+ FileUtils.mv 'rm.rb', 'require_me.rb', :verbose => true
41
+ File.unlink( dbfile ) if File.exist?( dbfile )
42
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'amalgalite'
5
+
6
+ db_name = ARGV.shift
7
+ unless db_name
8
+ puts "Usage: #{File.basename($0)} dbname"
9
+ exit 1
10
+ end
11
+ db = Amalgalite::Database.new( db_name )
12
+ col_info = %w[ default_value declared_data_type collation_sequence_name not_null_constraint primary_key auto_increment ]
13
+ max_width = col_info.collect { |c| c.length }.sort.last
14
+
15
+ db.schema.tables.keys.sort.each do |table_name|
16
+ puts "Table: #{table_name}"
17
+ puts "=" * 42
18
+ db.schema.tables[table_name].columns.each_pair do |col_name, col|
19
+ puts " Column : #{col.name}"
20
+ col_info.each do |ci|
21
+ puts " |#{ci.rjust( max_width, "." )} : #{col.send( ci )}"
22
+ end
23
+ puts
24
+ end
25
+
26
+ db.schema.tables[table_name].indexes.each_pair do |idx_name, index|
27
+ puts " Index : #{index.name}"
28
+ puts " |#{"sequence_number".rjust( max_width, "." )} : #{index.sequence_number}"
29
+ puts " |#{"is unique?".rjust( max_width, ".")} : #{index.unique?}"
30
+ puts " |#{"columns".rjust( max_width, ".")} : #{index.columns.collect { |c| c.name }.join(',') }"
31
+ puts
32
+ end
33
+ end
34
+
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Copyright (c) 2008 Jeremy Hinegardner
3
+ * All rights reserved. See LICENSE and/or COPYING for details.
4
+ *
5
+ * vim: shiftwidth=4
6
+ :*/
7
+
8
+ #include "amalgalite.h"
9
+
10
+ /* Module and Classes */
11
+ VALUE mA; /* module Amalgalite */
12
+ VALUE mAS; /* module Amalgalite::SQLite3 */
13
+ VALUE mASV; /* module Amalgalite::SQLite3::Version */
14
+ VALUE eAS_Error; /* class Amalgalite::SQLite3::Error */
15
+ VALUE cAS_Stat; /* class Amalgalite::SQLite3::Stat */
16
+
17
+ /*----------------------------------------------------------------------
18
+ * module methods for Amalgalite::SQLite3
19
+ *---------------------------------------------------------------------*/
20
+
21
+ /*
22
+ * call-seq:
23
+ * Amalgalite::SQLite3.threadsafe? -> true or false
24
+ *
25
+ * Has the SQLite3 extension been compiled "threadsafe". If threadsafe? is
26
+ * true then the internal SQLite mutexes are enabled and SQLite is threadsafe.
27
+ * That is threadsafe within the context of 'C' threads.
28
+ *
29
+ */
30
+ VALUE am_sqlite3_threadsafe(VALUE self)
31
+ {
32
+ if (sqlite3_threadsafe()) {
33
+ return Qtrue;
34
+ } else {
35
+ return Qfalse;
36
+ }
37
+ }
38
+
39
+ /*
40
+ * call-seq:
41
+ * Amalgalite::SQLite.temp_directory -> String or nil
42
+ *
43
+ * Return the directory name that all that all the temporary files created by
44
+ * SQLite creates will be placed. If _nil_ is returned, then SQLite will search
45
+ * for an appropriate directory.
46
+ */
47
+ VALUE am_sqlite3_get_temp_directory( VALUE self )
48
+ {
49
+ if (NULL == sqlite3_temp_directory) {
50
+ return Qnil;
51
+ } else {
52
+ return rb_str_new2( sqlite3_temp_directory );
53
+ }
54
+ }
55
+
56
+ /*
57
+ * call-seq:
58
+ * Amalgalite::SQLite.temp_directory = "/tmp/location"
59
+ *
60
+ * Set the temporary directory used by sqlite to store temporary directories.
61
+ * It is not safe to set this value after a Database has been opened.
62
+ *
63
+ */
64
+ VALUE am_sqlite3_set_temp_directory( VALUE self, VALUE new_dir )
65
+ {
66
+ char *p = NULL ;
67
+
68
+ if ( NULL != sqlite3_temp_directory ) {
69
+ free( sqlite3_temp_directory );
70
+ }
71
+
72
+ if ( Qnil != new_dir ) {
73
+ VALUE str = StringValue( new_dir );
74
+
75
+ p = calloc( RSTRING_LEN(str) + 1, sizeof(char) );
76
+ strncpy( p, RSTRING_PTR(str), RSTRING_LEN(str) );
77
+ }
78
+
79
+ sqlite3_temp_directory = p;
80
+
81
+ return Qnil;
82
+ }
83
+
84
+ VALUE amalgalite_format_string( const char* pattern, VALUE string )
85
+ {
86
+ VALUE to_s= rb_funcall( string, rb_intern("to_s"), 0 );
87
+ VALUE str = StringValue( to_s );
88
+ char *p = sqlite3_mprintf(pattern, RSTRING_PTR(str));
89
+ VALUE rv = Qnil;
90
+ if ( NULL != p ) {
91
+ rv = rb_str_new2( p );
92
+ sqlite3_free( p );
93
+ } else {
94
+ rb_raise( rb_eNoMemError, "Unable to quote string" );
95
+ }
96
+
97
+ return rv;
98
+ }
99
+ /*
100
+ * call-seq:
101
+ * Amalgalite::SQLite.escape( string ) => escaped_string
102
+ *
103
+ * Takes the input string and escapes each ' (single quote) character by
104
+ * doubling it.
105
+ */
106
+ VALUE am_sqlite3_escape( VALUE self, VALUE string )
107
+ {
108
+ return ( Qnil == string ) ? Qnil : amalgalite_format_string( "%q", string );
109
+ }
110
+
111
+ /*
112
+ * call-seq:
113
+ * Amalgalite::SQLite.quote( string ) => quoted-escaped string
114
+ *
115
+ * Takes the input string and surrounds it with single quotes, it also escapes
116
+ * each embedded single quote with double quotes.
117
+ */
118
+ VALUE am_sqlite3_quote( VALUE self, VALUE string )
119
+ {
120
+ return ( Qnil == string ) ? Qnil : amalgalite_format_string( "%Q", string );
121
+ }
122
+
123
+ /*
124
+ * call-seq:
125
+ * Amalgalite::SQLite3.complete?( ... , opts = { :utf16 => false }) -> True, False
126
+ *
127
+ * Is the text passed in as a parameter a complete SQL statement? Or is
128
+ * additional input required before sending the SQL to the extension. If the
129
+ * extra 'opts' parameter is used, you can send in a UTF-16 encoded string as
130
+ * the SQL.
131
+ *
132
+ * A complete statement must end with a semicolon.
133
+ *
134
+ */
135
+ VALUE am_sqlite3_complete(VALUE self, VALUE args)
136
+ {
137
+ VALUE sql = rb_ary_shift( args );
138
+ VALUE opts = rb_ary_shift( args );
139
+ VALUE utf16 = Qnil;
140
+ int result = 0;
141
+
142
+ if ( ( Qnil != opts ) && ( T_HASH == TYPE(opts) ) ){
143
+ utf16 = rb_hash_aref( opts, rb_intern("utf16") );
144
+ }
145
+
146
+ if ( (Qfalse == utf16) || (Qnil == utf16) ) {
147
+ result = sqlite3_complete( StringValuePtr( sql ) );
148
+ } else {
149
+ result = sqlite3_complete16( (void*) StringValuePtr( sql ) );
150
+ }
151
+
152
+ return ( result > 0 ) ? Qtrue : Qfalse;
153
+ }
154
+
155
+ /*
156
+ * call-seq:
157
+ * Amalgalite::SQLite3::Stat.update!( reset = false ) -> nil
158
+ *
159
+ * Populates the _@current_ and _@higwater_ instance variables of self
160
+ * object with the values from the sqlite3_status call. If reset it true then
161
+ * the highwater mark for the stat is reset
162
+ *
163
+ */
164
+ VALUE am_sqlite3_stat_update_bang( int argc, VALUE *argv, VALUE self )
165
+ {
166
+ int status_op = -1;
167
+ int current = -1;
168
+ int highwater = -1;
169
+ VALUE reset = Qfalse;
170
+ int reset_flag = 0;
171
+ int rc;
172
+
173
+ status_op = FIX2INT( rb_iv_get( self, "@code" ) );
174
+ if ( argc > 0 ) {
175
+ reset = argv[0];
176
+ reset_flag = ( Qtrue == reset ) ? 1 : 0 ;
177
+ }
178
+
179
+ rc = sqlite3_status( status_op, &current, &highwater, reset_flag );
180
+
181
+ if ( SQLITE_OK != rc ) {
182
+ VALUE n = rb_iv_get( self, "@name" ) ;
183
+ char* name = StringValuePtr( n );
184
+ rb_raise(eAS_Error, "Failure to retrieve status for %s : [SQLITE_ERROR %d] \n", name, rc);
185
+ }
186
+
187
+ rb_iv_set( self, "@current", INT2NUM( current ) );
188
+ rb_iv_set( self, "@highwater", INT2NUM( highwater) );
189
+
190
+ return Qnil;
191
+ }
192
+
193
+ /*
194
+ * call-seq:
195
+ * Amalgalite::SQLite3.randomness( N ) -> String of length N
196
+ *
197
+ * Generate N bytes of random data.
198
+ *
199
+ */
200
+ VALUE am_sqlite3_randomness(VALUE self, VALUE num_bytes)
201
+ {
202
+ int n = NUM2INT(num_bytes);
203
+ char *buf = ALLOCA_N(char, n);
204
+
205
+ sqlite3_randomness( n, buf );
206
+ return rb_str_new( buf, n );
207
+ }
208
+
209
+ /*----------------------------------------------------------------------
210
+ * module methods for Amalgalite::SQLite3::Version
211
+ *---------------------------------------------------------------------*/
212
+
213
+ /*
214
+ * call-seq:
215
+ * Amalgalite::SQLite3::Version.to_s -> String
216
+ *
217
+ * Return the SQLite C library version number as a string
218
+ *
219
+ */
220
+ VALUE am_sqlite3_runtime_version(VALUE self)
221
+ {
222
+ return rb_str_new2(sqlite3_libversion());
223
+ }
224
+
225
+ /*
226
+ * call-seq:
227
+ * Amalgalite::SQLite3.Version.to_i -> Fixnum
228
+ *
229
+ * Return the SQLite C library version number as an integer
230
+ *
231
+ */
232
+ VALUE am_sqlite3_runtime_version_number(VALUE self)
233
+ {
234
+ return INT2FIX(sqlite3_libversion_number());
235
+ }
236
+
237
+ /*
238
+ * call-seq:
239
+ * Amalgalite::SQLite3::Version.runtime_source_id -> String
240
+ *
241
+ * Return the SQLite C library source id as a string
242
+ *
243
+ */
244
+ VALUE am_sqlite3_runtime_source_id(VALUE self)
245
+ {
246
+ return rb_str_new2(sqlite3_sourceid());
247
+ }
248
+
249
+ /*
250
+ * call-seq:
251
+ * Amalgalite::SQLite::Version.compiled_version -> String
252
+ *
253
+ * Return the compiletime version number as a string.
254
+ *
255
+ */
256
+ VALUE am_sqlite3_compiled_version(VALUE self)
257
+ {
258
+ return rb_str_new2( SQLITE_VERSION );
259
+ }
260
+
261
+ /*
262
+ * call-seql:
263
+ * Amalgalite::SQLite::Version.compiled_version_number -> Fixnum
264
+ *
265
+ * Return the compiletime library version from the
266
+ * embedded version of sqlite3.
267
+ *
268
+ */
269
+ VALUE am_sqlite3_compiled_version_number( VALUE self )
270
+ {
271
+ return INT2FIX( SQLITE_VERSION_NUMBER );
272
+ }
273
+
274
+ /*
275
+ * call-seq:
276
+ * Amalgalite::SQLite3::Version.compiled_source_id -> String
277
+ *
278
+ * Return the compiled SQLite C library source id as a string
279
+ *
280
+ */
281
+ VALUE am_sqlite3_compiled_source_id(VALUE self)
282
+ {
283
+ return rb_str_new2( SQLITE_SOURCE_ID );
284
+ }
285
+
286
+ /**
287
+ * Document-class: Amalgalite::SQLite3
288
+ *
289
+ * The SQLite ruby extension inside Amalgalite.
290
+ *
291
+ */
292
+
293
+ void Init_amalgalite()
294
+ {
295
+ int rc = 0;
296
+
297
+ /*
298
+ * top level module encapsulating the entire Amalgalite library
299
+ */
300
+ mA = rb_define_module("Amalgalite");
301
+
302
+ mAS = rb_define_module_under(mA, "SQLite3");
303
+ rb_define_module_function(mAS, "threadsafe?", am_sqlite3_threadsafe, 0);
304
+ rb_define_module_function(mAS, "complete?", am_sqlite3_complete, -2);
305
+ rb_define_module_function(mAS, "randomness", am_sqlite3_randomness,1);
306
+ rb_define_module_function(mAS, "temp_directory", am_sqlite3_get_temp_directory, 0);
307
+ rb_define_module_function(mAS, "temp_directory=", am_sqlite3_set_temp_directory, 1);
308
+
309
+ rb_define_module_function(mAS, "escape", am_sqlite3_escape, 1);
310
+ rb_define_module_function(mAS, "quote", am_sqlite3_quote, 1);
311
+
312
+ /*
313
+ * class encapsulating a single Stat
314
+ */
315
+ cAS_Stat = rb_define_class_under(mAS, "Stat", rb_cObject);
316
+ rb_define_method(cAS_Stat, "update!", am_sqlite3_stat_update_bang, -1);
317
+
318
+ /*
319
+ * Base class of all SQLite3 errors
320
+ */
321
+ eAS_Error = rb_define_class_under(mAS, "Error", rb_eStandardError); /* in amalgalite.c */
322
+
323
+ /**
324
+ * Encapsulation of the SQLite C library version
325
+ */
326
+ mASV = rb_define_module_under(mAS, "Version");
327
+ rb_define_module_function(mASV, "to_s", am_sqlite3_runtime_version, 0); /* in amalgalite.c */
328
+ rb_define_module_function(mASV, "runtime_version", am_sqlite3_runtime_version, 0); /* in amalgalite.c */
329
+ rb_define_module_function(mASV, "to_i", am_sqlite3_runtime_version_number, 0); /* in amalgalite.c */
330
+ rb_define_module_function(mASV, "runtime_version_number", am_sqlite3_runtime_version_number, 0); /* in amalgalite.c */
331
+ rb_define_module_function(mASV, "compiled_version", am_sqlite3_compiled_version, 0 ); /* in amalgalite.c */
332
+ rb_define_module_function(mASV, "compiled_version_number", am_sqlite3_compiled_version_number, 0 ); /* in amalgalite.c */
333
+ rb_define_module_function(mASV, "runtime_source_id", am_sqlite3_runtime_source_id, 0); /* in amalgalite.c */
334
+ rb_define_module_function(mASV, "compiled_source_id", am_sqlite3_compiled_source_id, 0); /* in amalgalite.c */
335
+
336
+ /*
337
+ * Initialize the rest of the module
338
+ */
339
+ Init_amalgalite_constants( );
340
+ Init_amalgalite_database( );
341
+ Init_amalgalite_statement( );
342
+ Init_amalgalite_blob( );
343
+ Init_amalgalite_requires_bootstrap( );
344
+
345
+ /*
346
+ * initialize sqlite itself
347
+ */
348
+ rc = sqlite3_initialize();
349
+ if ( SQLITE_OK != rc ) {
350
+ rb_raise(eAS_Error, "Failure to initialize the sqlite3 library : [SQLITE_ERROR %d]\n", rc);
351
+ }
352
+
353
+ }
354
+
355
+