Skip to content
This repository has been archived by the owner on Oct 14, 2020. It is now read-only.

Commit

Permalink
SQL Improvements:
Browse files Browse the repository at this point in the history
- added a sample database to postgres
- fixed SQL create/insert commands
- added ability to import csv files
- improved postgres service starting
  • Loading branch information
jhoffner committed Oct 18, 2016
1 parent 25e5609 commit b3ebc31
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 40 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ services:
- ./examples:/runner/examples
- ./frameworks:/runner/frameworks
- ./test:/runner/test
- ./sample_data:/runner/sample_data
entrypoint: ''
command: bash

Expand Down
20 changes: 19 additions & 1 deletion docker/ruby.docker
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ RUN gem install chronic --no-ri --no-rdoc
# partner packages
RUN gem install ably --no-ri --no-rdoc

USER codewarrior


# Sample Database
# http://www.postgresqltutorial.com/postgresql-sample-database/#

ENV PGDATA /home/codewarrior/pg
ADD sample_data /runner/sample_data
RUN /usr/lib/postgresql/9.6/bin/pg_ctl -w start \
&& createdb -U codewarrior spec \
&& createdb -U codewarrior dvdrental \
&& pg_restore -U codewarrior -d dvdrental -v /runner/sample_data/dvdrental.tar || true \
# && psql -f /runner/sample_data/mlb-samples.sql sports codewarrior \
&& psql -l \
&& /usr/lib/postgresql/9.6/bin/pg_ctl -w stop

USER root

# add the package json first to a tmp directory and build, copy over so that we dont rebuild every time
ADD package.json /tmp/package.json
RUN cd /tmp && npm install --production
Expand All @@ -154,7 +172,7 @@ ENV USER codewarrior
ENV HOME /home/codewarrior

RUN mocha -t 5000 test/runners/ruby_spec.js
#RUN mocha -t 5000 test/runners/sql_spec.js
RUN mocha -t 5000 test/runners/sql_spec.js

#timeout is a fallback in case an error with node
#prevents it from exiting properly
Expand Down
12 changes: 3 additions & 9 deletions examples/sql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@ rspec:
fundamentals:
initial: |-
answer: |-
CREATE TABLE Persons
(
PersonID int,
LastName varchar(255),
FirstName varchar(255)
);
-- SQLite
CREATE TABLE Persons(PersonID int, LastName varchar(255), FirstName varchar(255));
INSERT INTO Persons (PersonID, LastName, FirstName) VALUES (1, "Doe", "Jane");
SELECT * FROM Persons
SELECT * FROM Persons;
fixture: |-
describe :Persons do
Expand Down
12 changes: 12 additions & 0 deletions frameworks/ruby/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,17 @@ def prop(name, value)
def format_msg(msg)
msg.gsub("\n", '<:LF:>')
end

def status(msg)
print("STATUS", msg)
end

def raw(content)
puts html_tag("pre", content)
end

def html_tag(tag, content)
puts "<#{tag}>#{content}</#{tag}>"
end
end
end
37 changes: 28 additions & 9 deletions frameworks/ruby/sql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,39 @@
require 'hashie'
require_relative 'sql/csv_importer'

def clean_sql(sql)
sql.gsub(/(\/\*([\s\S]*?)\*\/|--.*)/, "")
end

def split_sql_commands(sql)
sql.split(/;[ \n\r]*$/).select(&:present?)
end

$sql = File.read('/home/codewarrior/solution.txt')
$sql_cleaned = $sql.gsub(/(\/\*([\s\S]*?)\*\/|--.*)/, "")
$sql_commands = $sql_cleaned.split(/; *$/).select(&:present?)
$sql_cleaned = clean_sql($sql)
$sql_commands = split_sql_commands($sql_cleaned)

# runs sql commands within a file. Useful for running external scripts such as importing data
def run_sql_file(file, &block)
sql = clean_sql(File.read(file))

split_sql_commands(sql).each do |cmd|
result = cmd.downcase =~ /^(insert|create)/ ? DB.run(cmd) : DB[cmd]
block.call(cmd, result) if block
end
end

# the main method used when running user's code
def run_sql(limit: 100, cmds: $sql_commands, print: true)
results = cmds.map do |cmd|
(cmd.downcase.start_with?("insert") ? DB.prepare(cmd) : DB[cmd]).tap do |dataset|
if dataset.count > 0 or $sql_commands.length == 1
label = "SQL Results"
label += " (Top #{limit} of #{dataset.count})" if dataset.count > limit
dataset = (cmd.downcase =~ /^(insert|create)/ ? DB.run(cmd) : DB[cmd]) || []
if dataset.count > 0
label = "SQL Results"
label += " (Top #{limit} of #{dataset.count})" if dataset.count > limit

Display.table(dataset.limit(limit).to_a, label: label) if print
end
Display.table(dataset.to_a.take(limit), label: label) if print
end
dataset
end

results.select! {|r| r.count > 0 }
Expand All @@ -28,7 +47,7 @@ def run_sql(limit: 100, cmds: $sql_commands, print: true)
$sql_results = results
else
$sql_multi = false
$sql_results = results.first
$sql_results = results.first || []
end

rescue Sequel::DatabaseError => ex
Expand Down
7 changes: 5 additions & 2 deletions frameworks/ruby/sql/csv_importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def convert_value(field, value)
case @fields[field]
when DateTime
Chronic.parse(value)
when Integer
value.gsub(',', '').to_i
else
value
end
Expand All @@ -38,7 +40,7 @@ def convert_value(field, value)
def import(skip_schema: false)
create_schema unless skip_schema

puts "<STATUS::>Importing #{[limit, @csv.count].min} records..."
Display.status("Importing #{[limit, @csv.count].min} records...")

fields = @csv.first
dataset = DB[@table]
Expand All @@ -64,7 +66,8 @@ def import(skip_schema: false)
def self.import_sales_data(random: false, limit: 300)
importer = CsvImporter.new("/runner/sample_data/sales.csv", :sales, random: random, limit: limit, fields: {
'latitude' => Float,
'longitude' => Float
'longitude' => Float,
'price' => Integer
})

# TODO: figure out how to fix datetime fields for SQLite
Expand Down
21 changes: 17 additions & 4 deletions lib/runners/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,34 @@ module.exports.run = function run(opts, cb) {
function prepareSetup(opts) {
opts.setup = `
require '/runner/frameworks/ruby/sql'
DB = Sequel.${connectDb(opts.languageVersion)}
DB = Sequel.${connectDb(opts)}
${opts.setup || ''}
`
}


function connectDb(type) {
type = type || 'sqlite';
function connectDb(opts) {
var type = opts.languageVersion || 'sqlite',
database = dbName(opts) || 'postgres';

if (type == 'postgres') {
return `connect("postgres://localhost/postgres")`;
return `connect("postgres://localhost/${database}");DATABASE = "${database}"`;
}

return type;
}

// checks the first two lines of the setup code to see if a database name was specified. This is done
// because the first line can be used as a comment explaining why its there
function dbName(opts) {
if (opts.setup) {
var name = opts.setup.split("\n").slice(0,2).find(l => l.indexOf("# database:") === 0);
if (name) {
return name.split(":")[1].trim();
}
}
}

function addService(opts, name) {
opts.services = opts.services || [];
if (opts.services.indexOf(name) === -1) {
Expand Down
4 changes: 2 additions & 2 deletions lib/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const startService = {
// stop the service. This is mostly needed just for specs since under normal circumstances the entire
// container would be destroyed and no cleanup would be necessary
opts.onCompleted.push(function() {
spawn("/usr/lib/postgresql/9.6/bin/pg_ctl", ['-D', '/home/codewarrior/pg', 'stop'])
spawn("/usr/lib/postgresql/9.6/bin/pg_ctl", ['-D', '/home/codewarrior/pg', '-w', 'stop'])
});

return spawnAndWait(opts, '/usr/lib/postgresql/9.6/bin/pg_ctl', ['-D', '/home/codewarrior/pg', 'start'], 'autovacuum launcher started', 200 )
return spawnAndWait(opts, '/usr/lib/postgresql/9.6/bin/pg_ctl', ['-D', '/home/codewarrior/pg', '-w', 'start'])
},
mariadb: function(opts) {
// TODO
Expand Down
Binary file added sample_data/dvdrental.tar
Binary file not shown.
26 changes: 13 additions & 13 deletions test/runners/sql_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var expect = require('chai').expect;
var runner = require('../runner');

var itemsData = `
# database: spec
DB.drop_table :items rescue nil
DB.create_table :items do
primary_key :id
Expand Down Expand Up @@ -73,18 +73,18 @@ describe('sql runner', function () {
});
});

it("should support postgres", function(done) {
runner.run({
language: 'sql',
languageVersion: 'postgres',
setup: itemsData,
code: query,
fixture: itemsFixture
}, function(buffer) {
expect(buffer.stdout).to.contain('PASSED');
done();
});
});
// it("should support postgres", function(done) {
// runner.run({
// language: 'sql',
// languageVersion: 'postgres',
// setup: itemsData,
// code: query,
// fixture: itemsFixture
// }, function(buffer) {
// expect(buffer.stdout).to.contain('PASSED');
// done();
// });
// });
});
});
});

0 comments on commit b3ebc31

Please sign in to comment.