summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/vendor/groonga/lib/mrb/scripts
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
commita175314c3e5827eb193872241446f2f8f5c9d33c (patch)
treecd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/mroonga/vendor/groonga/lib/mrb/scripts
parentInitial commit. (diff)
downloadmariadb-10.5-upstream.tar.xz
mariadb-10.5-upstream.zip
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'storage/mroonga/vendor/groonga/lib/mrb/scripts')
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am16
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/accessor.rb5
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb28
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command.rb64
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command_input.rb15
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/grndb.rb452
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line_parser.rb168
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb85
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb30
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb205
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/database.rb65
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/error.rb16
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb18
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb68
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriter.rb22
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriters.rb41
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_size_estimator.rb54
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree.rb9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/accessor.rb14
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/binary_operation.rb67
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/constant.rb22
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/function_call.rb66
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/index_column.rb14
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/logical_operation.rb33
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/options.rb14
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/procedure.rb18
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/sources.am10
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/variable.rb18
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree_builder.rb111
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/fixed_size_column.rb5
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/id.rb12
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/index_column.rb39
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/index_cursor.rb18
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb10
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/post.rb28
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/pre.rb3
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb40
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb18
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/operator.rb22
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/plugin_loader.rb14
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger.rb9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/flag.rb39
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/record.rb38
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/require.rb71
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb38
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb577
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb324
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data_size_estimator.rb185
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_search_index.rb9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am37
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/table.rb144
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/table_cursor.rb28
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/variable_size_column.rb5
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/writer.rb21
66 files changed, 3582 insertions, 0 deletions
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am
new file mode 100644
index 00000000..835d881b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = \
+ command_line \
+ context \
+ expression_tree \
+ initialize \
+ logger \
+ query_logger
+
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/accessor.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/accessor.rb
new file mode 100644
index 00000000..3ae08fb6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/accessor.rb
@@ -0,0 +1,5 @@
+module Groonga
+ class Accessor
+ include Indexable
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb
new file mode 100644
index 00000000..34f95e96
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb
@@ -0,0 +1,28 @@
+module Groonga
+ class BacktraceEntry
+ class << self
+ def parse(entry)
+ match_data = /:(\d+):?/.match(entry)
+ file = match_data.pre_match
+ line = match_data[1].to_i
+ detail_match_data = /\A(in )?(\S+)\s*/.match(match_data.post_match)
+ if detail_match_data[1]
+ method = detail_match_data[2]
+ message = detail_match_data.post_match
+ else
+ method = ""
+ message = match_data.post_match
+ end
+ new(file, line, method, message)
+ end
+ end
+
+ attr_reader :file, :line, :method, :message
+ def initialize(file, line, method, message)
+ @file = file
+ @line = line
+ @method = method
+ @message = message
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command.rb
new file mode 100644
index 00000000..26dfe68c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command.rb
@@ -0,0 +1,64 @@
+module Groonga
+ class Command
+ @@classes = {}
+ class << self
+ def register_class(name, klass)
+ @@classes[name] = klass
+ end
+
+ def find_class(name)
+ @@classes[name]
+ end
+ end
+
+ private
+ def context
+ @context ||= Context.instance
+ end
+
+ def writer
+ @writer ||= context.writer
+ end
+
+ def query_logger
+ @query_logger ||= context.query_logger
+ end
+
+ def cache_key(input)
+ nil
+ end
+
+ def cache_output(key, options={})
+ if key.nil?
+ yield
+ else
+ cache = Cache.current
+ cached_value = cache.fetch(key)
+ if cached_value
+ context.output = cached_value
+ query_logger.log(:cache, ":", "cache(#{cached_value.bytesize})")
+ else
+ yield
+ cache.update(key, context.output) if options[:update]
+ end
+ end
+ end
+
+ def run_internal(input)
+ begin
+ options = {
+ :update => (input["cache"] != "no"),
+ }
+ cache_output(cache_key(input), options) do
+ run_body(input)
+ end
+ rescue GroongaError => groonga_error
+ context.set_groonga_error(groonga_error)
+ nil
+ rescue => error
+ context.record_error(:command_error, error)
+ nil
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_input.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_input.rb
new file mode 100644
index 00000000..779edb47
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_input.rb
@@ -0,0 +1,15 @@
+module Groonga
+ class CommandInput
+ include Enumerable
+
+ def each
+ args = arguments
+ arg = Record.new(args, nil)
+ args.each do |id|
+ arg.id = id
+ key = arg.key
+ yield(key, self[key])
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/Makefile.am
new file mode 100644
index 00000000..8d580810
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_command_linedir = $(ruby_scriptsdir)/command_line
+ruby_scripts_command_line_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/grndb.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/grndb.rb
new file mode 100644
index 00000000..edd16d58
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/grndb.rb
@@ -0,0 +1,452 @@
+module Groonga
+ module CommandLine
+ class Grndb
+ def initialize(argv)
+ @program_path, *@arguments = argv
+ @succeeded = true
+ @database_path = nil
+ end
+
+ def run
+ command_line_parser = create_command_line_parser
+ options = nil
+ begin
+ options = command_line_parser.parse(@arguments)
+ rescue Slop::Error => error
+ $stderr.puts(error.message)
+ $stderr.puts
+ $stderr.puts(command_line_parser.help_message)
+ return false
+ end
+ @succeeded
+ end
+
+ private
+ def create_command_line_parser
+ program_name = File.basename(@program_path)
+ parser = CommandLineParser.new(program_name)
+
+ parser.add_command("check") do |command|
+ command.description = "Check database"
+
+ options = command.options
+ options.banner += " DB_PATH"
+ options.string("--target", "Check only the target object.")
+
+ command.add_action do |options|
+ open_database(command, options) do |database, rest_arguments|
+ check(database, options, rest_arguments)
+ end
+ end
+ end
+
+ parser.add_command("recover") do |command|
+ command.description = "Recover database"
+
+ options = command.options
+ options.banner += " DB_PATH"
+ options.boolean("--force-truncate", "Force to truncate corrupted objects.")
+
+ command.add_action do |options|
+ open_database(command, options) do |database, rest_arguments|
+ recover(database, options, rest_arguments)
+ end
+ end
+ end
+
+ parser
+ end
+
+ def open_database(command, options)
+ arguments = options.arguments
+ if arguments.empty?
+ $stderr.puts("Database path is missing")
+ $stderr.puts
+ $stderr.puts(command.help_message)
+ @succeesed = false
+ return
+ end
+
+ database = nil
+ @database_path, *rest_arguments = arguments
+ begin
+ database = Database.open(@database_path)
+ rescue Error => error
+ $stderr.puts("Failed to open database: <#{@database_path}>")
+ $stderr.puts(error.message)
+ @succeeded = false
+ return
+ end
+
+ begin
+ yield(database, rest_arguments)
+ ensure
+ database.close
+ end
+ end
+
+ def failed(*messages)
+ messages.each do |message|
+ $stderr.puts(message)
+ end
+ @succeeded = false
+ end
+
+ def recover(database, options, arguments)
+ recoverer = Recoverer.new
+ recoverer.database = database
+ recoverer.force_truncate = options[:force_truncate]
+ begin
+ recoverer.recover
+ rescue Error => error
+ failed("Failed to recover database: <#{@database_path}>",
+ error.message)
+ end
+ end
+
+ def check(database, options, arguments)
+ checker = Checker.new
+ checker.program_path = @program_path
+ checker.database_path = @database_path
+ checker.database = database
+ checker.on_failure = lambda do |message|
+ failed(message)
+ end
+
+ checker.check_database
+
+ target_name = options[:target]
+ if target_name
+ checker.check_one(target_name)
+ else
+ checker.check_all
+ end
+ end
+
+ class Checker
+ attr_writer :program_path
+ attr_writer :database_path
+ attr_writer :database
+ attr_writer :on_failure
+
+ def initialize
+ @context = Context.instance
+ @checked = {}
+ end
+
+ def check_database
+ check_database_orphan_inspect
+ check_database_locked
+ check_database_corrupt
+ check_database_dirty
+ end
+
+ def check_one(target_name)
+ target = @context[target_name]
+ if target.nil?
+ exist_p = open_database_cursor do |cursor|
+ cursor.any? do
+ cursor.key == target_name
+ end
+ end
+ if exist_p
+ failed_to_open(target_name)
+ else
+ message = "[#{target_name}] Not exist."
+ failed(message)
+ end
+ return
+ end
+
+ check_object_recursive(target)
+ end
+
+ def check_all
+ open_database_cursor do |cursor|
+ cursor.each do |id|
+ next if ID.builtin?(id)
+ next if builtin_object_name?(cursor.key)
+ next if @context[id]
+ failed_to_open(cursor.key)
+ end
+ end
+
+ @database.each do |object|
+ check_object(object)
+ end
+ end
+
+ private
+ def check_database_orphan_inspect
+ open_database_cursor do |cursor|
+ cursor.each do |id|
+ if cursor.key == "inspect" and @context[id].nil?
+ message =
+ "Database has orphan 'inspect' object. " +
+ "Remove it by '#{@program_path} recover #{@database_path}'."
+ failed(message)
+ break
+ end
+ end
+ end
+ end
+
+ def check_database_locked
+ return unless @database.locked?
+
+ message =
+ "Database is locked. " +
+ "It may be broken. " +
+ "Re-create the database."
+ failed(message)
+ end
+
+ def check_database_corrupt
+ return unless @database.corrupt?
+
+ message =
+ "Database is corrupt. " +
+ "Re-create the database."
+ failed(message)
+ end
+
+ def check_database_dirty
+ return unless @database.dirty?
+
+ last_modified = @database.last_modified
+ if File.stat(@database.path).mtime > last_modified
+ return
+ end
+
+ open_database_cursor do |cursor|
+ cursor.each do |id|
+ next if ID.builtin?(id)
+ path = "%s.%07x" % [@database.path, id]
+ next unless File.exist?(path)
+ return if File.stat(path).mtime > last_modified
+ end
+ end
+
+ message =
+ "Database wasn't closed successfully. " +
+ "It may be broken. " +
+ "Re-create the database."
+ failed(message)
+ end
+
+ def check_object(object)
+ return if @checked.key?(object.id)
+ @checked[object.id] = true
+
+ check_object_locked(object)
+ check_object_corrupt(object)
+ end
+
+ def check_object_locked(object)
+ case object
+ when IndexColumn
+ return unless object.locked?
+ message =
+ "[#{object.name}] Index column is locked. " +
+ "It may be broken. " +
+ "Re-create index by '#{@program_path} recover #{@database_path}'."
+ failed(message)
+ when Column
+ return unless object.locked?
+ name = object.name
+ message =
+ "[#{name}] Data column is locked. " +
+ "It may be broken. " +
+ "(1) Truncate the column (truncate #{name}) or " +
+ "clear lock of the column (lock_clear #{name}) " +
+ "and (2) load data again."
+ failed(message)
+ when Table
+ return unless object.locked?
+ name = object.name
+ message =
+ "[#{name}] Table is locked. " +
+ "It may be broken. " +
+ "(1) Truncate the table (truncate #{name}) or " +
+ "clear lock of the table (lock_clear #{name}) " +
+ "and (2) load data again."
+ failed(message)
+ end
+ end
+
+ def check_object_corrupt(object)
+ case object
+ when IndexColumn
+ return unless object.corrupt?
+ message =
+ "[#{object.name}] Index column is corrupt. " +
+ "Re-create index by '#{@program_path} recover #{@database_path}'."
+ failed(message)
+ when Column
+ return unless object.corrupt?
+ name = object.name
+ message =
+ "[#{name}] Data column is corrupt. " +
+ "(1) Truncate the column (truncate #{name} or " +
+ "'#{@program_path} recover --force-truncate #{@database_path}') " +
+ "and (2) load data again."
+ failed(message)
+ when Table
+ return unless object.corrupt?
+ name = object.name
+ message =
+ "[#{name}] Table is corrupt. " +
+ "(1) Truncate the table (truncate #{name} or " +
+ "'#{@program_path} recover --force-truncate #{@database_path}') " +
+ "and (2) load data again."
+ failed(message)
+ end
+ end
+
+ def check_object_recursive(target)
+ return if @checked.key?(target.id)
+
+ check_object(target)
+ case target
+ when Table
+ unless target.is_a?(Groonga::Array)
+ domain_id = target.domain_id
+ domain = @context[domain_id]
+ if domain.nil?
+ record = Record.new(@database, domain_id)
+ failed_to_open(record.key)
+ elsif domain.is_a?(Table)
+ check_object_recursive(domain)
+ end
+ end
+
+ target.column_ids.each do |column_id|
+ column = @context[column_id]
+ if column.nil?
+ record = Record.new(@database, column_id)
+ failed_to_open(record.key)
+ else
+ check_object_recursive(column)
+ end
+ end
+ when FixedSizeColumn, VariableSizeColumn
+ range_id = target.range_id
+ range = @context[range_id]
+ if range.nil?
+ record = Record.new(@database, range_id)
+ failed_to_open(record.key)
+ elsif range.is_a?(Table)
+ check_object_recursive(range)
+ end
+
+ lexicon_ids = []
+ target.indexes.each do |index_info|
+ index = index_info.index
+ lexicon_ids << index.domain_id
+ check_object(index)
+ end
+ lexicon_ids.uniq.each do |lexicon_id|
+ lexicon = @context[lexicon_id]
+ if lexicon.nil?
+ record = Record.new(@database, lexicon_id)
+ failed_to_open(record.key)
+ else
+ check_object(lexicon)
+ end
+ end
+ when IndexColumn
+ range_id = target.range_id
+ range = @context[range_id]
+ if range.nil?
+ record = Record.new(@database, range_id)
+ failed_to_open(record.key)
+ return
+ end
+ check_object(range)
+
+ target.source_ids.each do |source_id|
+ source = @context[source_id]
+ if source.nil?
+ record = Record.new(database, source_id)
+ failed_to_open(record.key)
+ elsif source.is_a?(Column)
+ check_object_recursive(source)
+ end
+ end
+ end
+ end
+
+ def open_database_cursor(&block)
+ flags =
+ TableCursorFlags::ASCENDING |
+ TableCursorFlags::BY_ID
+ TableCursor.open(@database, :flags => flags, &block)
+ end
+
+ def builtin_object_name?(name)
+ case name
+ when "inspect"
+ # Just for compatibility. It's needed for users who used
+ # Groonga master at between 2016-02-03 and 2016-02-26.
+ true
+ else
+ false
+ end
+ end
+
+ def failed(message)
+ @on_failure.call(message)
+ end
+
+ def failed_to_open(name)
+ message =
+ "[#{name}] Can't open object. " +
+ "It's broken. " +
+ "Re-create the object or the database."
+ failed(message)
+ end
+ end
+
+ class Recoverer
+ attr_writer :database
+ attr_writer :force_truncate
+
+ def initialize
+ @context = Context.instance
+ end
+
+ def recover
+ if @force_truncate
+ truncate_corrupt_objects
+ end
+ @database.recover
+ end
+
+ def truncate_corrupt_objects
+ @database.each do |object|
+ next unless object.corrupt?
+ logger = @context.logger
+ object_path = object.path
+ object_dirname = File.dirname(object_path)
+ object_basename = File.basename(object_path)
+ object.truncate
+ Dir.foreach(object_dirname) do |path|
+ if path.start_with?("#{object_basename}.")
+ begin
+ File.unlink("#{object_dirname}/#{path}")
+ message = "Corrupted <#{object_path}> related file is removed: <#{path}>"
+ $stdout.puts(message)
+ logger.log(Logger::Level::INFO.to_i, __FILE__, __LINE__, "truncate_corrupt_objects", message)
+ rescue Error => error
+ message = "Failed to remove file which is related to corrupted <#{object_path}>: <#{path}>"
+ $stderr.puts(message)
+ logger.log_error(message)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/sources.am
new file mode 100644
index 00000000..759948ee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line/sources.am
@@ -0,0 +1,2 @@
+RUBY_SCRIPT_FILES = \
+ grndb.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line_parser.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line_parser.rb
new file mode 100644
index 00000000..7dad55a7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/command_line_parser.rb
@@ -0,0 +1,168 @@
+module Slop
+ class MissingCommand < Error
+ end
+
+ class UnknownCommand < Error
+ attr_reader :name
+
+ def initialize(msg, name)
+ super(msg)
+ @name = name
+ end
+ end
+end
+
+module Groonga
+ class CommandLineParser
+ def initialize(program_name=nil)
+ $0 ||= nil
+ @program_name = program_name || $0
+ @commands = []
+ @action_runner = ActionRunner.new
+ @options = Slop::Options.new
+ setup_options
+ end
+
+ def options
+ yield(@options) if block_given?
+ @options
+ end
+
+ def add_command(name)
+ command = Command.new(@program_name, name)
+ setup_common_options(command.options)
+ yield(command)
+ @commands << command
+ end
+
+ def add_action(&action)
+ @action_runner.add(&action)
+ end
+
+ def parse(command_line)
+ if @commands.empty?
+ result = @options.parse(command_line)
+ apply_actions(result)
+ return result
+ end
+
+ if command_line.empty?
+ message = "Command is missing"
+ raise Slop::MissingCommand.new(message)
+ end
+
+ first_argument = command_line.first
+ if first_argument.start_with?("-")
+ result = @options.parse(command_line)
+ apply_actions(result)
+ return result
+ end
+
+ command_name = first_argument
+ command = find_command(command_name)
+ if command.nil?
+ message = "Unknown command: <#{command_name}>"
+ raise Slop::UnknownCommand.new(message, command_name)
+ end
+
+ command.parse(command_line[1..-1])
+ end
+
+ def help_message
+ message = @options.to_s
+ @commands.each do |command|
+ message << "\n"
+ indent = " " * 4
+ message << "#{command.description}:\n" if command.description
+ message << "#{indent}#{command.options.banner}\n"
+ end
+ message
+ end
+
+ private
+ def setup_options
+ @options.banner = "Usage: #{@program_name} [OPTIONS]"
+ setup_common_options(@options)
+ @options.on("-h", "--help", "Display this help message.",
+ :tail => true) do
+ $stdout.puts(help_message)
+ end
+ end
+
+ def setup_common_options(options)
+ options.string("--log-path",
+ "Change log path (#{Logger.default_path})",
+ default: Logger.default_path)
+ default_log_level = Logger.default_level
+ options.string("--log-level",
+ "Change log level (#{default_log_level.name})",
+ default: default_log_level)
+ end
+
+ def find_command(name)
+ @commands.find do |command|
+ command.name == name
+ end
+ end
+
+ def apply_actions(result)
+ @action_runner.run(result) unless result.help?
+ end
+
+ class ActionRunner
+ def initialize
+ @actions = []
+ end
+
+ def add(&action)
+ @actions << action
+ end
+
+ def run(*args)
+ @actions.each do |action|
+ action.call(*args)
+ end
+ end
+ end
+
+ class Command
+ attr_reader :name
+ attr_reader :options
+ attr_accessor :description
+ def initialize(program_name, name)
+ @program_name = program_name
+ @name = name
+ @options = Slop::Options.new
+ setup_options
+ @description = nil
+ @action_runner = ActionRunner.new
+ end
+
+ def add_action(&action)
+ @action_runner.add(&action)
+ end
+
+ def parse(command_line)
+ result = @options.parse(command_line)
+ @action_runner.run(result) unless result.help?
+ result
+ end
+
+ def help_message
+ message = ""
+ message << "Description: #{@description}\n" if @description
+ message << @options.to_s
+ message
+ end
+
+ private
+ def setup_options
+ @options.banner = "Usage: #{@program_name} #{@name} [OPTIONS]"
+ @options.on("-h", "--help", "Display this help message.",
+ :tail => true) do
+ $stdout.puts(help_message)
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb
new file mode 100644
index 00000000..b6f59dc3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb
@@ -0,0 +1,85 @@
+require "context/error_level"
+require "context/rc"
+
+module Groonga
+ class Context
+ def guard(fallback=nil)
+ begin
+ yield
+ rescue => error
+ logger.log_error(error)
+ fallback
+ end
+ end
+
+ def logger
+ @logger ||= Logger.new
+ end
+
+ def query_logger
+ @query_logger ||= QueryLogger.new
+ end
+
+ def writer
+ @writer ||= Writer.new
+ end
+
+ def set_groonga_error(groonga_error)
+ set_error_raw(groonga_error.class.rc,
+ ErrorLevel::ERROR,
+ groonga_error.message,
+ groonga_error.backtrace)
+ end
+
+ def record_error(rc, error)
+ rc = RC.find(rc) if rc.is_a?(Symbol)
+ set_error_raw(rc, ErrorLevel::ERROR, error.message, error.backtrace)
+
+ logger.log_error(error)
+ end
+
+ def with_command_version(version)
+ old_version = command_version
+ begin
+ self.command_version = version
+ yield
+ ensure
+ self.command_version = old_version
+ end
+ end
+
+ def open_temporary(id)
+ if Thread.limit == 1
+ need_close = !opened?(id)
+ else
+ need_close = false
+ end
+ object = self[id]
+ begin
+ yield(object)
+ ensure
+ if need_close and object and !object.closed?
+ case object
+ when Table, Column
+ object.close
+ end
+ end
+ end
+ end
+
+ private
+ def set_error_raw(rc, error_level, message, backtrace)
+ self.rc = rc.to_i
+ self.error_level = error_level.to_i
+
+ self.error_message = message
+
+ if backtrace
+ entry = BacktraceEntry.parse(backtrace.first)
+ self.error_file = entry.file
+ self.error_line = entry.line
+ self.error_method = entry.method
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/Makefile.am
new file mode 100644
index 00000000..8d862082
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_contextdir = $(ruby_scriptsdir)/context
+ruby_scripts_context_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb
new file mode 100644
index 00000000..c0685f16
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb
@@ -0,0 +1,30 @@
+module Groonga
+ class Context
+ class ErrorLevel
+ @@names = {}
+
+ class << self
+ def find(name)
+ @@names[name]
+ end
+ end
+
+ attr_reader :name
+ def initialize(name, level)
+ @@names[name] = self
+ @name = name
+ @level = level
+ end
+
+ def to_i
+ @level
+ end
+
+ EMERGENCY = new(:emergency, 1)
+ ALERT = new(:alert, 2)
+ CRITICAL = new(:critical, 3)
+ ERROR = new(:error, 4)
+ WARNING = new(:warning, 5)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb
new file mode 100644
index 00000000..45187c22
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb
@@ -0,0 +1,205 @@
+module Groonga
+ class Context
+ class RC
+ @@codes = {}
+ @@names = {}
+
+ class << self
+ def find(name_or_code)
+ if name_or_code.is_a?(Symbol)
+ @@names[name_or_code] || UNKNOWN_ERROR
+ else
+ @@codes[name_or_code] || UNKNOWN_ERROR
+ end
+ end
+
+ def register(name, code, error_class)
+ rc = new(name, code, error_class)
+ @@codes[code] = rc
+ @@names[name] = rc
+ error_class.rc = rc if error_class
+ rc
+ end
+ end
+
+ attr_reader :name
+ attr_reader :error_class
+ def initialize(name, code, error_class)
+ @name = name
+ @code = code
+ @error_class = error_class
+ end
+
+ def to_i
+ @code
+ end
+
+ SUCCESS =
+ register(:success, 0, nil)
+ END_OF_DATA =
+ register(:end_of_data, 1, EndOfData)
+ UNKNOWN_ERROR =
+ register(:unknown_error, -1, UnknownError)
+ OPERATION_NOT_PERMITTED =
+ register(:operation_not_permitted, -2, OperationNotPermitted)
+ NO_SUCH_FILE_OR_DIRECTORY =
+ register(:no_such_file_or_directory, -3, NoSuchFileOrDirectory)
+ NO_SUCH_PROCESS =
+ register(:no_such_process, -4, NoSuchProcess)
+ INTERRUPTED_FUNCTION_CALL =
+ register(:interrupted_function_call, -5, InterruptedFunctionCall)
+ INPUT_OUTPUT_ERROR =
+ register(:input_output_error, -6, InputOutputError)
+ NO_SUCH_DEVICE_OR_ADDRESS =
+ register(:no_such_device_or_address, -7, NoSuchDeviceOrAddress)
+ ARG_LIST_TOO_LONG =
+ register(:arg_list_too_long, -8, ArgListTooLong)
+ EXEC_FORMAT_ERROR =
+ register(:exec_format_error, -9, ExecFormatError)
+ BAD_FILE_DESCRIPTOR =
+ register(:bad_file_descriptor, -10, BadFileDescriptor)
+ NO_CHILD_PROCESSES =
+ register(:no_child_processes, -11, NoChildProcesses)
+ RESOURCE_TEMPORARILY_UNAVAILABLE =
+ register(:resource_temporarily_unavailable, -12,
+ ResourceTemporarilyUnavailable)
+ NOT_ENOUGH_SPACE =
+ register(:not_enough_space, -13, NotEnoughSpace)
+ PERMISSION_DENIED =
+ register(:permission_denied, -14, PermissionDenied)
+ BAD_ADDRESS =
+ register(:bad_address, -15, BadAddress)
+ RESOURCE_BUSY =
+ register(:resource_busy, -16, ResourceBusy)
+ FILE_EXISTS =
+ register(:file_exists, -17, FileExists)
+ IMPROPER_LINK =
+ register(:improper_link, -18, ImproperLink)
+ NO_SUCH_DEVICE =
+ register(:no_such_device, -19, NoSuchDevice)
+ NOT_DIRECTORY =
+ register(:not_directory, -20, NotDirectory)
+ IS_DIRECTORY =
+ register(:is_directory, -21, IsDirectory)
+ INVALID_ARGUMENT =
+ register(:invalid_argument, -22, InvalidArgument)
+ TOO_MANY_OPEN_FILES_IN_SYSTEM =
+ register(:too_many_open_files_in_system, -23, TooManyOpenFilesInSystem)
+ TOO_MANY_OPEN_FILES =
+ register(:too_many_open_files, -24, TooManyOpenFiles)
+ INAPPROPRIATE_IO_CONTROL_OPERATION =
+ register(:inappropriate_io_control_operation, -25,
+ InappropriateIOControlOperation)
+ FILE_TOO_LARGE =
+ register(:file_too_large, -26, FileTooLarge)
+ NO_SPACE_LEFT_ON_DEVICE =
+ register(:no_space_left_on_device, -27, NoSpaceLeftOnDevice)
+ INVALID_SEEK =
+ register(:invalid_seek, -28, InvalidSeek)
+ READ_ONLY_FILE_SYSTEM =
+ register(:read_only_file_system, -29, ReadOnlyFileSystem)
+ TOO_MANY_LINKS =
+ register(:too_many_links, -30, TooManyLinks)
+ BROKEN_PIPE =
+ register(:broken_pipe, -31, BrokenPipe)
+ DOMAIN_ERROR =
+ register(:domain_error, -32, DomainError)
+ RESULT_TOO_LARGE =
+ register(:result_too_large, -33, ResultTooLarge)
+ RESOURCE_DEADLOCK_AVOIDED =
+ register(:resource_deadlock_avoided, -34, ResourceDeadlockAvoided)
+ NO_MEMORY_AVAILABLE =
+ register(:no_memory_available, -35, NoMemoryAvailable)
+ FILENAME_TOO_LONG =
+ register(:filename_too_long, -36, FilenameTooLong)
+ NO_LOCKS_AVAILABLE =
+ register(:no_locks_available, -37, NoLocksAvailable)
+ FUNCTION_NOT_IMPLEMENTED =
+ register(:function_not_implemented, -38, FunctionNotImplemented)
+ DIRECTORY_NOT_EMPTY =
+ register(:directory_not_empty, -39, DirectoryNotEmpty)
+ ILLEGAL_BYTE_SEQUENCE =
+ register(:illegal_byte_sequence, -40, IllegalByteSequence)
+ SOCKET_NOT_INITIALIZED =
+ register(:socket_not_initialized, -41, SocketNotInitialized)
+ OPERATION_WOULD_BLOCK =
+ register(:operation_would_block, -42, OperationWouldBlock)
+ ADDRESS_IS_NOT_AVAILABLE =
+ register(:address_is_not_available, -43, AddressIsNotAvailable)
+ NETWORK_IS_DOWN =
+ register(:network_is_down, -44, NetworkIsDown)
+ NO_BUFFER =
+ register(:no_buffer, -45, NoBuffer)
+ SOCKET_IS_ALREADY_CONNECTED =
+ register(:socket_is_already_connected, -46, SocketIsAlreadyConnected)
+ SOCKET_IS_NOT_CONNECTED =
+ register(:socket_is_not_connected, -47, SocketIsNotConnected)
+ SOCKET_IS_ALREADY_SHUTDOWNED =
+ register(:socket_is_already_shutdowned, -48, SocketIsAlreadyShutdowned)
+ OPERATION_TIMEOUT =
+ register(:operation_timeout, -49, OperationTimeout)
+ CONNECTION_REFUSED =
+ register(:connection_refused, -50, ConnectionRefused)
+ RANGE_ERROR =
+ register(:range_error, -51, RangeError)
+ TOKENIZER_ERROR =
+ register(:tokenizer_error, -52, TokenizerError)
+ FILE_CORRUPT =
+ register(:file_corrupt, -53, FileCorrupt)
+ INVALID_FORMAT =
+ register(:invalid_format, -54, InvalidFormat)
+ OBJECT_CORRUPT =
+ register(:object_corrupt, -55, ObjectCorrupt)
+ TOO_MANY_SYMBOLIC_LINKS =
+ register(:too_many_symbolic_links, -56, TooManySymbolicLinks)
+ NOT_SOCKET =
+ register(:not_socket, -57, NotSocket)
+ OPERATION_NOT_SUPPORTED =
+ register(:operation_not_supported, -58, OperationNotSupported)
+ ADDRESS_IS_IN_USE =
+ register(:address_is_in_use, -59, AddressIsInUse)
+ ZLIB_ERROR =
+ register(:zlib_error, -60, ZlibError)
+ LZ4_ERROR =
+ register(:lz4_error, -61, LZ4Error)
+ STACK_OVER_FLOW =
+ register(:stack_over_flow, -62, StackOverFlow)
+ SYNTAX_ERROR =
+ register(:syntax_error, -63, SyntaxError)
+ RETRY_MAX =
+ register(:retry_max, -64, RetryMax)
+ INCOMPATIBLE_FILE_FORMAT =
+ register(:incompatible_file_format, -65, IncompatibleFileFormat)
+ UPDATE_NOT_ALLOWED =
+ register(:update_not_allowed, -66, UpdateNotAllowed)
+ TOO_SMALL_OFFSET =
+ register(:too_small_offset, -67, TooSmallOffset)
+ TOO_LARGE_OFFSET =
+ register(:too_large_offset, -68, TooLargeOffset)
+ TOO_SMALL_LIMIT =
+ register(:too_small_limit, -69, TooSmallLimit)
+ CAS_ERROR =
+ register(:cas_error, -70, CASError)
+ UNSUPPORTED_COMMAND_VERSION =
+ register(:unsupported_command_version, -71, UnsupportedCommandVersion)
+ NORMALIZER_ERROR =
+ register(:normalizer_error, -72, NormalizerError)
+ TOKEN_FILTER_ERROR =
+ register(:token_filter_error, -73, TokenFilterError)
+ COMMAND_ERROR =
+ register(:command_error, -74, CommandError)
+ PLUGIN_ERROR =
+ register(:plugin_error, -75, PluginError)
+ SCORER_ERROR =
+ register(:scorer_error, -76, ScorerError)
+ CANCEL =
+ register(:cancel, -77, Cancel)
+ WINDOW_FUNCTION_ERROR =
+ register(:window_function_error, -78, WindowFunctionError)
+ ZSTD_ERROR =
+ register(:zstd_error, -79, ZstdError)
+
+ GroongaError.rc = UNKNOWN_ERROR
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/sources.am
new file mode 100644
index 00000000..1f0d1624
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/sources.am
@@ -0,0 +1,3 @@
+RUBY_SCRIPT_FILES = \
+ error_level.rb \
+ rc.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/database.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/database.rb
new file mode 100644
index 00000000..e0c6b4a0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/database.rb
@@ -0,0 +1,65 @@
+module Groonga
+ class Database
+ def each_raw(options=nil)
+ return to_enum(__method__, options) unless block_given?
+
+ if options
+ min = options[:prefix]
+ order = options[:order]
+ order_by = options[:order_by]
+ else
+ min = nil
+ order = :ascending
+ order_by = :id
+ end
+ flags = 0
+
+ if order == :descending
+ flags |= TableCursorFlags::DESCENDING
+ else
+ flags |= TableCursorFlags::ASCENDING
+ end
+ if order_by == :id
+ flags |= TableCursorFlags::BY_ID
+ else
+ flags |= TableCursorFlags::BY_KEY
+ end
+ flags |= TableCursorFlags::PREFIX if min
+ TableCursor.open(self, :min => min, :flags => flags) do |cursor|
+ cursor.each do |id|
+ yield(id, cursor)
+ end
+ end
+ end
+
+ def each(options=nil)
+ return to_enum(__method__, options) unless block_given?
+
+ context = Context.instance
+ each_raw(options) do |id, cursor|
+ object = context[id]
+ yield(object) if object
+ end
+ end
+
+ def each_name(options=nil)
+ return to_enum(__method__, options) unless block_given?
+
+ each_raw(options) do |id, cursor|
+ name = cursor.key
+ yield(name)
+ end
+ end
+
+ def each_table(options={})
+ return to_enum(__method__, options) unless block_given?
+
+ context = Context.instance
+ each_name(options) do |name|
+ next if name.include?(".")
+ object = context[name]
+ yield(object) if object.is_a?(Table)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/error.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/error.rb
new file mode 100644
index 00000000..e39c9045
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/error.rb
@@ -0,0 +1,16 @@
+module Groonga
+ class GroongaError
+ class << self
+ def rc
+ @rc
+ end
+
+ def rc=(rc)
+ @rc = rc
+ end
+ end
+ end
+
+ class ErrorMessage < Error
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb
new file mode 100644
index 00000000..05c7ee8d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb
@@ -0,0 +1,18 @@
+module Groonga
+ class EvalContext
+ def eval(script)
+ proc = compile(script)
+ instance_eval(&proc)
+ end
+
+ def method_missing(id, *args, &block)
+ return super unless args.empty?
+ return super if block_given?
+
+ object = Context.instance[id.to_s]
+ return super if object.nil?
+
+ object
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb
new file mode 100644
index 00000000..27dadcb7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb
@@ -0,0 +1,68 @@
+require "expression_rewriter"
+require "expression_rewriters"
+
+require "expression_tree_builder"
+
+require "scan_info"
+require "scan_info_builder"
+
+require "scan_info_data_size_estimator"
+require "expression_size_estimator"
+
+module Groonga
+ class Expression
+ def rewrite
+ rewritten = nil
+ begin
+ return nil unless ExpressionRewriters.enabled?
+ source = self
+ ExpressionRewriters.classes.each do |rewriter_class|
+ rewriter = rewriter_class.new(source)
+ new_rewritten = rewriter.rewrite
+ if new_rewritten
+ rewritten.close if rewritten
+ rewritten = new_rewritten
+ source = rewritten
+ end
+ end
+ rescue GroongaError => groonga_error
+ context.set_groonga_error(groonga_error)
+ rewritten.close if rewritten
+ rewritten = nil
+ rescue => error
+ context.record_error(:invalid_argument, error)
+ rewritten.close if rewritten
+ rewritten = nil
+ end
+ rewritten
+ end
+
+ def build_scan_info(op, record_exist)
+ begin
+ builder = ScanInfoBuilder.new(self, op, record_exist)
+ builder.build
+ rescue => error
+ context.record_error(:invalid_argument, error)
+ nil
+ end
+ end
+
+ def estimate_size(table)
+ begin
+ estimator = ExpressionSizeEstimator.new(self, table)
+ estimator.estimate
+ rescue GroongaError => groonga_error
+ context.set_groonga_error(groonga_error)
+ table.size
+ rescue => error
+ context.record_error(:unknown_error, error)
+ table.size
+ end
+ end
+
+ private
+ def context
+ @context ||= Context.instance
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriter.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriter.rb
new file mode 100644
index 00000000..8f56ca7c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriter.rb
@@ -0,0 +1,22 @@
+module Groonga
+ class ExpressionRewriter
+ class << self
+ def register(name)
+ ExpressionRewriters.register(name, self)
+ end
+ end
+
+ def initialize(expression)
+ @expression = expression
+ end
+
+ def rewrite
+ nil
+ end
+
+ private
+ def context
+ @context ||= Context.instance
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriters.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriters.rb
new file mode 100644
index 00000000..ae773541
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_rewriters.rb
@@ -0,0 +1,41 @@
+module Groonga
+ module ExpressionRewriters
+ @rewriters = {}
+
+ class << self
+ def register(name, rewriter_class)
+ @rewriters[name] = rewriter_class
+ end
+
+ def enabled?
+ rewriters_table_name =
+ Config["expression_rewriter.table"] || "expression_rewriters"
+ rewriters_table = Context.instance[rewriters_table_name]
+ return false if rewriters_table.nil?
+ return false if rewriters_table.empty?
+
+ true
+ end
+
+ def classes
+ rewriters_table_name =
+ Config["expression_rewriter.table"] || "expression_rewriters"
+ rewriters_table = Context.instance[rewriters_table_name]
+ return [] if rewriters_table.nil?
+
+ rewriters_table.collect do |id|
+ record = Record.new(rewriters_table, id)
+ name = record.key
+ rewriter = @rewriters[name]
+ if rewriter.nil?
+ plugin_name = record.plugin_name.value
+ require plugin_name
+ rewriter = @rewriters[name]
+ raise "unknown rewriter: <#{name}>:<#{plugin_name}>" if rewriter.nil?
+ end
+ rewriter
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_size_estimator.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_size_estimator.rb
new file mode 100644
index 00000000..597b56f9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_size_estimator.rb
@@ -0,0 +1,54 @@
+module Groonga
+ class ExpressionSizeEstimator
+ def initialize(expression, table)
+ @expression = expression
+ @table = table
+ @table_size = @table.size
+ end
+
+ def estimate
+ builder = ScanInfoBuilder.new(@expression, Operator::OR, false)
+ data_list = builder.build
+ return @table_size if data_list.nil?
+
+ current_size = 0
+ sizes = []
+ data_list.each do |data|
+ if (data.flags & ScanInfo::Flags::POP) != 0
+ size = sizes.pop
+ case data.logical_op
+ when Operator::AND, Operator::AND_NOT
+ current_size = size if size < current_size
+ when Operator::OR
+ current_size = size if size > current_size
+ else
+ message = "invalid logical operator: <#{data.logical_op.inspect}>"
+ raise InvalidArgument, message
+ end
+ else
+ if (data.flags & ScanInfo::Flags::PUSH) != 0
+ sizes.push(current_size)
+ current_size = 0
+ end
+
+ estimator = ScanInfoDataSizeEstimator.new(data, @table)
+ size = estimator.estimate
+ case data.logical_op
+ when Operator::AND
+ current_size = size if size < current_size
+ when Operator::AND_NOT
+ size = @table_size - size
+ size = 0 if size < 0
+ current_size = size if size < current_size
+ when Operator::OR
+ current_size = size if size > current_size
+ else
+ message = "invalid logical operator: <#{data.logical_op.inspect}>"
+ raise InvalidArgument, message
+ end
+ end
+ end
+ current_size
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree.rb
new file mode 100644
index 00000000..124ace0e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree.rb
@@ -0,0 +1,9 @@
+require "expression_tree/accessor"
+require "expression_tree/constant"
+require "expression_tree/binary_operation"
+require "expression_tree/function_call"
+require "expression_tree/index_column"
+require "expression_tree/logical_operation"
+require "expression_tree/options"
+require "expression_tree/procedure"
+require "expression_tree/variable"
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/Makefile.am
new file mode 100644
index 00000000..1235932f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_expression_treedir = $(ruby_scriptsdir)/expression_tree
+ruby_scripts_expression_tree_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/accessor.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/accessor.rb
new file mode 100644
index 00000000..ac4b122f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/accessor.rb
@@ -0,0 +1,14 @@
+module Groonga
+ module ExpressionTree
+ class Accessor
+ attr_reader :object
+ def initialize(object)
+ @object = object
+ end
+
+ def build(expression)
+ expression.append_object(@object, Operator::PUSH, 1)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/binary_operation.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/binary_operation.rb
new file mode 100644
index 00000000..d59c65c7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/binary_operation.rb
@@ -0,0 +1,67 @@
+module Groonga
+ module ExpressionTree
+ class BinaryOperation
+ attr_reader :operator
+ attr_reader :left
+ attr_reader :right
+ def initialize(operator, left, right)
+ @operator = operator
+ @left = left
+ @right = right
+ end
+
+ def build(expression)
+ @left.build(expression)
+ @right.build(expression)
+ expression.append_operator(@operator, 2)
+ end
+
+ RANGE_OPERATORS = [
+ Operator::LESS,
+ Operator::GREATER,
+ Operator::LESS_EQUAL,
+ Operator::GREATER_EQUAL,
+ ]
+ def estimate_size(table)
+ case @operator
+ when *RANGE_OPERATORS
+ estimate_size_range(table)
+ else
+ table.size
+ end
+ end
+
+ private
+ def estimate_size_range(table)
+ return table.size unless @left.is_a?(Variable)
+ return table.size unless @right.is_a?(Constant)
+
+ column = @left.column
+ value = @right.value
+ index_info = column.find_index(@operator)
+ return table.size if index_info.nil?
+
+ index_column = index_info.index
+ lexicon = index_column.lexicon
+ options = {}
+ case @operator
+ when Operator::LESS
+ options[:max] = value
+ options[:flags] = TableCursorFlags::LT
+ when Operator::LESS_EQUAL
+ options[:max] = value
+ options[:flags] = TableCursorFlags::LE
+ when Operator::GREATER
+ options[:min] = value
+ options[:flags] = TableCursorFlags::GT
+ when Operator::GREATER_EQUAL
+ options[:min] = value
+ options[:flags] = TableCursorFlags::GE
+ end
+ TableCursor.open(lexicon, options) do |cursor|
+ index_column.estimate_size(:lexicon_cursor => cursor)
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/constant.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/constant.rb
new file mode 100644
index 00000000..228a1fc8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/constant.rb
@@ -0,0 +1,22 @@
+module Groonga
+ module ExpressionTree
+ class Constant
+ attr_reader :value
+ def initialize(value)
+ @value = value
+ end
+
+ def build(expression)
+ expression.append_constant(@value, Operator::PUSH, 1)
+ end
+
+ def estimate_size(table)
+ if Bulk.true?(@value)
+ table.size
+ else
+ 0
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/function_call.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/function_call.rb
new file mode 100644
index 00000000..0c7438d6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/function_call.rb
@@ -0,0 +1,66 @@
+module Groonga
+ module ExpressionTree
+ class FunctionCall
+ attr_reader :procedure
+ attr_reader :arguments
+ def initialize(procedure, arguments)
+ @procedure = procedure
+ @arguments = arguments
+ end
+
+ def build(expression)
+ @procedure.build(expression)
+ @arguments.each do |argument|
+ argument.build(expression)
+ end
+ expression.append_operator(Operator::CALL, @arguments.size)
+ end
+
+ def estimate_size(table)
+ return table.size unless @procedure.name == "between"
+
+ column, min, min_border, max, max_border = @arguments
+
+ if column.is_a?(Groonga::ExpressionTree::IndexColumn)
+ index_column = column.object
+ else
+ index_info = column.column.find_index(Operator::CALL)
+ return table.size if index_info.nil?
+ index_column = index_info.index
+ end
+
+ while index_column.is_a?(Groonga::Accessor)
+ if index_column.have_next?
+ index_column = index_column.next
+ else
+ index_column = index_column.object
+ index_info = index_column.find_index(Operator::CALL)
+ return table.size if index_info.nil?
+ index_column = index_info.index
+ end
+ end
+
+ lexicon = index_column.lexicon
+ options = {
+ :min => min.value,
+ :max => max.value,
+ :flags => 0,
+ }
+ if min_border.value == "include"
+ options[:flags] |= TableCursorFlags::LT
+ else
+ options[:flags] |= TableCursorFlags::LE
+ end
+ if max_border.value == "include"
+ options[:flags] |= TableCursorFlags::GT
+ else
+ options[:flags] |= TableCursorFlags::GE
+ end
+
+ TableCursor.open(lexicon, options) do |cursor|
+ index_column.estimate_size(:lexicon_cursor => cursor)
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/index_column.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/index_column.rb
new file mode 100644
index 00000000..c62a8d19
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/index_column.rb
@@ -0,0 +1,14 @@
+module Groonga
+ module ExpressionTree
+ class IndexColumn
+ attr_reader :object
+ def initialize(object)
+ @object = object
+ end
+
+ def build(expression)
+ expression.append_object(@object, Operator::PUSH, 1)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/logical_operation.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/logical_operation.rb
new file mode 100644
index 00000000..e8d494f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/logical_operation.rb
@@ -0,0 +1,33 @@
+module Groonga
+ module ExpressionTree
+ class LogicalOperation
+ attr_reader :operator
+ attr_reader :nodes
+ def initialize(operator, nodes)
+ @operator = operator
+ @nodes = nodes
+ end
+
+ def build(expression)
+ @nodes.each_with_index do |node, i|
+ node.build(expression)
+ expression.append_operator(@operator, 2) if i > 0
+ end
+ end
+
+ def estimate_size(table)
+ estimated_sizes = @nodes.collect do |node|
+ node.estimate_size(table)
+ end
+ case @operator
+ when Operator::AND
+ estimated_sizes.min
+ when Operator::OR
+ estimated_sizes.max
+ else
+ estimated_sizes.first
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/options.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/options.rb
new file mode 100644
index 00000000..1504b57a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/options.rb
@@ -0,0 +1,14 @@
+module Groonga
+ module ExpressionTree
+ class Options
+ attr_reader :object
+ def initialize(object)
+ @object = object
+ end
+
+ def build(expression)
+ expression.append_object(@object, Operator::PUSH, 1)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/procedure.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/procedure.rb
new file mode 100644
index 00000000..1d63149c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/procedure.rb
@@ -0,0 +1,18 @@
+module Groonga
+ module ExpressionTree
+ class Procedure
+ attr_reader :object
+ def initialize(object)
+ @object = object
+ end
+
+ def name
+ @object.name
+ end
+
+ def build(expression)
+ expression.append_object(@object, Operator::PUSH, 1)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/sources.am
new file mode 100644
index 00000000..d8a776bf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/sources.am
@@ -0,0 +1,10 @@
+RUBY_SCRIPT_FILES = \
+ accessor.rb \
+ binary_operation.rb \
+ constant.rb \
+ function_call.rb \
+ index_column.rb \
+ logical_operation.rb \
+ options.rb \
+ procedure.rb \
+ variable.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/variable.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/variable.rb
new file mode 100644
index 00000000..e99ad9a8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree/variable.rb
@@ -0,0 +1,18 @@
+module Groonga
+ module ExpressionTree
+ class Variable
+ attr_reader :column
+ def initialize(column)
+ @column = column
+ end
+
+ def build(expression)
+ expression.append_object(@column, Operator::GET_VALUE, 1)
+ end
+
+ def estimate_size(table)
+ table.size
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree_builder.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree_builder.rb
new file mode 100644
index 00000000..f2cc297c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression_tree_builder.rb
@@ -0,0 +1,111 @@
+require "expression_tree"
+
+module Groonga
+ class ExpressionTreeBuilder
+ RELATION_OPERATORS = [
+ Operator::MATCH,
+ Operator::NEAR,
+ Operator::NEAR2,
+ Operator::SIMILAR,
+ Operator::PREFIX,
+ Operator::SUFFIX,
+ Operator::EQUAL,
+ Operator::NOT_EQUAL,
+ Operator::LESS,
+ Operator::GREATER,
+ Operator::LESS_EQUAL,
+ Operator::GREATER_EQUAL,
+ Operator::GEO_WITHINP5,
+ Operator::GEO_WITHINP6,
+ Operator::GEO_WITHINP8,
+ Operator::TERM_EXTRACT,
+ Operator::REGEXP,
+ Operator::FUZZY,
+ ]
+
+ ARITHMETIC_OPERATORS = [
+ Operator::BITWISE_OR,
+ Operator::BITWISE_XOR,
+ Operator::BITWISE_AND,
+ Operator::BITWISE_NOT,
+ Operator::SHIFTL,
+ Operator::SHIFTR,
+ Operator::SHIFTRR,
+ Operator::PLUS,
+ Operator::MINUS,
+ Operator::STAR,
+ Operator::MOD,
+ ]
+
+ LOGICAL_OPERATORS = [
+ Operator::AND,
+ Operator::OR,
+ Operator::AND_NOT,
+ Operator::ADJUST,
+ ]
+
+ def initialize(expression)
+ @expression = expression
+ end
+
+ def build
+ stack = []
+ codes = @expression.codes
+ codes.each do |code|
+ case code.op
+ when *LOGICAL_OPERATORS
+ right = stack.pop
+ left = stack.pop
+ nodes = []
+ add_logical_operation_node(code.op, nodes, left)
+ add_logical_operation_node(code.op, nodes, right)
+ node = ExpressionTree::LogicalOperation.new(code.op, nodes)
+ stack.push(node)
+ when *RELATION_OPERATORS, *ARITHMETIC_OPERATORS
+ right = stack.pop
+ left = stack.pop
+ node = ExpressionTree::BinaryOperation.new(code.op, left, right)
+ stack.push(node)
+ when Operator::GET_VALUE
+ node = ExpressionTree::Variable.new(code.value)
+ stack.push(node)
+ when Operator::PUSH
+ case code.value
+ when Procedure
+ node = ExpressionTree::Procedure.new(code.value)
+ when IndexColumn
+ node = ExpressionTree::IndexColumn.new(code.value)
+ when Accessor
+ node = ExpressionTree::Accessor.new(code.value)
+ when HashTable
+ node = ExpressionTree::Options.new(code.value)
+ else
+ node = ExpressionTree::Constant.new(code.value.value)
+ end
+ stack.push(node)
+ when Operator::CALL
+ arguments = []
+ (code.n_args - 1).times do
+ arguments.unshift(stack.pop)
+ end
+ procedure = stack.pop
+ node = ExpressionTree::FunctionCall.new(procedure, arguments)
+ stack.push(node)
+ else
+ raise "unknown operator: #{code.inspect}"
+ end
+ end
+ stack.pop
+ end
+
+ private
+ def add_logical_operation_node(operator, nodes, node)
+ if node.is_a?(ExpressionTree::LogicalOperation) and
+ node.operator == operator
+ nodes.concat(node.nodes)
+ else
+ nodes << node
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/fixed_size_column.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/fixed_size_column.rb
new file mode 100644
index 00000000..4fab9fb7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/fixed_size_column.rb
@@ -0,0 +1,5 @@
+module Groonga
+ class FixedSizeColumn
+ include Indexable
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/id.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/id.rb
new file mode 100644
index 00000000..a49e4fde
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/id.rb
@@ -0,0 +1,12 @@
+module Groonga
+ module ID
+ # TODO: Should we bind GRN_N_RESERVED_TYPES?
+ N_RESERVED_TYPES = 256
+
+ class << self
+ def builtin?(id)
+ id < N_RESERVED_TYPES
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_column.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_column.rb
new file mode 100644
index 00000000..25ebc149
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_column.rb
@@ -0,0 +1,39 @@
+module Groonga
+ class IndexColumn
+ private :estimate_size_for_term_id
+ private :estimate_size_for_query
+ private :estimate_size_for_lexicon_cursor
+
+ # Estimate the number of matched records for term ID or query.
+ #
+ # @overload estimate_size(:term_id => term_id)
+ # @return [Integer] the number of matched records for the term ID.
+ #
+ # @overload estimate_size(:query => query)
+ # @return [Integer] the number of matched records for the query.
+ #
+ # @overload estimate_size(:lexicon_cursor => lexicon_cursor)
+ # @return [Integer] the number of matched records for the lexicon cursor.
+ #
+ def estimate_size(parameters)
+ term_id = parameters[:term_id]
+ if term_id
+ return estimate_size_for_term_id(term_id)
+ end
+
+ query = parameters[:query]
+ if query
+ return estimate_size_for_query(query, parameters)
+ end
+
+ lexicon_cursor = parameters[:lexicon_cursor]
+ if lexicon_cursor
+ return estimate_size_for_lexicon_cursor(lexicon_cursor)
+ end
+
+ message =
+ "must specify :term_id, :query, :lexicon_cursor: #{parameters.inspect}"
+ raise InvalidArgument, message
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_cursor.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_cursor.rb
new file mode 100644
index 00000000..80440669
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_cursor.rb
@@ -0,0 +1,18 @@
+module Groonga
+ class IndexCursor
+ class << self
+ def open(*arguments)
+ cursor = open_raw(*arguments)
+ if block_given?
+ begin
+ yield(cursor)
+ ensure
+ cursor.close
+ end
+ else
+ cursor
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb
new file mode 100644
index 00000000..cf8336e5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb
@@ -0,0 +1,10 @@
+module Groonga
+ class IndexInfo
+ attr_reader :index
+ attr_reader :section_id
+ def initialize(index, section_id)
+ @index = index
+ @section_id = section_id
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/Makefile.am
new file mode 100644
index 00000000..e7531fdc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_initializedir = $(ruby_scriptsdir)/initialize
+ruby_scripts_initialize_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/post.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/post.rb
new file mode 100644
index 00000000..c1215f3e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/post.rb
@@ -0,0 +1,28 @@
+require "error"
+
+require "context"
+
+require "writer"
+
+require "id"
+
+require "object"
+require "database"
+require "table"
+require "record"
+require "fixed_size_column"
+require "variable_size_column"
+require "index_column"
+require "accessor"
+require "command"
+require "command_input"
+require "table_cursor"
+require "index_cursor"
+
+require "expression"
+
+require "plugin_loader"
+
+require "eval_context"
+
+require "command_line_parser"
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/pre.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/pre.rb
new file mode 100644
index 00000000..99300d11
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/pre.rb
@@ -0,0 +1,3 @@
+require "backtrace_entry"
+
+require "operator"
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/sources.am
new file mode 100644
index 00000000..3c26e19b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/initialize/sources.am
@@ -0,0 +1,3 @@
+RUBY_SCRIPT_FILES = \
+ pre.rb \
+ post.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb
new file mode 100644
index 00000000..95f86974
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb
@@ -0,0 +1,34 @@
+module Groonga
+ class Logger
+ def log_error(error)
+ log_level = Level::ERROR.to_i
+
+ if error.is_a?(Error)
+ message = error.message
+ else
+ message = "#{error.class}: #{error.message}"
+ end
+ backtrace = error.backtrace
+ first_raw_entry = backtrace.first
+ if first_raw_entry
+ first_entry = BacktraceEntry.parse(first_raw_entry)
+ file = first_entry.file
+ line = first_entry.line
+ method = first_entry.method
+ # message = "#{file}:#{line}:#{method}: #{message}"
+ else
+ file = ""
+ line = 0
+ method = ""
+ end
+ log(log_level, file, line, method, message)
+
+ backtrace.each_with_index do |raw_entry, i|
+ entry = BacktraceEntry.parse(raw_entry)
+ message = entry.message
+ message = raw_entry if message.empty?
+ log(log_level, entry.file, entry.line, entry.method, raw_entry)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/Makefile.am
new file mode 100644
index 00000000..448e72ca
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_loggerdir = $(ruby_scriptsdir)/logger
+ruby_scripts_logger_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb
new file mode 100644
index 00000000..4b8afa2c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb
@@ -0,0 +1,40 @@
+module Groonga
+ class Logger
+ class Level
+ @@names = {}
+ @@levels = {}
+ class << self
+ def find(name_or_level)
+ if name_or_level.is_a?(Integer)
+ @@levels[name_or_level]
+ else
+ @@names[name_or_level]
+ end
+ end
+ end
+
+ attr_reader :name
+ def initialize(name, level)
+ @@names[name] = self
+ @@levels[level] = self
+ @name = name
+ @level = level
+ end
+
+ def to_i
+ @level
+ end
+
+ NONE = new(:none, 0)
+ EMERG = new(:emerg, 1)
+ ALERT = new(:alert, 2)
+ CRIT = new(:crit, 3)
+ ERROR = new(:error, 4)
+ WARNING = new(:warning, 5)
+ NOTICE = new(:notice, 6)
+ INFO = new(:info, 7)
+ DEBUG = new(:debug, 8)
+ DUMP = new(:dump, 9)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/sources.am
new file mode 100644
index 00000000..7231ee4e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/sources.am
@@ -0,0 +1,2 @@
+RUBY_SCRIPT_FILES = \
+ level.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb
new file mode 100644
index 00000000..aa8e9e65
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb
@@ -0,0 +1,18 @@
+module Groonga
+ class Object
+ def domain
+ Context.instance[domain_id]
+ end
+
+ def range
+ Context.instance[range_id]
+ end
+
+ def corrupt?
+ check_corrupt
+ false
+ rescue
+ true
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/operator.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/operator.rb
new file mode 100644
index 00000000..349695e1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/operator.rb
@@ -0,0 +1,22 @@
+module Groonga
+ class Operator
+ @values = {}
+ class << self
+ def register(operator)
+ const_set(operator.name, operator)
+ @values[operator.value] = operator
+ end
+
+ def find(value)
+ @values[value]
+ end
+ end
+
+ attr_reader :name
+ attr_reader :value
+ def initialize(name, value)
+ @name = name
+ @value = value
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/plugin_loader.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/plugin_loader.rb
new file mode 100644
index 00000000..09b972f1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/plugin_loader.rb
@@ -0,0 +1,14 @@
+module Groonga
+ class PluginLoader
+ class << self
+ def load_file(path)
+ begin
+ load(path)
+ rescue => error
+ Context.instance.record_error(:plugin_error, error)
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger.rb
new file mode 100644
index 00000000..386ec0e2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger.rb
@@ -0,0 +1,9 @@
+module Groonga
+ class QueryLogger
+ def log(flag, mark, message)
+ flag = Flag.find(flag) if flag.is_a?(Symbol)
+ flag = flag.to_i if flag.is_a?(Flag)
+ log_raw(flag, mark, message)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/Makefile.am
new file mode 100644
index 00000000..14b4f61c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+ruby_scripts_query_loggerdir = $(ruby_scriptsdir)/query_logger
+ruby_scripts_query_logger_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/flag.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/flag.rb
new file mode 100644
index 00000000..659570f6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/flag.rb
@@ -0,0 +1,39 @@
+module Groonga
+ class QueryLogger
+ class Flag
+ @@names = {}
+ class << self
+ def find(name)
+ @@names[name]
+ end
+ end
+
+ attr_reader :name
+ def initialize(name, flag)
+ @@names[name] = self
+ @name = name
+ @flag = flag
+ end
+
+ def to_i
+ @flag
+ end
+
+ NONE = new(:none, 0x00)
+ COMMAND = new(:command, 0x01 << 0)
+ RESULT_CODE = new(:result_code, 0x01 << 1)
+ DESTINATION = new(:destination, 0x01 << 2)
+ CACHE = new(:cache, 0x01 << 3)
+ SIZE = new(:size, 0x01 << 4)
+ SCORE = new(:score, 0x01 << 5)
+
+ all_flags = COMMAND.to_i |
+ RESULT_CODE.to_i |
+ DESTINATION.to_i |
+ CACHE.to_i |
+ SIZE.to_i |
+ SCORE.to_i
+ ALL = new(:all, all_flags)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/sources.am
new file mode 100644
index 00000000..44871a85
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/query_logger/sources.am
@@ -0,0 +1,2 @@
+RUBY_SCRIPT_FILES = \
+ flag.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/record.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/record.rb
new file mode 100644
index 00000000..cd7d1a4c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/record.rb
@@ -0,0 +1,38 @@
+module Groonga
+ class Record
+ attr_reader :table
+ attr_reader :id
+
+ def inspect
+ super.gsub(/>\z/) do
+ "@id=#{@id.inspect}, @table=#{@table.inspect}>"
+ end
+ end
+
+ def [](name)
+ column = find_column(name)
+ if column.nil?
+ raise InvalidArgument, "unknown column: <#{absolute_column_name(name)}>"
+ end
+ column[@id]
+ end
+
+ def method_missing(name, *args, &block)
+ return super unless args.empty?
+
+ column = find_column(name)
+ return super if column.nil?
+
+ column[@id]
+ end
+
+ private
+ def absolute_column_name(name)
+ "#{@table.name}.#{name}"
+ end
+
+ def find_column(name)
+ Context.instance[absolute_column_name(name)]
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/require.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/require.rb
new file mode 100644
index 00000000..1352b327
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/require.rb
@@ -0,0 +1,71 @@
+$" = [__FILE__]
+
+class ScriptLoader
+ @@loading_paths = {}
+
+ def initialize(path)
+ @base_path = path
+ end
+
+ def load_once
+ if absolute_path?(@base_path)
+ loaded = load_once_path(@base_path)
+ if loaded.nil?
+ raise LoadError, error_message
+ else
+ loaded
+ end
+ else
+ $LOAD_PATH.each do |load_path|
+ unless absolute_path?(load_path)
+ load_path = File.expand_path(load_path)
+ if File::ALT_SEPARATOR
+ load_path = load_path.gsub(File::ALT_SEPARATOR, "/")
+ end
+ end
+ loaded = load_once_path(File.join(load_path, @base_path))
+ return loaded unless loaded.nil?
+ end
+ raise LoadError, error_message
+ end
+ end
+
+ private
+ def error_message
+ "cannot load such file -- #{@base_path}"
+ end
+
+ def absolute_path?(path)
+ path.start_with?("/") or (/\A[a-z]:\\/i === path)
+ end
+
+ def load_once_path(path)
+ loaded = load_once_absolute_path(path)
+ return loaded unless loaded.nil?
+
+ return nil unless File.extname(path).empty?
+
+ load_once_absolute_path("#{path}.rb")
+ end
+
+ def load_once_absolute_path(path)
+ return false if $".include?(path)
+ return false if @@loading_paths.key?(path)
+
+ return nil unless File.file?(path)
+
+ @@loading_paths[path] = true
+ load(path)
+ $" << path
+ @@loading_paths.delete(path)
+
+ true
+ end
+end
+
+module Kernel
+ def require(path)
+ loader = ScriptLoader.new(path)
+ loader.load_once
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb
new file mode 100644
index 00000000..acdb2eeb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb
@@ -0,0 +1,38 @@
+module Groonga
+ class ScanInfo
+ module Flags
+ ACCESSOR = 0x01
+ PUSH = 0x02
+ POP = 0x04
+ PRE_CONST = 0x08
+ end
+
+ def apply(data)
+ self.op = data.op
+ self.logical_op = data.logical_op
+ self.end = data.end
+ self.query = data.query
+ self.flags = data.flags
+ if data.max_interval
+ self.max_interval = data.max_interval
+ end
+ if data.similarity_threshold
+ self.similarity_threshold = data.similarity_threshold
+ end
+ data.args.each do |arg|
+ push_arg(arg)
+ end
+ data.search_indexes.each do |search_index|
+ put_index(search_index.index_column,
+ search_index.section_id,
+ search_index.weight,
+ search_index.scorer,
+ search_index.scorer_args_expr,
+ search_index.scorer_args_expr_offset || 0)
+ end
+ if data.start_position
+ self.start_position = data.start_position
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb
new file mode 100644
index 00000000..66dad9ea
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb
@@ -0,0 +1,577 @@
+require "scan_info_data"
+
+module Groonga
+ class ScanInfoBuilder
+ def initialize(expression, operator, record_exist)
+ @data_list = []
+ @expression = expression
+ @operator = operator
+ @record_exist = record_exist
+ @variable = @expression[0]
+ @table = Context.instance[@variable.domain]
+ end
+
+ RELATION_OPERATORS = [
+ Operator::MATCH,
+ Operator::NEAR,
+ Operator::NEAR2,
+ Operator::SIMILAR,
+ Operator::PREFIX,
+ Operator::SUFFIX,
+ Operator::EQUAL,
+ Operator::NOT_EQUAL,
+ Operator::LESS,
+ Operator::GREATER,
+ Operator::LESS_EQUAL,
+ Operator::GREATER_EQUAL,
+ Operator::GEO_WITHINP5,
+ Operator::GEO_WITHINP6,
+ Operator::GEO_WITHINP8,
+ Operator::TERM_EXTRACT,
+ Operator::REGEXP,
+ Operator::FUZZY,
+ ]
+
+ ARITHMETIC_OPERATORS = [
+ Operator::BITWISE_OR,
+ Operator::BITWISE_XOR,
+ Operator::BITWISE_AND,
+ Operator::BITWISE_NOT,
+ Operator::SHIFTL,
+ Operator::SHIFTR,
+ Operator::SHIFTRR,
+ Operator::PLUS,
+ Operator::MINUS,
+ Operator::STAR,
+ Operator::MOD,
+ ]
+
+ LOGICAL_OPERATORS = [
+ Operator::AND,
+ Operator::OR,
+ Operator::AND_NOT,
+ Operator::ADJUST,
+ ]
+ def build
+ return nil unless valid?
+
+ context = BuildContext.new(@expression)
+ codes = context.codes
+ while context.have_next?
+ code = context.code
+ code_op = context.code_op
+ i = context.i
+ context.next
+
+ case code_op
+ when *RELATION_OPERATORS
+ context.status = :start
+ data = context.data
+ data.op = code_op
+ data.end = i
+ data.weight = code.value.value if code.value
+ data.match_resolve_index
+ @data_list << data
+ context.data = nil
+ when *LOGICAL_OPERATORS
+ if context.status == :const
+ data = context.data
+ data.op = Operator::PUSH
+ data.end = data.start
+ @data_list << data
+ context.data = nil
+ end
+ put_logical_op(code_op, i)
+ # TODO: rescue and return nil
+ context.status = :start
+ when Operator::PUSH
+ context.data ||= ScanInfoData.new(i)
+ data = context.data
+ if code.value == @variable
+ context.status = :var
+ else
+ data.args << code.value
+ if context.status == :start
+ data.flags |= ScanInfo::Flags::PRE_CONST
+ end
+ context.status = :const
+ end
+ if code.modify > 0 and
+ LOGICAL_OPERATORS.include?(codes[i + code.modify].op)
+ data.op = Operator::PUSH
+ data.end = data.start
+ @data_list << data
+ context.data = nil
+ context.status = :start
+ end
+ when Operator::GET_VALUE
+ case context.status
+ when :start
+ context.data ||= ScanInfoData.new(i)
+ data = context.data
+ context.status = :column1
+ data.args << code.value
+ when :const, :var
+ context.status = :column1
+ data.args << code.value
+ when :column1
+ message = "invalid expression: can't use column as a value: "
+ message << "<#{code.value.name}>: <#{@expression.grn_inspect}>"
+ raise ErrorMessage, message
+ when :column2
+ # Do nothing
+ else
+ message = "internal expression parsing error: unknown status: "
+ message << "<#{context.status.inspect}>: "
+ message << "<#{@expression.grn_inspect}>"
+ raise ErrorMessage, message
+ end
+ when Operator::CALL
+ context.data ||= ScanInfoData.new(i)
+ data = context.data
+ if (code.flags & ExpressionCode::Flags::RELATIONAL_EXPRESSION) != 0 or
+ (not context.have_next?)
+ context.status = :start
+ data.op = code_op
+ data.end = i
+ data.call_relational_resolve_indexes
+ @data_list << data
+ context.data = nil
+ else
+ context.status = :column2
+ end
+ when Operator::GET_REF
+ context.data ||= ScanInfoData.new(i)
+ case context.status
+ when :start
+ data = context.data
+ context.status = :column1
+ data.args << code.value
+ end
+ when Operator::GET_MEMBER
+ data = context.data
+ index = data.args.pop
+ data.start_position = index.value
+ context.status = :column1
+ when Operator::NOT
+ success = build_not(context, code, i)
+ return nil unless success
+ end
+ end
+
+ if @operator == Operator::OR and !@record_exist
+ first_data = @data_list.first
+ if (first_data.flags & ScanInfo::Flags::PUSH) == 0 or
+ first_data.logical_op != @operator
+ raise ErrorMessage, "invalid expr"
+ else
+ first_data.flags &= ~ScanInfo::Flags::PUSH
+ first_data.logical_op = @operator
+ end
+ else
+ put_logical_op(@operator, context.n_codes)
+ end
+
+ optimize
+ end
+
+ private
+ def valid?
+ n_relation_expressions = 0
+ n_logical_expressions = 0
+ status = :start
+ codes = @expression.codes
+ codes.each_with_index do |code, i|
+ case code.op
+ when *RELATION_OPERATORS
+ if status == :start || status == :var
+ return false
+ end
+ status = :start
+ n_relation_expressions += 1
+ when *ARITHMETIC_OPERATORS
+ if status == :start || status == :var
+ return false
+ end
+ status = :start
+ return false if n_relation_expressions != (n_logical_expressions + 1)
+ when *LOGICAL_OPERATORS
+ case status
+ when :start
+ n_logical_expressions += 1
+ return false if n_logical_expressions >= n_relation_expressions
+ when :const
+ n_logical_expressions += 1
+ n_relation_expressions += 1
+ return false if n_logical_expressions >= n_relation_expressions
+ status = :start
+ else
+ return false
+ end
+ when Operator::PUSH
+ if code.modify > 0 and
+ LOGICAL_OPERATORS.include?(codes[i + code.modify].op)
+ n_relation_expressions += 1
+ status = :start
+ else
+ if code.value == @variable
+ status = :var
+ else
+ status = :const
+ end
+ end
+ when Operator::GET_VALUE
+ case status
+ when :start, :const, :var
+ status = :column1
+ when :column1
+ status = :column2
+ when :column2
+ # Do nothing
+ else
+ return false
+ end
+ when Operator::CALL
+ if (code.flags & ExpressionCode::Flags::RELATIONAL_EXPRESSION) != 0 or
+ code == codes.last
+ status = :start
+ n_relation_expressions += 1
+ else
+ status = :column2
+ end
+ when Operator::GET_REF
+ case status
+ when :start
+ status = :column1
+ else
+ return false
+ end
+ when Operator::GET_MEMBER
+ case status
+ when :const
+ return false unless codes[i - 1].value.value.is_a?(Integer)
+ status = :column1
+ else
+ return false
+ end
+ when Operator::NOT
+ # Do nothing
+ else
+ return false
+ end
+ end
+
+ return false if status != :start
+ return false if n_relation_expressions != (n_logical_expressions + 1)
+
+ true
+ end
+
+ def put_logical_op(operator, start)
+ n_parens = 1
+ n_dif_ops = 0
+ r = 0
+ j = @data_list.size
+ while j > 0
+ j -= 1
+ data = @data_list[j]
+ if (data.flags & ScanInfo::Flags::POP) != 0
+ n_dif_ops += 1
+ n_parens += 1
+ else
+ if (data.flags & ScanInfo::Flags::PUSH) != 0
+ n_parens -= 1
+ if n_parens == 0
+ if r == 0
+ if n_dif_ops > 0
+ if j > 0 and operator != Operator::AND_NOT
+ n_parens = 1
+ n_dif_ops = 0
+ r = j
+ else
+ new_data = ScanInfoData.new(start)
+ new_data.flags = ScanInfo::Flags::POP
+ new_data.logical_op = operator
+ @data_list << new_data
+ break
+ end
+ else
+ data.flags &= ~ScanInfo::Flags::PUSH
+ data.logical_op = operator
+ break
+ end
+ else
+ if n_dif_ops > 0
+ new_data = ScanInfoData.new(start)
+ new_data.flags = ScanInfo::Flags::POP
+ new_data.logical_op = operator
+ @data_list << new_data
+ else
+ data.flags &= ~ScanInfo::Flags::PUSH
+ data.logical_op = operator
+ @data_list =
+ @data_list[0...j] +
+ @data_list[r..-1] +
+ @data_list[j...r]
+ end
+ break
+ end
+ end
+ else
+ if operator == Operator::AND_NOT or operator != data.logical_op
+ n_dif_ops += 1
+ end
+ end
+ end
+
+ if j < 0
+ raise ErrorMessage, "unmatched nesting level"
+ end
+ end
+ end
+
+ def build_not(context, code, i)
+ last_data = @data_list.last
+ return false if last_data.nil?
+
+ case last_data.op
+ when Operator::LESS
+ last_data.op = Operator::GREATER_EQUAL
+ last_data.end += 1
+ when Operator::LESS_EQUAL
+ last_data.op = Operator::GREATER
+ last_data.end += 1
+ when Operator::GREATER
+ last_data.op = Operator::LESS_EQUAL
+ last_data.end += 1
+ when Operator::GREATER_EQUAL
+ last_data.op = Operator::LESS
+ last_data.end += 1
+ when Operator::NOT_EQUAL
+ last_data.op = Operator::EQUAL
+ last_data.end += 1
+ else
+ if @data_list.size == 1
+ if last_data.search_indexes.empty?
+ if last_data.op == Operator::EQUAL
+ last_data.op = Operator::NOT_EQUAL
+ last_data.end += 1
+ else
+ return false
+ end
+ else
+ last_data.logical_op = Operator::AND_NOT
+ last_data.flags &= ~ScanInfo::Flags::PUSH
+ @data_list.unshift(create_all_match_data)
+ end
+ else
+ next_code = context.code
+ return false if next_code.nil?
+
+ case next_code.op
+ when Operator::AND
+ context.code_op = Operator::AND_NOT
+ when Operator::AND_NOT
+ context.code_op = Operator::AND
+ when Operator::OR
+ @data_list[-1, 0] = create_all_match_data
+ put_logical_op(Operator::AND_NOT, i)
+ else
+ return false
+ end
+ end
+ end
+
+ true
+ end
+
+ def optimize
+ optimized_data_list = []
+ i = 0
+ n = @data_list.size
+ while i < n
+ data = @data_list[i]
+ next_data = @data_list[i + 1]
+ i += 1
+ if next_data.nil?
+ optimized_data_list << data
+ next
+ end
+ if range_operations?(data, next_data)
+ between_data = create_between_data(data, next_data)
+ optimized_data_list << between_data
+ i += 1
+ next
+ end
+ optimized_data_list << data
+ end
+
+ optimize_by_estimated_size(optimized_data_list)
+ end
+
+ def optimize_by_estimated_size(data_list)
+ return data_list unless Groonga::ORDER_BY_ESTIMATED_SIZE
+
+ start_index = nil
+ data_list.size.times do |i|
+ data = data_list[i]
+ if data.logical_op != Operator::AND
+ if start_index.nil?
+ start_index = i
+ else
+ sort_by_estimated_size!(data_list, start_index...i)
+ start_index = nil
+ end
+ end
+ end
+ unless start_index.nil?
+ sort_by_estimated_size!(data_list, start_index...data_list.size)
+ end
+ data_list
+ end
+
+ def sort_by_estimated_size!(data_list, range)
+ target_data_list = data_list[range]
+ return if target_data_list.size < 2
+
+ start_logical_op = target_data_list.first.logical_op
+ sorted_data_list = target_data_list.sort_by do |data|
+ estimator = ScanInfoDataSizeEstimator.new(data, @table)
+ estimator.estimate
+ end
+ sorted_data_list.each do |sorted_data|
+ sorted_data.logical_op = Operator::AND
+ end
+ sorted_data_list.first.logical_op = start_logical_op
+ data_list[range] = sorted_data_list
+ end
+
+ def range_operations?(data, next_data)
+ return false unless next_data.logical_op == Operator::AND
+
+ op, next_op = data.op, next_data.op
+ return false if !(lower_condition?(op) or lower_condition?(next_op))
+ return false if !(upper_condition?(op) or upper_condition?(next_op))
+
+ return false if data.args[0] != next_data.args[0]
+
+ data_search_indexes = data.search_indexes
+ return false if data_search_indexes.empty?
+
+ data_search_indexes == next_data.search_indexes
+ end
+
+ def lower_condition?(operator)
+ case operator
+ when Operator::GREATER, Operator::GREATER_EQUAL
+ true
+ else
+ false
+ end
+ end
+
+ def upper_condition?(operator)
+ case operator
+ when Operator::LESS, Operator::LESS_EQUAL
+ true
+ else
+ false
+ end
+ end
+
+ def create_all_match_data
+ data = ScanInfoData.new(0)
+ data.end = 0
+ data.flags = ScanInfo::Flags::PUSH
+ data.op = Operator::CALL
+ data.logical_op = Operator::OR
+ data.args = [Context.instance["all_records"]]
+ data.search_indexes = []
+ data
+ end
+
+ def create_between_data(data, next_data)
+ between_data = ScanInfoData.new(data.start)
+ between_data.end = next_data.end + 1
+ between_data.flags = data.flags
+ between_data.op = Operator::CALL
+ between_data.logical_op = data.logical_op
+ between_data.args = create_between_data_args(data, next_data)
+ between_data.search_indexes = data.search_indexes
+ between_data
+ end
+
+ def create_between_data_args(data, next_data)
+ between = Context.instance["between"]
+ @expression.take_object(between)
+ column = data.args[0]
+ op, next_op = data.op, next_data.op
+ if lower_condition?(op)
+ min = data.args[1]
+ min_operator = op
+ max = next_data.args[1]
+ max_operator = next_op
+ else
+ min = next_data.args[1]
+ min_operator = next_op
+ max = data.args[1]
+ max_operator = op
+ end
+ if min_operator == Operator::GREATER
+ min_border = "exclude"
+ else
+ min_border = "include"
+ end
+ if max_operator == Operator::LESS
+ max_border = "exclude"
+ else
+ max_border = "include"
+ end
+
+ [
+ between,
+ column,
+ min,
+ @expression.allocate_constant(min_border),
+ max,
+ @expression.allocate_constant(max_border),
+ ]
+ end
+
+ class BuildContext
+ attr_accessor :status
+ attr_reader :codes
+ attr_reader :n_codes
+ attr_reader :i
+ attr_writer :code_op
+ attr_accessor :data
+ def initialize(expression)
+ @expression = expression
+ @status = :start
+ @current_data = nil
+ @codes = @expression.codes
+ @n_codes = @codes.size
+ @i = 0
+ @code_op = nil
+ @data = nil
+ end
+
+ def have_next?
+ @i < @n_codes
+ end
+
+ def next
+ @i += 1
+ @code_op = nil
+ end
+
+ def code
+ @codes[@i]
+ end
+
+ def code_op
+ @code_op || code.op
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb
new file mode 100644
index 00000000..342f7a7a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb
@@ -0,0 +1,324 @@
+require "scan_info_search_index"
+
+module Groonga
+ class ScanInfoData
+ attr_accessor :start
+ attr_accessor :end
+ attr_accessor :op
+ attr_accessor :logical_op
+ attr_accessor :query
+ attr_accessor :args
+ attr_accessor :search_indexes
+ attr_accessor :flags
+ attr_accessor :max_interval
+ attr_accessor :similarity_threshold
+ attr_accessor :start_position
+ attr_accessor :weight
+ def initialize(start)
+ @start = start
+ @end = 0
+ @op = Operator::NOP
+ @logical_op = Operator::OR
+ @query = nil
+ @args = []
+ @search_indexes = []
+ @flags = ScanInfo::Flags::PUSH
+ @max_interval = nil
+ @similarity_threshold = nil
+ @start_position = nil
+ @weight = 0
+ end
+
+ def match_resolve_index
+ if near_search?
+ match_near_resolve_index
+ elsif similar_search?
+ match_similar_resolve_index
+ else
+ match_generic_resolve_index
+ end
+ end
+
+ def call_relational_resolve_indexes
+ procedure, *args = *@args
+ return unless procedure.selector?
+
+ selector_op = procedure.selector_operator
+ args.each do |arg|
+ call_relational_resolve_index(arg, selector_op)
+ end
+ end
+
+ private
+ def near_search?
+ (@op == Operator::NEAR or @op == Operator::NEAR2) and @args.size == 3
+ end
+
+ def match_near_resolve_index
+ arg = @args[0]
+ case arg
+ when Expression
+ match_resolve_index_expression(arg)
+ when Accessor
+ match_resolve_index_accessor(arg)
+ when Indexable
+ match_resolve_index_indexable(arg)
+ else
+ message =
+ "The first argument of NEAR/NEAR2 must be Expression, Accessor or Indexable: #{arg.class}"
+ raise ErrorMessage, message
+ end
+
+ self.query = @args[1]
+ self.max_interval = @args[2].value
+ end
+
+ def similar_search?
+ @op == Operator::SIMILAR and @args.size == 3
+ end
+
+ def match_similar_resolve_index
+ arg = @args[0]
+ case arg
+ when Expression
+ match_resolve_index_expression(arg)
+ when Accessor
+ match_resolve_index_accessor(arg)
+ when Indexable
+ match_resolve_index_indexable(arg)
+ else
+ message =
+ "The first argument of SIMILAR must be Expression, Accessor or Indexable: #{arg.class}"
+ raise ErrorMesesage, message
+ end
+
+ self.query = @args[1]
+ self.similarity_threshold = @args[2].value
+ end
+
+ def match_generic_resolve_index
+ @args.each do |arg|
+ case arg
+ when Expression
+ match_resolve_index_expression(arg)
+ when Accessor
+ match_resolve_index_accessor(arg)
+ when IndexColumn
+ match_resolve_index_index_column(arg)
+ when Indexable
+ match_resolve_index_indexable(arg)
+ when Procedure
+ break
+ else
+ self.query = arg
+ end
+ end
+ if @op == Operator::REGEXP and not index_searchable_regexp?(@query)
+ @search_indexes.clear
+ end
+ end
+
+ def index_searchable_regexp?(pattern)
+ return false if pattern.nil?
+
+ previous_char = nil
+ pattern.value.each_char do |char|
+ if previous_char == "\\"
+ case char
+ when "Z"
+ return false
+ when "b", "B"
+ return false
+ when "d", "D", "h", "H", "p", "s", "S", "w", "W"
+ return false
+ when "X"
+ return false
+ when "k", "g", "1", "2", "3", "4", "5", "6", "7", "8", "9"
+ return false
+ when "\\"
+ previous_char = nil
+ next
+ end
+ else
+ case char
+ when ".", "[", "]", "|", "?", "+", "*", "{", "}", "^", "$", "(", ")"
+ return false
+ end
+ end
+ previous_char = char
+ end
+ true
+ end
+
+ def match_resolve_index_expression(expression)
+ codes = expression.codes
+ n_codes = codes.size
+ i = 0
+ while i < n_codes
+ i = match_resolve_index_expression_codes(expression, codes, i, n_codes)
+ end
+ end
+
+ def match_resolve_index_expression_codes(expression, codes, i, n_codes)
+ code = codes[i]
+ value = code.value
+ return i + 1 if value.nil?
+
+ case value
+ when Accessor, Column
+ index_info, offset =
+ match_resolve_index_expression_find_index(expression,
+ codes, i, n_codes)
+ i += offset - 1
+ if index_info
+ if value.is_a?(Accessor)
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ end
+ weight, offset = codes[i].weight
+ i += offset
+ put_search_index(index_info.index, index_info.section_id, weight)
+ end
+ when Procedure
+ unless value.scorer?
+ message = "procedure must be scorer: #{scorer.name}>"
+ raise ErrorMessage, message
+ end
+ scorer = value
+ i += 1
+ index_info, offset =
+ match_resolve_index_expression_find_index(expression,
+ codes, i, n_codes)
+ i += offset
+ if index_info
+ scorer_args_expr_offset = 0
+ if codes[i].op != Operator::CALL
+ scorer_args_expr_offset = i
+ end
+ while i < n_codes and codes[i].op != Operator::CALL
+ i += 1
+ end
+ weight, offset = codes[i].weight
+ i += offset
+ put_search_index(index_info.index,
+ index_info.section_id,
+ weight,
+ scorer,
+ expression,
+ scorer_args_expr_offset)
+ end
+ when Table
+ raise ErrorMessage, "invalid match target: <#{value.name}>"
+ end
+ i + 1
+ end
+
+ def match_resolve_index_expression_find_index(expression, codes, i, n_codes)
+ code = codes[i]
+ value = code.value
+ index_info = nil
+ offset = 1
+ case value
+ when Accessor
+ accessor = value
+ index_info = accessor.find_index(@op)
+ if index_info
+ if accessor.have_next? and index_info.index != accessor.object
+ index_info = IndexInfo.new(accessor, index_info.section_id)
+ end
+ end
+ when FixedSizeColumn, VariableSizeColumn
+ index_info = value.find_index(@op)
+ when IndexColumn
+ index = value
+ section_id = 0
+ rest_n_codes = n_codes - i
+ if rest_n_codes >= 2 and
+ codes[i + 1].value.is_a?(Bulk) and
+ (codes[i + 1].value.domain == ID::UINT32 or
+ codes[i + 1].value.domain == ID::INT32) and
+ codes[i + 2].op == Operator::GET_MEMBER
+ section_id = codes[i + 1].value.value + 1
+ offset += 2
+ end
+ index_info = IndexInfo.new(index, section_id)
+ end
+
+ [index_info, offset]
+ end
+
+ def match_resolve_index_expression_accessor(expr_code)
+ accessor = expr_code.value
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(op)
+ return if index_info.nil?
+
+ section_id = index_info.section_id
+ weight = expr_code.weight
+ if accessor.next
+ put_search_index(accessor, section_id, weight)
+ else
+ put_search_index(index_info.index, section_id, weight)
+ end
+ end
+
+ def match_resolve_index_expression_data_column(expr_code)
+ column = expr_code.value
+ index_info = column.find_index(op)
+ return if index_info.nil?
+ put_search_index(index_info.index, index_info.section_id, expr_code.weight)
+ end
+
+ def match_resolve_index_index_column(index)
+ put_search_index(index, 0, 1)
+ end
+
+ def match_resolve_index_indexable(indexable)
+ index_info = indexable.find_index(op)
+ return if index_info.nil?
+ put_search_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def match_resolve_index_accessor(accessor)
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(op)
+ return if index_info.nil?
+ if accessor.next
+ put_search_index(accessor, index_info.section_id, 1)
+ else
+ put_search_index(index_info.index, index_info.section_id, 1)
+ end
+ end
+
+ def call_relational_resolve_index(object, selector_op)
+ case object
+ when Accessor
+ call_relational_resolve_index_accessor(object, selector_op)
+ when Bulk
+ self.query = object
+ when Indexable
+ call_relational_resolve_index_indexable(object, selector_op)
+ end
+ end
+
+ def call_relational_resolve_index_indexable(indexable, selector_op)
+ index_info = indexable.find_index(selector_op)
+ return if index_info.nil?
+ put_search_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def call_relational_resolve_index_accessor(accessor, selector_op)
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(selector_op)
+ return if index_info.nil?
+ put_search_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def put_search_index(index, section_id, weight, *args)
+ search_index = ScanInfoSearchIndex.new(index,
+ section_id,
+ weight + @weight,
+ *args)
+ @search_indexes << search_index
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data_size_estimator.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data_size_estimator.rb
new file mode 100644
index 00000000..5d3dc4e0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data_size_estimator.rb
@@ -0,0 +1,185 @@
+module Groonga
+ class ScanInfoDataSizeEstimator
+ def initialize(data, table)
+ @data = data
+ @table = table
+ @table_size = @table.size
+ end
+
+ def estimate
+ search_index = @data.search_indexes.first
+ return @table_size if search_index.nil?
+
+ index_column = resolve_index_column(search_index.index_column)
+ return @table_size if index_column.nil?
+
+ size = nil
+ case @data.op
+ when Operator::MATCH,
+ Operator::FUZZY
+ size = estimate_match(index_column)
+ when Operator::REGEXP
+ size = estimate_regexp(index_column)
+ when Operator::EQUAL
+ size = estimate_equal(index_column)
+ when Operator::LESS,
+ Operator::LESS_EQUAL,
+ Operator::GREATER,
+ Operator::GREATER_EQUAL
+ size = estimate_range(index_column)
+ when Operator::PREFIX
+ size = estimate_prefix(index_column)
+ when Operator::CALL
+ size = estimate_call(index_column)
+ end
+ size || @table_size
+ end
+
+ private
+ def resolve_index_column(index_column)
+ while index_column.is_a?(Accessor)
+ index_info = index_column.find_index(@data.op)
+ return nil if index_info.nil?
+ break if index_info.index == index_column
+ index_column = index_info.index
+ end
+
+ index_column
+ end
+
+ def sampling_cursor_limit(n_terms)
+ limit = n_terms * 0.01
+ if limit < 10
+ 10
+ elsif limit > 1000
+ 1000
+ else
+ limit.to_i
+ end
+ end
+
+ def estimate_match(index_column)
+ index_column.estimate_size(:query => @data.query.value)
+ end
+
+ def estimate_regexp(index_column)
+ index_column.estimate_size(:query => @data.query.value,
+ :mode => @data.op)
+ end
+
+ def estimate_equal(index_column)
+ query = @data.query
+ if index_column.is_a?(Accessor)
+ table = index_column.object
+ if index_column.name == "_id"
+ if table.id?(query.value)
+ 1
+ else
+ 0
+ end
+ else
+ if table[query.value]
+ 1
+ else
+ 0
+ end
+ end
+ else
+ lexicon = index_column.lexicon
+ if query.domain == lexicon.id
+ term_id = query.value
+ else
+ term_id = lexicon[query]
+ end
+ return 0 if term_id.nil?
+
+ index_column.estimate_size(:term_id => term_id)
+ end
+ end
+
+ def estimate_range(index_column)
+ if index_column.is_a?(Table)
+ is_table_search = true
+ lexicon = index_column
+ elsif index_column.is_a?(Groonga::Accessor)
+ is_table_search = true
+ lexicon = index_column.object
+ else
+ is_table_search = false
+ lexicon = index_column.lexicon
+ end
+ n_terms = lexicon.size
+ return 0 if n_terms.zero?
+
+ value = @data.query.value
+ options = {
+ :limit => sampling_cursor_limit(n_terms),
+ }
+ case @data.op
+ when Operator::LESS
+ options[:max] = value
+ options[:flags] = TableCursorFlags::LT
+ when Operator::LESS_EQUAL
+ options[:max] = value
+ options[:flags] = TableCursorFlags::LE
+ when Operator::GREATER
+ options[:min] = value
+ options[:flags] = TableCursorFlags::GT
+ when Operator::GREATER_EQUAL
+ options[:min] = value
+ options[:flags] = TableCursorFlags::GE
+ end
+ TableCursor.open(lexicon, options) do |cursor|
+ if is_table_search
+ size = 1
+ else
+ size = index_column.estimate_size(:lexicon_cursor => cursor)
+ end
+ size += 1 if cursor.next != ID::NIL
+ size
+ end
+ end
+
+ def estimate_prefix(index_column)
+ is_table_search =
+ (index_column.is_a?(Accessor) and
+ index_column.name == "_key")
+ if is_table_search
+ lexicon = index_column.object
+ else
+ lexicon = index_column.lexicon
+ end
+ n_terms = lexicon.size
+ return 0 if n_terms.zero?
+
+ value = @data.query.value
+ options = {
+ :min => value,
+ :limit => sampling_cursor_limit(n_terms),
+ :flags => TableCursorFlags::PREFIX,
+ }
+ TableCursor.open(lexicon, options) do |cursor|
+ if is_table_search
+ size = 1
+ else
+ size = index_column.estimate_size(:lexicon_cursor => cursor)
+ end
+ size += 1 if cursor.next != ID::NIL
+ size
+ end
+ end
+
+ def estimate_call(index_column)
+ procedure = @data.args[0]
+ arguments = @data.args[1..-1].collect do |arg|
+ if arg.is_a?(::Groonga::Object)
+ ExpressionTree::Variable.new(arg)
+ else
+ ExpressionTree::Constant.new(arg)
+ end
+ end
+ node = ExpressionTree::FunctionCall.new(procedure, arguments)
+ node.estimate_size(@table)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_search_index.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_search_index.rb
new file mode 100644
index 00000000..a2818160
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_search_index.rb
@@ -0,0 +1,9 @@
+module Groonga
+ class ScanInfoSearchIndex < Struct.new(:index_column,
+ :section_id,
+ :weight,
+ :scorer,
+ :scorer_args_expr,
+ :scorer_args_expr_offset)
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am
new file mode 100644
index 00000000..9a9e2bae
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am
@@ -0,0 +1,37 @@
+RUBY_SCRIPT_FILES = \
+ accessor.rb \
+ backtrace_entry.rb \
+ command.rb \
+ command_input.rb \
+ command_line_parser.rb \
+ context.rb \
+ database.rb \
+ error.rb \
+ eval_context.rb \
+ expression.rb \
+ expression_rewriter.rb \
+ expression_rewriters.rb \
+ expression_size_estimator.rb \
+ expression_tree.rb \
+ expression_tree_builder.rb \
+ fixed_size_column.rb \
+ id.rb \
+ index_column.rb \
+ index_cursor.rb \
+ index_info.rb \
+ logger.rb \
+ object.rb \
+ operator.rb \
+ plugin_loader.rb \
+ query_logger.rb \
+ record.rb \
+ require.rb \
+ scan_info.rb \
+ scan_info_builder.rb \
+ scan_info_data.rb \
+ scan_info_data_size_estimator.rb \
+ scan_info_search_index.rb \
+ table.rb \
+ table_cursor.rb \
+ variable_size_column.rb \
+ writer.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/table.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/table.rb
new file mode 100644
index 00000000..75c91894
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/table.rb
@@ -0,0 +1,144 @@
+module Groonga
+ class Table
+ include Enumerable
+ include Indexable
+
+ def columns
+ context = Context.instance
+ column_ids.collect do |id|
+ context[id]
+ end
+ end
+
+ def each
+ flags =
+ TableCursorFlags::ASCENDING |
+ TableCursorFlags::BY_ID
+ TableCursor.open(self, :flags => flags) do |cursor|
+ cursor.each do |id|
+ yield(id)
+ end
+ end
+ end
+
+ def sort(keys, options={})
+ offset = options[:offset] || 0
+ limit = options[:limit] || -1
+ ensure_sort_keys(keys) do |sort_keys|
+ sorted = Array.create("", self)
+ begin
+ sort_raw(sort_keys, offset, limit, sorted)
+ rescue Exception
+ sorted.close
+ raise
+ end
+ sorted
+ end
+ end
+
+ def group(keys, result)
+ ensure_sort_keys(keys) do |sort_keys|
+ group_raw(sort_keys, result)
+ end
+ end
+
+ def apply_window_function(output_column,
+ window_function_call,
+ options={})
+ ensure_sort_keys_accept_nil(options[:sort_keys]) do |sort_keys|
+ ensure_sort_keys_accept_nil(options[:group_keys]) do |group_keys|
+ window_definition = WindowDefinition.new
+ begin
+ window_definition.sort_keys = sort_keys
+ window_definition.group_keys = group_keys
+ apply_window_function_raw(output_column,
+ window_definition,
+ window_function_call)
+ ensure
+ window_definition.close
+ end
+ end
+ end
+ end
+
+ private
+ def ensure_sort_keys_accept_nil(keys, &block)
+ return yield(nil) if keys.nil?
+
+ ensure_sort_keys(keys, &block)
+ end
+
+ def ensure_sort_keys(keys)
+ if keys.is_a?(::Array) and keys.all? {|key| key.is_a?(TableSortKey)}
+ return yield(keys)
+ end
+
+ converted_keys = []
+
+ begin
+ keys = [keys] unless keys.is_a?(::Array)
+ sort_keys = keys.collect do |key|
+ ensure_sort_key(key, converted_keys)
+ end
+ yield(sort_keys)
+ ensure
+ converted_keys.each do |converted_key|
+ converted_key.close
+ end
+ end
+ end
+
+ def ensure_sort_key(key, converted_keys)
+ return key if key.is_a?(TableSortKey)
+
+ sort_key = TableSortKey.new
+ converted_keys << sort_key
+
+ key_name = nil
+ order = :ascending
+ offset = 0
+ if key.is_a?(::Hash)
+ key_name = key[:key]
+ order = key[:order] || order
+ offset = key[:offset] || offset
+ else
+ key_name = key
+ end
+
+ case key_name
+ when String
+ # Do nothing
+ when Symbol
+ key_name = key_name.to_s
+ else
+ message = "sort key name must be String or Symbol: " +
+ "#{key_name.inspect}: #{key.inspect}"
+ raise ArgumentError, message
+ end
+
+ if key_name.start_with?("-")
+ key_name[0] = ""
+ order = :descending
+ elsif key_name.start_with?("+")
+ key_name[0] = ""
+ end
+
+ key = find_column(key_name)
+ if key.nil?
+ table_name = name || "(temporary)"
+ message = "unknown key: #{key_name.inspect}: "
+ message << "#{table_name}(#{size})"
+ raise ArgumentError, message
+ end
+
+ sort_key.key = key
+ if order == :ascending
+ sort_key.flags = Groonga::TableSortFlags::ASCENDING
+ else
+ sort_key.flags = Groonga::TableSortFlags::DESCENDING
+ end
+ sort_key.offset = offset
+ sort_key
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/table_cursor.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/table_cursor.rb
new file mode 100644
index 00000000..45949b71
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/table_cursor.rb
@@ -0,0 +1,28 @@
+module Groonga
+ class TableCursor
+ include Enumerable
+
+ class << self
+ def open(*arguments)
+ cursor = open_raw(*arguments)
+ if block_given?
+ begin
+ yield(cursor)
+ ensure
+ cursor.close
+ end
+ else
+ cursor
+ end
+ end
+ end
+
+ def each
+ loop do
+ id = self.next
+ return if id == Groonga::ID::NIL
+ yield(id)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/variable_size_column.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/variable_size_column.rb
new file mode 100644
index 00000000..3d75502f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/variable_size_column.rb
@@ -0,0 +1,5 @@
+module Groonga
+ class VariableSizeColumn
+ include Indexable
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/writer.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/writer.rb
new file mode 100644
index 00000000..de2bc261
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/writer.rb
@@ -0,0 +1,21 @@
+module Groonga
+ class Writer
+ def array(name, n_elements)
+ open_array(name, n_elements)
+ begin
+ yield
+ ensure
+ close_array
+ end
+ end
+
+ def map(name, n_elements)
+ open_map(name, n_elements)
+ begin
+ yield
+ ensure
+ close_map
+ end
+ end
+ end
+end