Skip to content
Snippets Groups Projects
Commit 5ab352e3 authored by Konstantin Osipov's avatar Konstantin Osipov
Browse files

Merge branch 'test-runner'

parents e0839e9c 78d36e0b
No related branches found
No related tags found
No related merge requests found
...@@ -23,3 +23,33 @@ Found 1 tuple: ...@@ -23,3 +23,33 @@ Found 1 tuple:
select * from t0 where k0 = 1 select * from t0 where k0 = 1
Found 1 tuple: Found 1 tuple:
[1, 'I am a tuple'] [1, 'I am a tuple']
delete from t0 where k0 = 1
Delete OK, 1 row affected
select * from t0 where k0 = 1
No match
update t0 set k1 = "I am a new tuple" where k0=1
Insert OK, 0 row affected
select * from t0 where k0 = 1
No match
insert into t0 values (1, "I am a new tuple")
Insert OK, 1 row affected
select * from t0 where k0 = 1
Found 1 tuple:
[1, 'I am a new tuple']
update t0 set k1 = "I am the newest tuple" where k0=1
Insert OK, 1 row affected
select * from t0 where k0 = 1
Found 1 tuple:
[1, 'I am the newest tuple']
update t0 set k1 = "Huh", k2 = "Oh-ho-ho" where k0=1
An error occurred: ERR_CODE_ILLEGAL_PARAMS, 'Illegal parameters'
select * from t0 where k0 = 1
Found 1 tuple:
[1, 'I am the newest tuple']
insert into t0 values (1, "I am a new tuple", "stub")
Insert OK, 1 row affected
update t0 set k1 = "Huh", k2 = "Oh-ho-ho" where k0=1
Insert OK, 1 row affected
select * from t0 where k0 = 1
Found 1 tuple:
[1, 'Huh', 'Oh-ho-ho']
...@@ -14,5 +14,19 @@ exec admin "save snapshot" ...@@ -14,5 +14,19 @@ exec admin "save snapshot"
exec sql 'select * from t0 where k0 = 1' exec sql 'select * from t0 where k0 = 1'
server.restart() server.restart()
exec sql 'select * from t0 where k0 = 1' exec sql 'select * from t0 where k0 = 1'
exec sql 'delete from t0 where k0 = 1'
exec sql 'select * from t0 where k0 = 1'
exec sql 'update t0 set k1 = "I am a new tuple" where k0=1'
exec sql 'select * from t0 where k0 = 1'
exec sql 'insert into t0 values (1, "I am a new tuple")'
exec sql 'select * from t0 where k0 = 1'
exec sql 'update t0 set k1 = "I am the newest tuple" where k0=1'
exec sql 'select * from t0 where k0 = 1'
# this is illegal, can't change tuple dimension with update
exec sql 'update t0 set k1 = "Huh", k2 = "Oh-ho-ho" where k0=1'
exec sql 'select * from t0 where k0 = 1'
exec sql 'insert into t0 values (1, "I am a new tuple", "stub")'
exec sql 'update t0 set k1 = "Huh", k2 = "Oh-ho-ho" where k0=1'
exec sql 'select * from t0 where k0 = 1'
# vim: syntax=python # vim: syntax=python
import sql_ast import sql_ast
import re
object_no_re = re.compile("[a-z_]*", re.I)
%% %%
...@@ -26,18 +29,18 @@ parser sql: ...@@ -26,18 +29,18 @@ parser sql:
select {{ stmt = select }} | select {{ stmt = select }} |
ping {{ stmt = ping }}) END {{ return stmt }} ping {{ stmt = ping }}) END {{ return stmt }}
rule insert: INSERT [INTO] ID VALUES value_list rule insert: INSERT [INTO] ident VALUES value_list
{{ return sql_ast.StatementInsert(ID, value_list) }} {{ return sql_ast.StatementInsert(ident, value_list) }}
rule update: UPDATE ID SET update_list opt_where rule update: UPDATE ident SET update_list opt_where
{{ return sql_ast.StatementUpdate(ID, update_list, opt_where) }} {{ return sql_ast.StatementUpdate(ident, update_list, opt_where) }}
rule delete: DELETE FROM ID opt_where rule delete: DELETE FROM ident opt_where
{{ return sql_ast.StatementDelete(ID, opt_where) }} {{ return sql_ast.StatementDelete(ident, opt_where) }}
rule select: SELECT '\*' FROM ID opt_where rule select: SELECT '\*' FROM ident opt_where
{{ return sql_ast.StatementSelect(ID, opt_where) }} {{ return sql_ast.StatementSelect(ident, opt_where) }}
rule ping: PING rule ping: PING
{{ return sql_ast.StatementPing() }} {{ return sql_ast.StatementPing() }}
rule predicate: ID '=' constant rule predicate: ident '=' constant
{{ return (ID, constant) }} {{ return (ident, constant) }}
rule opt_where: {{ return None }} rule opt_where: {{ return None }}
| WHERE predicate | WHERE predicate
{{ return predicate }} {{ return predicate }}
...@@ -49,6 +52,7 @@ parser sql: ...@@ -49,6 +52,7 @@ parser sql:
{{ return update_list }} {{ return update_list }}
rule expr: constant {{ return constant }} rule expr: constant {{ return constant }}
rule constant: NUM {{ return int(NUM) }} | STR {{ return STR[1:-1] }} rule constant: NUM {{ return int(NUM) }} | STR {{ return STR[1:-1] }}
rule ident: ID {{ return int(object_no_re.sub("", ID)) }}
%% %%
# SQL is case-insensitive, but in yapps it's not possible to # SQL is case-insensitive, but in yapps it's not possible to
......
import sql_ast import sql_ast
import re
object_no_re = re.compile("[a-z_]*", re.I)
# Begin -- grammar generated by Yapps # Begin -- grammar generated by Yapps
...@@ -60,36 +63,36 @@ class sql(runtime.Parser): ...@@ -60,36 +63,36 @@ class sql(runtime.Parser):
INSERT = self._scan('INSERT', context=_context) INSERT = self._scan('INSERT', context=_context)
if self._peek('INTO', 'ID', context=_context) == 'INTO': if self._peek('INTO', 'ID', context=_context) == 'INTO':
INTO = self._scan('INTO', context=_context) INTO = self._scan('INTO', context=_context)
ID = self._scan('ID', context=_context) ident = self.ident(_context)
VALUES = self._scan('VALUES', context=_context) VALUES = self._scan('VALUES', context=_context)
value_list = self.value_list(_context) value_list = self.value_list(_context)
return sql_ast.StatementInsert(ID, value_list) return sql_ast.StatementInsert(ident, value_list)
def update(self, _parent=None): def update(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'update', []) _context = self.Context(_parent, self._scanner, 'update', [])
UPDATE = self._scan('UPDATE', context=_context) UPDATE = self._scan('UPDATE', context=_context)
ID = self._scan('ID', context=_context) ident = self.ident(_context)
SET = self._scan('SET', context=_context) SET = self._scan('SET', context=_context)
update_list = self.update_list(_context) update_list = self.update_list(_context)
opt_where = self.opt_where(_context) opt_where = self.opt_where(_context)
return sql_ast.StatementUpdate(ID, update_list, opt_where) return sql_ast.StatementUpdate(ident, update_list, opt_where)
def delete(self, _parent=None): def delete(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'delete', []) _context = self.Context(_parent, self._scanner, 'delete', [])
DELETE = self._scan('DELETE', context=_context) DELETE = self._scan('DELETE', context=_context)
FROM = self._scan('FROM', context=_context) FROM = self._scan('FROM', context=_context)
ID = self._scan('ID', context=_context) ident = self.ident(_context)
opt_where = self.opt_where(_context) opt_where = self.opt_where(_context)
return sql_ast.StatementDelete(ID, opt_where) return sql_ast.StatementDelete(ident, opt_where)
def select(self, _parent=None): def select(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'select', []) _context = self.Context(_parent, self._scanner, 'select', [])
SELECT = self._scan('SELECT', context=_context) SELECT = self._scan('SELECT', context=_context)
self._scan("'\\*'", context=_context) self._scan("'\\*'", context=_context)
FROM = self._scan('FROM', context=_context) FROM = self._scan('FROM', context=_context)
ID = self._scan('ID', context=_context) ident = self.ident(_context)
opt_where = self.opt_where(_context) opt_where = self.opt_where(_context)
return sql_ast.StatementSelect(ID, opt_where) return sql_ast.StatementSelect(ident, opt_where)
def ping(self, _parent=None): def ping(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'ping', []) _context = self.Context(_parent, self._scanner, 'ping', [])
...@@ -98,10 +101,10 @@ class sql(runtime.Parser): ...@@ -98,10 +101,10 @@ class sql(runtime.Parser):
def predicate(self, _parent=None): def predicate(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'predicate', []) _context = self.Context(_parent, self._scanner, 'predicate', [])
ID = self._scan('ID', context=_context) ident = self.ident(_context)
self._scan("'='", context=_context) self._scan("'='", context=_context)
constant = self.constant(_context) constant = self.constant(_context)
return (ID, constant) return (ident, constant)
def opt_where(self, _parent=None): def opt_where(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'opt_where', []) _context = self.Context(_parent, self._scanner, 'opt_where', [])
...@@ -154,6 +157,11 @@ class sql(runtime.Parser): ...@@ -154,6 +157,11 @@ class sql(runtime.Parser):
STR = self._scan('STR', context=_context) STR = self._scan('STR', context=_context)
return STR[1:-1] return STR[1:-1]
def ident(self, _parent=None):
_context = self.Context(_parent, self._scanner, 'ident', [])
ID = self._scan('ID', context=_context)
return int(object_no_re.sub("", ID))
def parse(rule, text): def parse(rule, text):
P = sql(sqlScanner(text)) P = sql(sqlScanner(text))
......
...@@ -4,11 +4,17 @@ import ctypes ...@@ -4,11 +4,17 @@ import ctypes
# IPROTO header is always 3 4-byte ints: # IPROTO header is always 3 4-byte ints:
# command code, length, request id # command code, length, request id
IPROTO_HEADER_LEN = 12 INT_FIELD_LEN = 4
INSERT_REQUEST_FIXED_LEN = 8 INT_BER_MAX_LEN = 5
SELECT_REQUEST_FIXED_LEN = 20 IPROTO_HEADER_LEN = 3*INT_FIELD_LEN
INSERT_REQUEST_FIXED_LEN = 2*INT_FIELD_LEN
UPDATE_REQUEST_FIXED_LEN = 2*INT_FIELD_LEN
DELETE_REQUEST_FIXED_LEN = INT_FIELD_LEN
SELECT_REQUEST_FIXED_LEN = 5*INT_FIELD_LEN
PACKET_BUF_LEN = 2048 PACKET_BUF_LEN = 2048
UPDATE_SET_FIELD_OPCODE = 0
# command code in IPROTO header # command code in IPROTO header
INSERT_REQUEST_TYPE = 13 INSERT_REQUEST_TYPE = 13
...@@ -51,7 +57,6 @@ def format_error(return_code): ...@@ -51,7 +57,6 @@ def format_error(return_code):
return "An error occurred: {0}, \'{1}'".format(ER[return_code][0], return "An error occurred: {0}, \'{1}'".format(ER[return_code][0],
ER[return_code][1]) ER[return_code][1])
object_no_re = re.compile("[a-z_]*", re.I)
def save_varint32(value): def save_varint32(value):
"""Implement Perl pack's 'w' option, aka base 128 encoding.""" """Implement Perl pack's 'w' option, aka base 128 encoding."""
...@@ -92,29 +97,48 @@ def opt_resize_buf(buf, newsize): ...@@ -92,29 +97,48 @@ def opt_resize_buf(buf, newsize):
return buf return buf
def pack_field(value, buf, offset):
if type(value) is int:
buf = opt_resize_buf(buf, offset + INT_FIELD_LEN)
struct.pack_into("<cL", buf, offset, chr(INT_FIELD_LEN), value)
offset += INT_FIELD_LEN + 1
elif type(value) is str:
opt_resize_buf(buf, offset + INT_BER_MAX_LEN + len(value))
value_len_ber = save_varint32(len(value))
struct.pack_into("{0}s{1}s".format(len(value_len_ber), len(value)),
buf, offset, value_len_ber, value)
offset += len(value_len_ber) + len(value)
else:
raise RuntimeError("Unsupported value type in value list")
return (buf, offset)
def pack_tuple(value_list, buf, offset): def pack_tuple(value_list, buf, offset):
"""Represents <tuple> rule in tarantool protocol description.
Pack tuple into a binary representation.
buf and offset are in-out parameters, offset is advanced
to the amount of bytes that it took to pack the tuple"""
# length of int field: 1 byte - field len (is always 4), 4 bytes - data # length of int field: 1 byte - field len (is always 4), 4 bytes - data
INT_FIELD_LEN = 4
# max length of compressed integer # max length of compressed integer
INT_BER_MAX_LEN = 5
cardinality = len(value_list) cardinality = len(value_list)
struct.pack_into("<L", buf, offset, cardinality) struct.pack_into("<L", buf, offset, cardinality)
offset += INT_FIELD_LEN offset += INT_FIELD_LEN
for value in value_list: for value in value_list:
if type(value) is int: (buf, offset) = pack_field(value, buf, offset)
buf = opt_resize_buf(buf, offset + INT_FIELD_LEN)
struct.pack_into("<cL", buf, offset, chr(INT_FIELD_LEN), value)
offset += INT_FIELD_LEN + 1
elif type(value) is str:
opt_resize_buf(buf, offset + INT_BER_MAX_LEN + len(value))
value_len_ber = save_varint32(len(value))
struct.pack_into("{0}s{1}s".format(len(value_len_ber), len(value)),
buf, offset, value_len_ber, value)
offset += len(value_len_ber) + len(value)
else:
raise RuntimeError("Unsupported value type in value list")
return buf, offset return buf, offset
def pack_operation_list(update_list, buf, offset):
buf = opt_resize_buf(buf, offset + INT_FIELD_LEN)
struct.pack_into("<L", buf, offset, len(update_list))
offset += INT_FIELD_LEN
for update in update_list:
opt_resize_buf(buf, offset + INT_FIELD_LEN + 1)
struct.pack_into("<Lc", buf, offset,
update[0],
chr(UPDATE_SET_FIELD_OPCODE))
offset += INT_FIELD_LEN + 1
(buf, offset) = pack_field(update[1], buf, offset)
return (buf, offset)
def unpack_tuple(response, offset): def unpack_tuple(response, offset):
(size,cardinality) = struct.unpack("<LL", response[offset:offset + 8]) (size,cardinality) = struct.unpack("<LL", response[offset:offset + 8])
...@@ -128,6 +152,7 @@ def unpack_tuple(response, offset): ...@@ -128,6 +152,7 @@ def unpack_tuple(response, offset):
(data,) = struct.unpack("<L", data) (data,) = struct.unpack("<L", data)
res.append(data) res.append(data)
return str(res), offset return str(res), offset
class StatementPing: class StatementPing:
reqeust_type = PING_REQUEST_TYPE reqeust_type = PING_REQUEST_TYPE
...@@ -141,7 +166,7 @@ class StatementInsert(StatementPing): ...@@ -141,7 +166,7 @@ class StatementInsert(StatementPing):
reqeust_type = INSERT_REQUEST_TYPE reqeust_type = INSERT_REQUEST_TYPE
def __init__(self, table_name, value_list): def __init__(self, table_name, value_list):
self.namespace_no = int(object_no_re.sub("", table_name)) self.namespace_no = table_name
self.flags = 0 self.flags = 0
self.value_list = value_list self.value_list = value_list
...@@ -163,25 +188,58 @@ class StatementUpdate(StatementPing): ...@@ -163,25 +188,58 @@ class StatementUpdate(StatementPing):
reqeust_type = UPDATE_REQUEST_TYPE reqeust_type = UPDATE_REQUEST_TYPE
def __init__(self, table_name, update_list, where): def __init__(self, table_name, update_list, where):
self.namespace_no = int(object_no_re.sub("", table_name)) self.namespace_no = table_name
self.flags = 0
key_no = where[0]
if key_no != 0:
raise RuntimeError("UPDATE can only be made by the primary key (#0)")
self.value_list = where[1:]
self.update_list = update_list self.update_list = update_list
self.where = where
def pack(self):
buf = ctypes.create_string_buffer(PACKET_BUF_LEN)
struct.pack_into("<LL", buf, 0, self.namespace_no, self.flags)
(buf, offset) = pack_tuple(self.value_list, buf, UPDATE_REQUEST_FIXED_LEN)
(buf, offset) = pack_operation_list(self.update_list, buf, offset)
return buf[:offset]
def unpack(self, response):
(return_code,) = struct.unpack("<L", response[:4])
if return_code:
return format_error(return_code)
(result_code, row_count) = struct.unpack("<LL", response)
return "Insert OK, {0} row affected".format(row_count)
class StatementDelete(StatementPing): class StatementDelete(StatementPing):
reqeust_type = DELETE_REQUEST_TYPE reqeust_type = DELETE_REQUEST_TYPE
def __init__(self, table_name, where): def __init__(self, table_name, where):
self.namespace_no = int(object_no_re.sub("", table_name)) self.namespace_no = table_name
self.where = where key_no = where[0]
if key_no != 0:
raise RuntimeError("DELETE can only be made by the primary key (#0)")
self.value_list = where[1:]
def pack(self):
buf = ctypes.create_string_buffer(PACKET_BUF_LEN)
(buf, offset) = pack_tuple(self.value_list, buf, DELETE_REQUEST_FIXED_LEN)
struct.pack_into("<L", buf, 0, self.namespace_no)
return buf[:offset]
def unpack(self, response):
(return_code,) = struct.unpack("<L", response[:4])
if return_code:
return format_error(return_code)
(result_code, row_count) = struct.unpack("<LL", response)
return "Delete OK, {0} row affected".format(row_count)
class StatementSelect(StatementPing): class StatementSelect(StatementPing):
reqeust_type = SELECT_REQUEST_TYPE reqeust_type = SELECT_REQUEST_TYPE
def __init__(self, table_name, where): def __init__(self, table_name, where):
self.namespace_no = int(object_no_re.sub("", table_name)) self.namespace_no = table_name
if where: if where:
(index, key) = where (self.index_no, key) = where
self.index_no = int(object_no_re.sub("", index))
self.key = [key] self.key = [key]
else: else:
self.index_no = 0 self.index_no = 0
......
...@@ -116,7 +116,7 @@ class Options: ...@@ -116,7 +116,7 @@ class Options:
help = """Run test suite in memory, using tmpfs or ramdisk. help = """Run test suite in memory, using tmpfs or ramdisk.
Is used only if vardir is not an absolute path. In that case Is used only if vardir is not an absolute path. In that case
vardir is sym-linked to /dev/shm/<vardir>. vardir is sym-linked to /dev/shm/<vardir>.
Linux only. Default: true""") Linux only. Default: false""")
self.check(parser) self.check(parser)
self.args = parser.parse_args() self.args = parser.parse_args()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment