Skip to content

Commit

Permalink
SQL in ruby (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
coilysiren authored Oct 22, 2023
1 parent 44ca31b commit 59bf008
Show file tree
Hide file tree
Showing 8 changed files with 351 additions and 40 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@ You will get output like so:
```bash
$ invoke test python any

docker run ...
⏱ ./src/python/sort_builtin.py on ./data/sort_input_0.txt ran for 0.5 seconds
docker run ... python ./src/python/sort_builtin.py
⏱ ./src/python/sort_builtin.py on ./data/sort_input_0.txt ran for 0.52 seconds
🟢 ./src/python/sort_builtin.py on ./data/sort_input_0.txt succeeded
docker run ...
⏱ ./src/python/sort_bubble_sort.py on ./data/sort_input_0.txt ran for 0.51 seconds
🟢 ./src/python/sort_bubble_sort.py on ./data/sort_input_0.txt succeeded
docker run ...
⏱ ./src/python/sort_selection_sort.py on ./data/sort_input_0.txt ran for 0.58 seconds
🟢 ./src/python/sort_selection_sort.py on ./data/sort_input_0.txt succeeded
docker run ...
⏱ ./src/python/sort_insertion_sort.py on ./data/sort_input_0.txt ran for 0.52 seconds
🟢 ./src/python/sort_insertion_sort.py on ./data/sort_input_0.txt succeeded
docker run ... python ./src/python/sql_test.py
⏱ ./src/python/sql_test.py on ./data/sql_input_2.sql ran for 0.75 seconds
🟢 ./src/python/sql_test.py on ./data/sql_input_2.sql succeeded
docker run ... python ./src/python/sql_test.py
⏱ ./src/python/sql_test.py on ./data/sql_input_3.sql ran for 0.75 seconds
🟢 ./src/python/sql_test.py on ./data/sql_input_3.sql succeeded
docker run ... python ./src/python/sql_test.py
⏱ ./src/python/sql_test.py on ./data/sql_input_1.sql ran for 0.74 seconds
🟢 ./src/python/sql_test.py on ./data/sql_input_1.sql succeeded

✨ script run success ✨
```
Expand Down
144 changes: 144 additions & 0 deletions snippets/ruby/sql_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
class SQLState
attr_reader :state

def initialize(state = {})
@state = state
end

def read_table_meta(table_name)
@state[table_name] ? @state[table_name]['metadata'] || {} : {}
end

def read_table_rows(table_name)
@state[table_name] ? @state[table_name]['rows'] || [] : []
end

def read_information_schema
@state.values.map { |data| data['metadata'] }
end

def write_table_meta(table_name, data)
@state[table_name] ||= {}
@state[table_name]['metadata'] ||= {}
@state[table_name]['metadata'].merge!(data)
self.class.new(@state)
end

def write_table_rows(table_name, data)
@state[table_name] ||= {}
@state[table_name]['rows'] ||= []
@state[table_name]['rows'] << data
self.class.new(@state)
end
end

module SQLType
def self.varchar(data)
data.to_s.strip.sub(/^['"]/, '').sub(/['"]$/, '')
end

def self.int(data)
data.to_i
end
end

module SQLFunctions
def self.create_table(state, *args, table_schema: 'public')
output = []
table_name = args[2]

columns = {}
columns_str = args[3..].join(' ').gsub(/[()]/, '').strip
unless columns_str.empty?
columns = Hash[columns_str.split(',').map { |column| column.strip.split(' ') }]
end

if state.read_table_meta(table_name).empty?
metadata = {
'table_name' => table_name,
'table_schema' => table_schema,
'columns' => columns
}
state = state.write_table_meta(table_name, metadata)
end

[output, state]
end

def self.insert_into(state, *args)
output = []
table_name = args[2]

sql_type_map = {
'VARCHAR' => SQLType.method(:varchar),
'INT' => SQLType.method(:int)
}

values_index = args.index('VALUES')
raise 'VALUES not found' if values_index.nil?

keys = args[3...values_index].join(' ').gsub(/[()]/, '').split(',').map(&:strip)
values = args[values_index + 1..].join(' ').gsub(/[()]/, '').split(',').map(&:strip)
key_value_map = Hash[keys.zip(values)]

metadata = state.read_table_meta(table_name)
row = {}
key_value_map.each do |key, value|
row[key] = sql_type_map[metadata['columns'][key]].call(value)
end
state = state.write_table_rows(table_name, row)

[output, state]
end

def self.select(state, *args)
output = []

from_index = args.index('FROM')
# where_index = args.index('WHERE')

raise 'FROM not found' if from_index.nil?

select_keys = args[1...from_index].join(' ').split(',').map(&:strip)
from_value = args[from_index + 1]

data = if from_value == 'information_schema.tables'
state.read_information_schema
else
state.read_table_rows(from_value)
end

data.each do |datum|
output << select_keys.map { |key| [key, datum[key]] }.to_h
end

[output, state]
end
end

def run_sql(input_sql)
output = []
state = SQLState.new

input_sql = input_sql.map(&:strip).reject { |line| line.start_with?('--') }
input_sql = input_sql.join(' ').split(';')

sql_function_map = {
'CREATE TABLE' => SQLFunctions.method(:create_table),
'SELECT' => SQLFunctions.method(:select),
'INSERT INTO' => SQLFunctions.method(:insert_into)
}

input_sql.each do |line|
words = line.split(' ')
words.each_index do |i|
key = words[0..i].join(' ').strip
if sql_function_map.key?(key)
output, state = sql_function_map[key].call(state, *words.reject(&:empty?))
break
end
end
end

[output.to_json]
end
14 changes: 7 additions & 7 deletions src/ruby/sort_bubble_sort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ def do_sorting_round(input_list)
# business logic end #
######################

# 👇🏽 copy pasted helpers
if __FILE__ == $PROGRAM_NAME
input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)

input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)
sorted_data = do_sorting(input_file)

sorted_data = do_sorting(input_file)

output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
end
14 changes: 7 additions & 7 deletions src/ruby/sort_builtin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ def do_sorting(input_list)
# business logic end #
######################

# 👇🏽 copy pasted helpers
if __FILE__ == $PROGRAM_NAME
input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)

input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)
sorted_data = do_sorting(input_file)

sorted_data = do_sorting(input_file)

output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
end
14 changes: 7 additions & 7 deletions src/ruby/sort_insertion_sort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ def swap(idx)
# business logic end #
######################

# 👇🏽 copy pasted helpers
if __FILE__ == $PROGRAM_NAME
input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)

input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)
sorted_data = do_sorting(input_file)

sorted_data = do_sorting(input_file)

output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
end
14 changes: 7 additions & 7 deletions src/ruby/sort_selection_sort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ def find_smallest_index(input_list)
# business logic end #
######################

# 👇🏽 copy pasted helpers
if __FILE__ == $PROGRAM_NAME
input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)

input_file_path = ENV['INPUT_PATH']
input_file = File.readlines(input_file_path)
sorted_data = do_sorting(input_file)

sorted_data = do_sorting(input_file)

output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
output_file_path = ENV['OUTPUT_PATH']
File.open(output_file_path, 'wb') { |f| f.write(sorted_data.join('')) }
end
Loading

0 comments on commit 59bf008

Please sign in to comment.