Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/sql-parser/parser.racc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ rule
direct_sql_data_statement
: direct_select_statement_multiple_rows
| insert_specification
| update_specification
| delete_specification

direct_select_statement_multiple_rows
: query_expression order_by_clause { result = SQLParser::Statement::DirectSelect.new(val[0], val[1]) }
Expand Down Expand Up @@ -46,6 +48,22 @@ rule
: INSERT INTO table_reference value_list { result = SQLParser::Statement::Insert.new(val[2], nil, val[3]) }
| INSERT INTO table_reference column_list value_list { result = SQLParser::Statement::Insert.new(val[2], val[3], val[4]) }

update_specification
: UPDATE table_reference SET update_column_list where_clause { result = SQLParser::Statement::Update.new(val[1], val[3], val[4]) }

update_column
: column_name equals_operator value_expression { result = SQLParser::Statement::UpdateColumn.new(val[0], val[2]) }

update_column_sublist
: update_column comma update_column_sublist { result = Array(val[0]) + Array(val[2]) }
| update_column

update_column_list
: update_column_sublist { result = SQLParser::Statement::UpdateColumnList.new(val[0]) }

delete_specification
: DELETE from_clause where_clause { result = SQLParser::Statement::Delete.new(val[1], val[2]) }

column_list
: left_paren in_column_list right_paren { result = SQLParser::Statement::InColumnList.new(val[1]) }

Expand Down
1,518 changes: 795 additions & 723 deletions lib/sql-parser/parser.racc.rb

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions lib/sql-parser/parser.rex
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ rule

# keywords
SELECT { [:SELECT, text] }
UPDATE { [:UPDATE, text] }
DELETE { [:DELETE, text] }
DATE { [:DATE, text] }
ASC { [:ASC, text] }
AS { [:AS, text] }
Expand Down Expand Up @@ -74,6 +76,7 @@ rule
DESC { [:DESC, text] }
CURRENT_USER { [:CURRENT_USER, text] }
VALUES { [:VALUES, text] }
SET { [:SET, text] }

# tokens
E { [:E, text] }
Expand Down
15 changes: 12 additions & 3 deletions lib/sql-parser/parser.rex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def scan_file( filename )

def next_token
return if @ss.eos?

# skips empty actions
until token = _next_token or @ss.eos?; end
token
Expand Down Expand Up @@ -78,6 +78,12 @@ def _next_token
when (text = @ss.scan(/SELECT/i))
action { [:SELECT, text] }

when (text = @ss.scan(/UPDATE/i))
action { [:UPDATE, text] }

when (text = @ss.scan(/DELETE/i))
action { [:DELETE, text] }

when (text = @ss.scan(/DATE/i))
action { [:DATE, text] }

Expand Down Expand Up @@ -189,6 +195,9 @@ def _next_token
when (text = @ss.scan(/VALUES/i))
action { [:VALUES, text] }

when (text = @ss.scan(/SET/i))
action { [:SET, text] }

when (text = @ss.scan(/E/i))
action { [:E, text] }

Expand Down Expand Up @@ -260,7 +269,7 @@ def _next_token
action { @state = nil; [:quote, text] }

when (text = @ss.scan(/.*(?=\')/i))
action { [:character_string_literal, text.gsub("''", "'")] }
action { [:character_string_literal, text.gsub("''", "'")] }

else
text = @ss.string[@ss.pos .. -1]
Expand All @@ -273,7 +282,7 @@ def _next_token
action { @state = nil; [:quote, text] }

when (text = @ss.scan(/.*(?=\")/i))
action { [:character_string_literal, text.gsub('""', '"')] }
action { [:character_string_literal, text.gsub('""', '"')] }

else
text = @ss.string[@ss.pos .. -1]
Expand Down
25 changes: 24 additions & 1 deletion lib/sql-parser/sql_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ def initialize
def visit(node)
node.accept(self)
end

def visit_Delete(o)
name = visit(o.from_clause)
where_clause = o.where_clause.nil? ? '' : ' ' + visit(o.where_clause)
"DELETE #{name}#{where_clause}"
end

def visit_Update(o)
name = visit(o.table_reference)
update_column_list = visit(o.update_column_list)
where_clause = o.where_clause.nil? ? '' : ' ' + visit(o.where_clause)
"UPDATE #{name} SET #{update_column_list}#{where_clause}"
end

def visit_UpdateColumn(o)
column = visit(o.column)
value = visit(o.value)
"#{column} = #{value}"
end

def visit_UpdateColumnList(o)
arrayize(o.update_columns)
end

def visit_Insert(o)
name = visit(o.table_reference)
Expand Down Expand Up @@ -359,4 +382,4 @@ def qualified_join(join_type, o)

end

end
end
56 changes: 52 additions & 4 deletions lib/sql-parser/statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,42 @@ def demodulize(str)
end

end

class Delete < Node

def initialize(from_clause, where_clause = nil)
@from_clause = from_clause
@where_clause = where_clause
end

attr_reader :from_clause
attr_reader :where_clause

end

class Update < Node

def initialize(table_reference, update_column_list, where_clause = nil)
@table_reference = table_reference
@update_column_list = update_column_list
@where_clause = where_clause
end

attr_reader :table_reference
attr_reader :update_column_list
attr_reader :where_clause

end

class UpdateColumnList < Node

def initialize(update_columns)
@update_columns = Array(update_columns)
end

attr_reader :update_columns

end

class Insert < Node

Expand Down Expand Up @@ -262,17 +298,17 @@ class In < ComparisonPredicate
class InValueList < Node

def initialize(values)
@values = values
@values = Array(values)
end

attr_reader :values

end

class InColumnList < Node

def initialize(columns)
@columns = columns
@columns = Array(columns)
end

attr_reader :columns
Expand Down Expand Up @@ -408,6 +444,18 @@ class Table < Identifier
class Column < Identifier
end

class UpdateColumn < Node

def initialize(column, value)
@column = column
@value = value
end

attr_reader :column
attr_reader :value

end

class As < Node

def initialize(value, column)
Expand Down Expand Up @@ -513,4 +561,4 @@ class Integer < Literal
end

end
end
end
18 changes: 17 additions & 1 deletion test/test_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,28 @@ def test_current_user
assert_understands 'SELECT `CURRENT_USER`'
assert_understands 'SELECT `current_user`'
end

def test_delete_from
assert_understands 'DELETE FROM `users`'
end

def test_delete_from_with_where
assert_understands 'DELETE FROM `users` WHERE `id` = 1'
end

def test_update
assert_understands 'UPDATE `users` SET `active` = 0'
end

def test_update_multiple
assert_understands 'UPDATE `users` SET `active` = 0, `email` = \'\''
end

def test_insert_into_clause
assert_understands 'INSERT INTO `users` VALUES (1, 2)'
end

def test_insert_into_clause
def test_insert_into_clause_quoted
assert_understands 'INSERT INTO `users` VALUES (`a`, `b`)'
end

Expand Down
28 changes: 28 additions & 0 deletions test/test_statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@
require 'test/unit'

class TestStatement < Test::Unit::TestCase
def test_update_column
assert_sql '`active` = 1', update_col(col('active'), int(1))
end

def test_update
assert_sql 'UPDATE `users` SET `active` = 1', SQLParser::Statement::Update.new(tbl('users'), update_col(col('active'), int(1)))
end

def test_delete
assert_sql 'DELETE FROM `users`', SQLParser::Statement::Delete.new(from(tbl('users')))
end

def test_insert
assert_sql 'INSERT INTO `users` (`id`) VALUES (1)', SQLParser::Statement::Insert.new(tbl('users'), cols(col('id')), values(int(1)))
end

def test_direct_select
assert_sql 'SELECT * FROM `users` ORDER BY `name`', SQLParser::Statement::DirectSelect.new(select(all, tblx(from(tbl('users')))), SQLParser::Statement::OrderBy.new(col('name')))
end
Expand Down Expand Up @@ -285,10 +301,18 @@ def int(value)
SQLParser::Statement::Integer.new(value)
end

def values(ary)
SQLParser::Statement::InValueList.new(ary)
end

def col(name)
SQLParser::Statement::Column.new(name)
end

def cols(ary)
SQLParser::Statement::InColumnList.new(ary)
end

def tbl(name)
SQLParser::Statement::Table.new(name)
end
Expand Down Expand Up @@ -320,4 +344,8 @@ def where(search_condition)
def group_by(columns)
SQLParser::Statement::GroupByClause.new(columns)
end

def update_col(column, value)
SQLParser::Statement::UpdateColumn.new(column, value)
end
end