amalgalite 1.6.0-x64-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,65 @@
1
+ ## Amalgalite
2
+
3
+ * [Homepage](http://github.com/copiousfreetime/amalgalite)
4
+ * email jeremy at copiousfreetime dot org
5
+ * `git clone git://github.com/copiousfreetime/amalgalite.git`
6
+ * [Github](http://github.com/copiousfreetime/amalgalite/)
7
+ * [Bug Tracking](http://github.com/copiousfreetime/amalgalite/issues)
8
+
9
+ ## Articles
10
+
11
+ * [Writing SQL Functions in Ruby](http://copiousfreetime.org/articles/2009/01/10/writing-sql-functions-in-ruby.html)
12
+
13
+ ## INSTALL
14
+
15
+ * `gem install amalgalite`
16
+
17
+ ## DESCRIPTION
18
+
19
+ Amalgalite embeds the SQLite database engine in a ruby extension. There is no
20
+ need to install SQLite separately.
21
+
22
+ Look in the examples/ directory to see
23
+
24
+ * general usage
25
+ * blob io
26
+ * schema information
27
+ * custom functions
28
+ * custom aggregates
29
+ * requiring ruby code from a database
30
+ * full text search
31
+
32
+ Also Scroll through Amalgalite::Database for a quick example, and a general
33
+ overview of the API.
34
+
35
+ Amalgalite adds in the following additional non-default SQLite extensions:
36
+
37
+ * [R*Tree index extension](http://sqlite.org/rtree.html)
38
+ * [Full Text Search](http://sqlite.org/fts3.html)
39
+
40
+ ## BUILDING FOR WINDOWS
41
+
42
+ This is done using https://github.com/rake-compiler/rake-compiler-dock
43
+
44
+ 1. have VirtualBox installed
45
+ 2. Install boot2docker `brew install boot2docker`
46
+ 3. `gem install rake-compiler-dock`
47
+ 4. `rake-compiler-dock`
48
+ 5. `bundle`
49
+ 6. `rake cross native gem`
50
+
51
+ ## CREDITS
52
+
53
+ * Jamis Buck for the first [ruby sqlite implementation](http://www.rubyforge.org/projects/sqlite-ruby)
54
+
55
+ ## CHANGES
56
+
57
+ Read the HISTORY.rdoc file.
58
+
59
+ ## LICENSE
60
+
61
+ Copyright (c) 2008 Jeremy Hinegardner
62
+
63
+ All rights reserved.
64
+
65
+ See LICENSE and/or COPYING for details.
@@ -0,0 +1,26 @@
1
+ # vim: syntax=ruby
2
+ load 'tasks/this.rb'
3
+
4
+ This.name = "amalgalite"
5
+ This.author = "Jeremy Hinegardner"
6
+ This.email = "jeremy@copiousfreetime.org"
7
+ This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
8
+
9
+ This.ruby_gemspec do |spec|
10
+ spec.add_dependency( 'arrayfields', '~> 4.9' )
11
+
12
+ spec.add_development_dependency( 'rspec' , '~> 3.0' )
13
+ spec.add_development_dependency( 'rake' , '~> 12.0')
14
+ spec.add_development_dependency( 'rake-compiler', '~> 1.0' )
15
+ spec.add_development_dependency( 'rake-compiler-dock', '~> 0.6' )
16
+ spec.add_development_dependency( 'rdoc' , '~> 5.0' )
17
+ spec.add_development_dependency( 'simplecov' , '~> 0.14' )
18
+ spec.add_development_dependency( 'zip' , '~> 2.0' )
19
+
20
+ spec.extensions.concat This.extension_conf_files
21
+ spec.license = "BSD"
22
+ end
23
+
24
+ load 'tasks/default.rake'
25
+ load 'tasks/extension.rake'
26
+ load 'tasks/custom.rake'
data/TODO.md ADDED
@@ -0,0 +1,57 @@
1
+ # Future Release possibilties:
2
+ - rebuild statement constants
3
+ - look at all pragma statements
4
+
5
+ ## SQLite API:
6
+ - authorizers
7
+ - loading of extensions -- readfile / writefile
8
+ - utf-16 integration
9
+ - create_collation
10
+ - encryption key support
11
+ - expose sqlite3_strnicmp
12
+ - table name and column name in a type map?
13
+ - type conversion for manifest typing? how to allow it through?
14
+ - explicit pragma handler
15
+ - application_id pragma setter
16
+
17
+ ## Non backwards compatible changes:
18
+ - change the schema objects to be more consistent
19
+ - change taps to use to_proc protocol
20
+ - convert type dependency to just use 'call'
21
+ - integrate transaction and savepoint under the same api
22
+
23
+ ## SQLite Features:
24
+ - activate SQLITE_ENABLE_ICU extension
25
+ - activate SQLITE_ENABLE_LOCKING_STYLE
26
+ - activate SQLITE_ENABLE_UNLOCK_NOTIFY
27
+ - expose PRAGMA foreign_keys
28
+ - virtual file system
29
+ - full text search (FTS3)
30
+ - expose the sqlite mutex lib
31
+ - statement status ( sqlite3_stmt_status )
32
+ - db status ( sqlite3_db_status )
33
+ - library status ( sqlite3_status )
34
+ - sqlite3_index_info
35
+ - sqlite3_create_function has 4th parameter SQLITE_DETERMINISTIC
36
+ - sqlite3_rtree_query_callback()
37
+
38
+ ## Drivers:
39
+ - data mapper driver
40
+ - sequel driver optimization
41
+
42
+ ## Features:
43
+ - Think about moving from arrayfields to ordered hash?
44
+ - add to command line which directory to pack into a rubylibs table
45
+ - amalgalite command line tool
46
+ - use ruby's ALLOC_N and hook into sqlite3_mem_methods
47
+
48
+ ## Functions to possibly expose:
49
+ - sqlite3_backup_remaining, sqlite3_backup_pagecount
50
+ - sqlite3_compileoption_used, sqlite3_compileoption_get
51
+ - sqlite3_config
52
+ - sqlite3_data_count - returns number of colums in the result set of a
53
+ prepared statement
54
+ - sqlite_sourceid, sqlite_source_id
55
+ - sqlite3_strnicmp
56
+ -
57
+
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'pathname'
4
+
5
+ #
6
+ # add relative paths to the load path if we are not a gem and calculate what the
7
+ # strip path will be if we decide to pack --self
8
+ #
9
+ this_path = Pathname.new( File.expand_path( __FILE__ ) )
10
+ gem_path = Pathname.new( Gem.dir )
11
+ rel_path = this_path.relative_path_from( gem_path )
12
+ if ".." == rel_path.to_s.split( File::SEPARATOR ).first then
13
+ lib_path = File.join( File.dirname( __FILE__ ), "../lib" )
14
+ $:.unshift lib_path
15
+ $:.unshift File.join( File.dirname( __FILE__ ), "../ext" )
16
+ end
17
+
18
+ #
19
+ # snapshot of what is needed for amalgalite requires, this info may only be used
20
+ # when packing amalgalite itself
21
+ #
22
+ loaded_features_before = $LOADED_FEATURES.dup
23
+ require 'amalgalite/packer'
24
+ loaded_features_after = $LOADED_FEATURES.dup
25
+ load_diff = loaded_features_after - loaded_features_before
26
+
27
+ #
28
+ # strip off any LOAD_PATH elements from the front of load_diff since that
29
+ # will conflict with Amalgalite::Packer.amalgalite_require_order. Also
30
+ # strip out any 'rubygems' items since those are not used by Amalgalite
31
+ # and show as a side effect fo the "require 'amalgalite/packer'"
32
+ #
33
+ strip_paths = $LOAD_PATH.sort.reverse
34
+ amalgalite_needs = []
35
+ load_diff.each do |f|
36
+ next if f.split( File::SEPARATOR ).include?( "rubygems" )
37
+ appended = false
38
+ strip_paths.each do |path|
39
+ if 0 == f.index(path ) then
40
+ rel_path = f.sub( path, '' ).sub(%r{\A#{File::SEPARATOR}},'')
41
+ amalgalite_needs << rel_path
42
+ appended = true
43
+ break
44
+ end
45
+ end
46
+ amalgalite_needs << f unless appended
47
+ end
48
+
49
+ #
50
+ # Commandline parser
51
+ #
52
+ options = {}
53
+ begin
54
+ parser = OptionParser.new do |op|
55
+ op.banner = "Usage: #{op.program_name} [options] <files>"
56
+ op.separator ""
57
+
58
+ op.on("--dbfile DB", "The Database file in which to pack files") do |d|
59
+ options[:dbfile] = d
60
+ end
61
+
62
+ op.on("--drop-table", "Drop the table before inserting rows") do |t|
63
+ options[:drop_table] = t
64
+ end
65
+
66
+ op.on("-m", "--merge", "Merge these files into the existing table overwriting rows that conflict") do |m|
67
+ options[:merge] = true
68
+ end
69
+
70
+ op.on("--require-order", "Dump Amalgalite's require order" ) do |m|
71
+ puts amalgalite_needs
72
+ exit 0
73
+ end
74
+
75
+ op.on("--self", "pack amalgalite itself into the database") do |d|
76
+ options[:self] = true
77
+ end
78
+
79
+ op.on("--strip-prefix PREFIX", "strip this path prefix off the front of each file") do |p|
80
+ options[:strip_prefix] = File.expand_path( p )
81
+ end
82
+
83
+ op.on("-t", "--table TABLE", "the table name to pack into") do |t|
84
+ options[:table_name] = t
85
+ end
86
+
87
+ op.on("--verbose", "Be verbose about output") do |v|
88
+ options[:verbose] = v
89
+ end
90
+
91
+ op.on("-z", "--compressed", "compress the file contents on storage") do |z|
92
+ options[:compressed] = true
93
+ end
94
+
95
+ end
96
+
97
+ parser.parse!
98
+ require 'amalgalite/packer'
99
+ file_list = ARGV.dup
100
+
101
+
102
+ if options[:self] then
103
+ options[:table_name] = Amalgalite::Requires::Bootstrap::DEFAULT_BOOTSTRAP_TABLE
104
+ core_libs = (amalgalite_needs - Amalgalite::Packer.amalgalite_require_order).delete_if { |l| l.index(".rb").nil? }
105
+
106
+ #
107
+ # check and make sure nothing is missed
108
+ #
109
+ core_libs.each do |l|
110
+ if l.index("amalgalite") then
111
+ STDERR.puts "ERROR! require_order needs an update #{l}"
112
+ exit 2
113
+ end
114
+ end
115
+ file_list = core_libs.concat( Amalgalite::Packer.amalgalite_require_order )
116
+ if options[:compressed] then
117
+ STDERR.puts "Compressing --self is not allowed, reverting to uncompressed"
118
+ options[:compressed] = false
119
+ end
120
+ end
121
+ STDERR.puts parser if file_list.empty?
122
+
123
+ packer = Amalgalite::Packer.new( options )
124
+ packer.pack( file_list )
125
+
126
+ rescue OptionParser::ParseError => pe
127
+ STDERR.puts "ERROR : #{pe}"
128
+ STDERR.puts parser
129
+ exit 1
130
+ end
131
+
132
+ __END__
133
+
134
+ puts <<-text
135
+
136
+ Packing complete. To utilize the bootstrapping in #{dbfile} you must do
137
+ one of the following:
138
+
139
+ * statically compile the amalgalite C extension into your application
140
+ * require 'amalgalite/#{RUBY_VERSION.sub(/\.\d$/,'')}/amalgalite3'
141
+
142
+ Once one of those is working you can boostrap the Amalgalite library with
143
+ this line in your code:
144
+
145
+ Amalgalite::Requries::Boostrap.lift( 'dbfile' => '#{dbfile}' )
146
+
147
+ text
@@ -0,0 +1,9 @@
1
+ class A
2
+ def initialize
3
+ puts "Initialized A"
4
+ end
5
+
6
+ def a
7
+ puts "called a"
8
+ end
9
+ end
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # An Amalgalite example showing how Blob's can be utilized
5
+ #
6
+ # We'll make a database with one table, that we store files in. We'll use the
7
+ # Blob incremental IO to store the files and retrieve them from the database
8
+ #
9
+ # This little program will store 1 or more files in the sqlite3 database when
10
+ # the 'store' action is given, and cat a file to stdout on 'retrieve'
11
+ #
12
+ # e.g.
13
+ #
14
+ # ruby blob.rb store a.rb b.rb c.rb # => stores a.rb b.rb and c.rb in the db
15
+ #
16
+ # ruby blob.rb retrieve a.rb # => dumps a.rb to stdout
17
+ #
18
+
19
+ require 'rubygems'
20
+ $: << "../lib"
21
+ $: << "../ext"
22
+ require 'amalgalite'
23
+ require 'amalgalite/packer'
24
+ VALID_ACTIONS = %w[ list retrieve store ]
25
+ def usage
26
+ STDERR.puts "Usage: #{File.basename($0)} ( #{VALID_ACTIONS.join(' | ')} ) file(s)"
27
+ exit 1
28
+ end
29
+
30
+ #
31
+ # This does the basic command line parsing
32
+ #
33
+ usage if ARGV.size < 1
34
+ action = ARGV.shift
35
+ usage unless VALID_ACTIONS.include? action
36
+ file_list = ARGV
37
+
38
+ #
39
+ # create the database if it doesn't exist
40
+ #
41
+ db = Amalgalite::Database.new( "filestore.db" )
42
+
43
+ case action
44
+ #
45
+ # list all the files that are stored in the database
46
+ #
47
+ when 'list'
48
+ db.execute("SELECT filename FROM rubylibs") do |row|
49
+ puts row['filename']
50
+ end
51
+
52
+ #
53
+ # if we are doing the store action, then loop over the files and store them in
54
+ # the database. This will use incremental IO to store the files directly from
55
+ # the file names.
56
+ #
57
+ # It is slightly strange in that you have to tell the Blob object what column
58
+ # it is going to, but that is necessary at this point to be able to hook
59
+ # automatically into the lower level incremental blob IO api.
60
+ #
61
+ # This also shows using the $var syntax for binding name sql values in a
62
+ # prepared statement.
63
+ #
64
+ when 'store'
65
+ usage if file_list.empty?
66
+ require 'amalgalite/packer'
67
+
68
+ packer = Amalgalite::Packer.new( :dbfile => 'filestore.db',
69
+ :compressed => false )
70
+ packer.pack( file_list )
71
+
72
+ #
73
+ # dump the file that matches the right path to stdout. This also shows
74
+ # positional sql varible binding using the '?' syntax.
75
+ #
76
+ when 'retrieve'
77
+ usage if file_list.empty?
78
+ db.execute("SELECT id, compressed, filename, contents FROM rubylibs WHERE filename = ?", file_list.first) do |row|
79
+ STDERR.puts "Dumping #{row['filename']} to stdout"
80
+ if row['compressed'] then
81
+ s = row['contents'].to_s
82
+ STDOUT.puts Amalgalite::Packer.gunzip( s )
83
+ else
84
+ row['contents'].write_to_io( STDOUT )
85
+ end
86
+ end
87
+ end
88
+ db.close
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # An example of requiring all the files in a table via the Bootstrap::lift
4
+ # method.
5
+ #
6
+ # First use the blob.rb example in this same directory to load the a.rb file
7
+ # into an example database:
8
+ #
9
+ # ruby blob.rb store a.rb
10
+ #
11
+ # Then run this example.
12
+ #
13
+
14
+ # Require "ONLY" then binary extension, do not +require+ the ruby based code
15
+ $: << "../ext"
16
+ require 'amalgalite/amalgalite3'
17
+
18
+ # See what the load path is, notice that it is very small
19
+ puts "Before $\" : #{$LOADED_FEATURES.inspect}"
20
+
21
+ # tell the binary extension to "require" every file in the filestore.db in the
22
+ # table 'files' orderd by column 'id'. The 'path' column is added to $LOADED_FEATURES and the
23
+ # code in 'data' is evaled.
24
+ Amalgalite::Requires::Bootstrap.lift( "dbfile" => "filestore.db",
25
+ "table_name" => "rubylibs",
26
+ "rowid_column" => "id",
27
+ "filename_column" => "filename",
28
+ "contents_column" => "contents" )
29
+
30
+ # Notice that a.rb is in the list of files that has been required
31
+ puts "After $\" : #{$LOADED_FEATURES.inspect}"
32
+
33
+ # and look we prove that the code was eval'd appropriately
34
+ a = A.new
35
+ a.a
36
+
@@ -0,0 +1,75 @@
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 atest( words )" )
13
+
14
+ #------------------------------------------------------------------------------
15
+ # Create unique word count aggregate
16
+ #------------------------------------------------------------------------------
17
+ class UniqueWordCount < ::Amalgalite::Aggregate
18
+ attr_accessor :words
19
+
20
+ def initialize
21
+ @name = 'unique_word_count'
22
+ @arity = 1
23
+ @words = Hash.new { |h,k| h[k] = 0 }
24
+ end
25
+
26
+ def step( str )
27
+ str.split(/\W+/).each do |word|
28
+ words[ word.downcase ] += 1
29
+ end
30
+ return nil
31
+ end
32
+
33
+ def finalize
34
+ return words.size
35
+ end
36
+ end
37
+
38
+ db.define_aggregate( 'unique_word_count', UniqueWordCount )
39
+
40
+ #------------------------------------------------------------------------------
41
+ # Now we have a new aggregate function, lets insert some rows into the database
42
+ # and see what we can find.
43
+ #------------------------------------------------------------------------------
44
+ sql = "INSERT INTO atest( words ) VALUES( ? )"
45
+ verify = {}
46
+ db.prepare( sql ) do |stmt|
47
+ DATA.each do |words|
48
+ words.strip!
49
+ puts "Inserting #{words}"
50
+ stmt.execute( words )
51
+ words.split(/\W+/).each { |w| verify[w] = true }
52
+ end
53
+ end
54
+
55
+ #------------------------------------------------------------------------------
56
+ # And show the results
57
+ #------------------------------------------------------------------------------
58
+ puts
59
+ puts "Getting results..."
60
+ puts
61
+ all_rows = db.execute("SELECT unique_word_count( words ) AS uwc FROM atest")
62
+ puts "#{all_rows.first['uwc']} unique words found"
63
+ puts "#{verify.size} unique words to verify"
64
+
65
+ __END__
66
+ some random
67
+ words with
68
+ which
69
+ to play
70
+ and there should
71
+ be a couple of different
72
+ words that appear
73
+ more than once and
74
+ some that appear only
75
+ once