lib/ronin/code/sql/dialect.rb in ronin-sql-0.1.1 vs lib/ronin/code/sql/dialect.rb in ronin-sql-0.2.0
- old
+ new
@@ -1,11 +1,11 @@
#
#--
# Ronin SQL - A Ronin library providing support for SQL related security
# tasks.
#
-# Copyright (c) 2007 Hal Brodigan (postmodern at users.sourceforge.net)
+# Copyright (c) 2007-2009 Hal Brodigan (postmodern at users.sourceforge.net)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
@@ -20,141 +20,261 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#++
#
require 'ronin/code/sql/exceptions/unknown_dialect'
+require 'ronin/code/sql/exceptions/unknown_statement'
+require 'ronin/code/sql/exceptions/unknown_clause'
require 'ronin/code/sql/function'
+require 'ronin/code/symbol_table'
require 'ronin/extensions/meta'
module Ronin
module Code
module SQL
class Dialect
- # The style to use
- attr_reader :style
+ # Symbol Table for the dialect
+ attr_reader :symbols
- def initialize(style)
- @style = style
+ # Statements used within the dialect
+ attr_reader :statements
+
+ #
+ # Creates a new Dialect object connected to the specified
+ # _program_.
+ #
+ def initialize(symbols={})
+ @symbols = SymbolTable.new(symbols)
+ @statements = []
end
+ #
+ # Returns the Hash of defined SQL dialects.
+ #
def Dialect.dialects
@@dialects ||= {}
end
+ #
+ # Returns +true+ if there is a SQL Dialect defined with the
+ # specified _name_, returns +false+ otherwise.
+ #
def Dialect.has_dialect?(name)
Dialect.dialects.has_key?(name.to_sym)
end
- def Dialect.get_dialect(name)
+ #
+ # Returns the SQL Dialect defined with the specified _name_. If no
+ # such SQL Dialect exists, an UnknownDialect exception will be
+ # raised.
+ #
+ def Dialect.get(name)
name = name.to_sym
unless Dialect.has_dialect?(name)
- raise(UnknownDialect,"unknown dialect #{name.dump}",caller)
+ raise(UnknownDialect,"unknown dialect #{name}",caller)
end
return Dialect.dialects[name]
end
- def expresses?(name)
- public_methods.include?(name.to_s)
+ #
+ # Returns the Hash of defined Statements within the Dialect.
+ #
+ def self.statements
+ @@statements ||= {}
end
- def express(name,*args,&block)
- unless expresses?(name)
- raise(NameError,"undefined method '#{name}' for #{self}",caller)
+ def self.has_statement?(name)
+ self.statements.has_key?(name.to_sym)
+ end
+
+ def self.clauses
+ all_clauses = {}
+
+ self.statements.each do |stmt|
+ all_clauses.merge!(stmt.clauses)
end
- return send(name,*args,&block)
+ return all_clauses
end
- def field(name)
- field_cache[name.to_sym]
+ def self.has_clause?(name)
+ self.statements.each_value do |stmt|
+ return true if stmt.has_clause?(name)
+ end
+
+ return false
end
- protected
+ def has_statement?(name)
+ self.class.has_statement?(name)
+ end
- def self.dialect(name)
+ def statement(name,*arguments,&block)
name = name.to_sym
- class_def(:name) { name }
+ unless has_statement?(name)
+ raise(UnknownStatement,"unknown statement #{name} in #{dialect} dialect",caller)
+ end
- Dialect.dialects[name] = self
+ return self.class.statements[name].new(self,*arguments,&block)
+ end
+
+ def enqueue_statement(name,*arguments,&block)
+ stmt = statement(name,*arguments,&block)
+
+ @statements << stmt
+ return stmt
+ end
+
+ def has_clause?(name)
+ self.class.has_clause?(name)
+ end
+
+ def clause(name,*arguments)
+ name = name.to_sym
+
+ self.class.statements.each do |stmt|
+ if stmt.has_cluase?(name)
+ return stmt.clauses[name].new(*arguments)
+ end
+ end
+
+ raise(UnknownClause,"unknown clause #{name}",caller)
+ end
+
+ def symbol(name)
+ sym = @symbols.symbol(name)
+ sym.value ||= name
+
+ return sym
+ end
+
+ def field(name)
+ sym = @symbols.symbol(name)
+ sym.value ||= Field.new(@symbols,name)
+
+ return sym
+ end
+
+ def all
+ Token.new('*')
+ end
+
+ def id
+ field('id')
+ end
+
+ def each_token(&block)
+ @statements.each do |stmt|
+ stmt.emit.each(&block)
+
+ block.call(Token.separator)
+ end
+
return self
end
- def self.keyword(name,value=name.to_s.upcase)
- name = name.to_s.downcase
+ protected
- class_def("keyword_#{name}") { keyword(value) }
+ #
+ # Defines a SQL Dialect with the specified _name_.
+ #
+ def self.dialect(name)
+ name = name.to_sym
+
+ class_def(:dialect) { name }
+
+ Dialect.dialects[name.to_sym] = self
return self
end
+ #
+ # Defines various SQL primitives with the specified _names_.
+ #
def self.primitives(*names)
names.each do |name|
name = name.to_s.downcase
- class_def(name) { keyword(name) }
+ class_def(name) { Token.new(name) }
end
return self
end
+ #
+ # Defines a SQL data-type with the specified _name_ and given
+ # _options_.
+ #
def self.data_type(name,options={})
name = name.to_s.downcase
- type_name = name.upcase.to_sym
+ type_name = name.upcase
+ supports_length = options[:length]
- if options[:length]==true
- class_def(name) do |length|
- if length
- "#{type_name}(#{length})"
- else
- type_name
- end
+ class_def(name) do |length|
+ if (supports_length && length)
+ Token.new("#{type_name}(#{length})")
+ else
+ Token.new(type_name)
end
- else
- class_def(name) { type_name }
end
return self
end
- def self.function(*names)
+ #
+ # Defines various SQL function with the specified _names_.
+ #
+ def self.functions(*names)
names.each do |name|
- class_def(name) do |field|
- Function.new(@style,name,field)
+ class_def(name) do |*fields|
+ Function.new(name,*fields)
end
end
return self
end
+ #
+ # Defines various SQL aggregate functions with the specified
+ # _names_.
+ #
def self.aggregators(*names)
- function(*names)
+ names.each do |name|
+ class_def(name) do |field|
+ Function.new(name,field)
+ end
+ end
+
+ return self
end
- def self.command(name,base)
+ #
+ # Defines an SQL statement with the specified _name_ and _base_
+ # class.
+ #
+ def self.statement(name,base)
+ name = name.to_sym
+
+ self.statements[name] = base
+
class_eval %{
- def #{name}(*args,&block)
- #{base}.new(@style,*args,&block)
+ def #{name}(*arguments,&block)
+ enqueue_statement(:#{name},*arguments,&block)
end
}
return self
end
- def keyword(value)
- keyword_cache[value.to_sym]
- end
+ def method_missing(name,*arguments,&block)
+ if (arguments.empty? && block.nil?)
+ return field(name)
+ end
- private
-
- def keyword_cache
- @keyword_cache ||= Hash.new { |hash,key| hash[key] = Keyword.new(@style,key) }
- end
-
- def field_cache
- @field_cache ||= Hash.new { |hash,key| hash[key] = Field.new(@style,key) }
+ raise(NoMethodError,name.id2name)
end
end
end
end