Skip to content

Commit

Permalink
Adding delimited identifiers (#6)
Browse files Browse the repository at this point in the history
Delimited identifiers are surrounded by double-quotes. A delimited
identifier casing is kept the same. Whereas as regular identifier is not
case-sensitive and will always be represented as upper case.
  • Loading branch information
elliotchance authored Jul 24, 2021
1 parent 3d1c610 commit 0d5b19e
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 65 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ no dependencies.
- [UPDATE](#update)
- [Appendix](#appendix)
- [Data Types](#data-types)
- [Keywords](#keywords)
- [SQLSTATE (Errors)](#sqlstate-errors)
- [Testing](#testing)

Expand Down Expand Up @@ -254,12 +255,12 @@ separated by an empty line:

```sql
SELECT 1
-- col1: 1
-- COL1: 1

SELECT 2
SELECT 3
-- col2: 2
-- col1: 3
-- COL1: 2
-- COL1: 3
```

This is two tests where each test is given an a brand new database. All SQL statements are executed and each of the results collected and compared to the
Expand Down
12 changes: 11 additions & 1 deletion tests/create-table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,17 @@ CREATE TABLE bar (baz VARCHAR(10))
CREATE TABLE foo (a FLOAT)
CREATE TABLE foo (baz CHARACTER VARYING(10))
-- msg: CREATE TABLE 1
-- error: vsql.SQLState42P07: duplicate table: foo
-- error: vsql.SQLState42P07: duplicate table: FOO

CREATE TABLE t1 (f1 CHARACTER VARYING(10), f2 FLOAT)
-- msg: CREATE TABLE 1

CREATE TABLE foo (a FLOAT)
CREATE TABLE Foo (baz CHARACTER VARYING(10))
-- msg: CREATE TABLE 1
-- error: vsql.SQLState42P07: duplicate table: FOO

CREATE TABLE "foo" (a FLOAT)
CREATE TABLE "Foo" (baz CHARACTER VARYING(10))
-- msg: CREATE TABLE 1
-- msg: CREATE TABLE 1
4 changes: 2 additions & 2 deletions tests/delete.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
DELETE FROM foo
-- error: vsql.SQLState42P01: no such table: foo
-- error: vsql.SQLState42P01: no such table: FOO

CREATE TABLE foo (baz CHARACTER VARYING(10))
INSERT INTO foo (baz) VALUES ('hi')
Expand All @@ -24,4 +24,4 @@ SELECT * FROM foo
-- msg: INSERT 1
-- msg: DELETE 1
-- msg: DELETE 0
-- baz: 78
-- BAZ: 78
4 changes: 2 additions & 2 deletions tests/drop-table.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
DROP TABLE foo
-- error: vsql.SQLState42P01: no such table: foo
-- error: vsql.SQLState42P01: no such table: FOO

CREATE TABLE foo (a FLOAT)
DROP TABLE foo
DROP TABLE foo
-- msg: CREATE TABLE 1
-- msg: DROP TABLE 1
-- error: vsql.SQLState42P01: no such table: foo
-- error: vsql.SQLState42P01: no such table: FOO
4 changes: 2 additions & 2 deletions tests/insert.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ SELECT * FROM foo
-- msg: CREATE TABLE 1
-- msg: INSERT 1
-- msg: INSERT 1
-- a: 101
-- a: 102
-- A: 101
-- A: 102
17 changes: 15 additions & 2 deletions tests/select-from.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
SELECT * FROM foo
-- error: vsql.SQLState42P01: no such table: foo
-- error: vsql.SQLState42P01: no such table: FOO

CREATE TABLE foo (a FLOAT)
INSERT INTO foo (a) VALUES (1.234)
SELECT * FROM foo
-- msg: CREATE TABLE 1
-- msg: INSERT 1
-- a: 1.234
-- A: 1.234

CREATE TABLE foo (a FLOAT)
CREATE TABLE "Foo" ("a" FLOAT)
INSERT INTO FOO (a) VALUES (1.234)
INSERT INTO "Foo" ("a") VALUES (4.56)
SELECT * FROM Foo
SELECT * FROM "Foo"
-- msg: CREATE TABLE 1
-- msg: CREATE TABLE 1
-- msg: INSERT 1
-- msg: INSERT 1
-- A: 1.234
-- a: 4.56
8 changes: 4 additions & 4 deletions tests/select-literal.sql
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
SELECT 1
-- col1: 1
-- COL1: 1

SELECT 1.23
-- col1: 1.23
-- COL1: 1.23

select 789
-- col1: 789
-- COL1: 789

Select 'hello'
-- col1: hello
-- COL1: hello
30 changes: 15 additions & 15 deletions tests/select-where.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ SELECT * FROM foo WHERE num <= 27
-- msg: INSERT 1
-- msg: INSERT 1
-- msg: INSERT 1
-- col1: =
-- num: 27
-- col1: !=
-- num: 27
-- num: 35
-- col1: >
-- num: 35
-- col1: >=
-- num: 27
-- num: 35
-- col1: <
-- num: 13
-- col1: <=
-- num: 13
-- num: 27
-- COL1: =
-- NUM: 27
-- COL1: !=
-- NUM: 27
-- NUM: 35
-- COL1: >
-- NUM: 35
-- COL1: >=
-- NUM: 27
-- NUM: 35
-- COL1: <
-- NUM: 13
-- COL1: <=
-- NUM: 13
-- NUM: 27
10 changes: 5 additions & 5 deletions tests/update.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
UPDATE foo SET a = 123
-- error: vsql.SQLState42P01: no such table: foo
-- error: vsql.SQLState42P01: no such table: FOO

CREATE TABLE foo (baz CHARACTER VARYING(10))
INSERT INTO foo (baz) VALUES ('hi')
Expand All @@ -12,8 +12,8 @@ SELECT * FROM foo
-- msg: INSERT 1
-- msg: UPDATE 1
-- msg: UPDATE 2
-- baz: other
-- baz: other
-- BAZ: other
-- BAZ: other

CREATE TABLE foo (baz FLOAT)
INSERT INTO foo (baz) VALUES (35)
Expand All @@ -26,5 +26,5 @@ SELECT * FROM foo
-- msg: INSERT 1
-- msg: UPDATE 1
-- msg: UPDATE 0
-- baz: 78
-- baz: 100
-- BAZ: 78
-- BAZ: 100
19 changes: 13 additions & 6 deletions vsql/create_table.v
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ module vsql
// TODO(elliotchance): A table is allowed to have zero columns.

fn (mut c Connection) create_table(stmt CreateTableStmt) ?Result {
if stmt.table_name in c.storage.tables {
return sqlstate_42p07(stmt.table_name) // duplicate table
table_name := identifier_name(stmt.table_name)

if table_name in c.storage.tables {
return sqlstate_42p07(table_name) // duplicate table
}

if is_reserved_word(stmt.table_name) {
return sqlstate_42601('table name cannot be reserved word: $stmt.table_name.to_upper()')
if is_reserved_word(table_name) {
return sqlstate_42601('table name cannot be reserved word: $table_name')
}

mut columns := []Column{cap: stmt.columns.len}
for column in stmt.columns {
column_name := identifier_name(column.name)

if is_reserved_word(column.name) {
return sqlstate_42601('column name cannot be reserved word: $column.name.to_upper()')
return sqlstate_42601('column name cannot be reserved word: $column_name')
}

columns << Column{column_name, column.typ}
}

c.storage.create_table(stmt.table_name, stmt.columns) ?
c.storage.create_table(table_name, columns) ?

return new_result_msg('CREATE TABLE 1')
}
8 changes: 5 additions & 3 deletions vsql/delete.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
module vsql

fn (mut c Connection) delete(stmt DeleteStmt) ?Result {
if stmt.table_name !in c.storage.tables {
return sqlstate_42p01(stmt.table_name) // table not found
table_name := identifier_name(stmt.table_name)

if table_name !in c.storage.tables {
return sqlstate_42p01(table_name) // table not found
}

table := c.storage.tables[stmt.table_name]
table := c.storage.tables[table_name]
mut rows := c.storage.read_rows(table.index) ?

mut deleted := 0
Expand Down
8 changes: 5 additions & 3 deletions vsql/drop_table.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
module vsql

fn (mut c Connection) drop_table(stmt DropTableStmt) ?Result {
if stmt.table_name !in c.storage.tables {
return sqlstate_42p01(stmt.table_name) // table does not exist
table_name := identifier_name(stmt.table_name)

if table_name !in c.storage.tables {
return sqlstate_42p01(table_name) // table does not exist
}

// TODO(elliotchance): Also delete rows.
c.storage.delete_table(stmt.table_name) ?
c.storage.delete_table(table_name) ?

return new_result_msg('DROP TABLE 1')
}
8 changes: 5 additions & 3 deletions vsql/eval.v
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ fn eval(data Row, e BinaryExpr) ?bool {
}

fn eval_binary(data Row, e BinaryExpr) ?bool {
if data.data[e.col].typ == .is_f64 && e.value.typ == .is_f64 {
return eval_cmp<f64>(data.get_f64(e.col), e.value.f64_value, e.op)
col := identifier_name(e.col)

if data.data[col].typ == .is_f64 && e.value.typ == .is_f64 {
return eval_cmp<f64>(data.get_f64(col), e.value.f64_value, e.op)
}

return error('cannot $e.col $e.op $e.value')
return error('cannot $col $e.op $e.value')
}

fn eval_cmp<T>(lhs T, rhs T, op string) bool {
Expand Down
12 changes: 12 additions & 0 deletions vsql/identifier.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// indentifier.v contains logic for normalzing and using identifiers (such as
// table and column names).

module vsql

fn identifier_name(s string) string {
if s.len > 0 && s[0] == `"` {
return s[1..s.len - 1]
}

return s.to_upper()
}
5 changes: 3 additions & 2 deletions vsql/insert.v
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ fn (mut c Connection) insert(stmt InsertStmt) ?Result {
mut row := Row{
data: map[string]Value{}
}
row.data[stmt.columns[0]] = stmt.values[0]
row.data[identifier_name(stmt.columns[0])] = stmt.values[0]

c.storage.write_row(row, c.storage.tables[stmt.table_name]) ?
table_name := identifier_name(stmt.table_name)
c.storage.write_row(row, c.storage.tables[table_name]) ?

return new_result_msg('INSERT 1')
}
15 changes: 14 additions & 1 deletion vsql/lexer.v
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum TokenKind {
keyword_varchar // VARCHAR
keyword_varying // VARYING
keyword_where // WHERE
literal_identifier // foo
literal_identifier // foo or "foo" (delimited)
literal_number // 123
literal_string // 'hello'
op_comma // ,
Expand Down Expand Up @@ -87,6 +87,19 @@ fn tokenize(sql string) []Token {
continue
}

// delimited identifiers
if cs[i] == `"` {
mut word := ''
i++
for i < cs.len && cs[i] != `"` {
word += '${cs[i]}'
i++
}
i++
tokens << Token{TokenKind.literal_identifier, '"$word"'}
continue
}

// operators
multi := map{
'!=': TokenKind.op_neq
Expand Down
12 changes: 7 additions & 5 deletions vsql/select.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ module vsql

fn (mut c Connection) query_select(stmt SelectStmt) ?Result {
if stmt.from != '' {
if stmt.from in c.storage.tables {
table := c.storage.tables[stmt.from]
table_name := identifier_name(stmt.from)

if table_name in c.storage.tables {
table := c.storage.tables[table_name]

mut rows := c.storage.read_rows(table.index) ?
if stmt.where.op != '' {
Expand All @@ -15,13 +17,13 @@ fn (mut c Connection) query_select(stmt SelectStmt) ?Result {
return new_result(table.column_names(), rows)
}

return sqlstate_42p01(stmt.from)
return sqlstate_42p01(table_name)
}

return new_result(['col1'], [
return new_result(['COL1'], [
Row{
data: map{
'col1': stmt.value
'COL1': stmt.value
}
},
])
Expand Down
16 changes: 10 additions & 6 deletions vsql/update.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
module vsql

fn (mut c Connection) update(stmt UpdateStmt) ?Result {
if stmt.table_name !in c.storage.tables {
return sqlstate_42p01(stmt.table_name) // table does not exist
table_name := identifier_name(stmt.table_name)

if table_name !in c.storage.tables {
return sqlstate_42p01(table_name) // table does not exist
}

table := c.storage.tables[stmt.table_name]
table := c.storage.tables[table_name]

mut delete_rows := []Row{}
mut new_rows := []Row{}
Expand All @@ -24,10 +26,12 @@ fn (mut c Connection) update(stmt UpdateStmt) ?Result {
data: row.data.clone()
}
for k, v in stmt.set {
if row.data[k] != v {
column_name := identifier_name(k)

if row.data[column_name] != v {
did_modify = true
row.data[k] = v
new_row.data[k] = v
row.data[column_name] = v
new_row.data[column_name] = v
}
}

Expand Down

0 comments on commit 0d5b19e

Please sign in to comment.