aboutsummaryrefslogtreecommitdiff
path: root/databases
diff options
context:
space:
mode:
authorEitan Adler <eadler@FreeBSD.org>2012-01-02 15:41:28 +0000
committerEitan Adler <eadler@FreeBSD.org>2012-01-02 15:41:28 +0000
commit4ca7312e0e4f57600720f8e2c8ef343d5b03920a (patch)
tree26979d4606efbbd4c17c134fbb041aa7ebe768da /databases
parent2f2516d4df04f9ee2e7e9bbe7214ad2e476e6261 (diff)
Notes
Diffstat (limited to 'databases')
-rw-r--r--databases/Makefile1
-rw-r--r--databases/tarantool/Makefile43
-rw-r--r--databases/tarantool/distinfo2
-rw-r--r--databases/tarantool/files/patch-CMakeLists.txt11
-rw-r--r--databases/tarantool/files/patch-test_CMakeLists.txt9
-rw-r--r--databases/tarantool/files/patch-test_box_tarantool.cfg17
-rw-r--r--databases/tarantool/files/patch-test_lib_sql.g57
-rw-r--r--databases/tarantool/files/patch-test_lib_sql.py92
-rw-r--r--databases/tarantool/files/patch-test_lib_sql_ast.py45
-rw-r--r--databases/tarantool/files/patch-test_lib_tarantool_connection.py158
-rw-r--r--databases/tarantool/files/patch-test_lib_tarantool_silverbox_server.py266
-rw-r--r--databases/tarantool/files/patch-test_lib_test_suite.py210
-rw-r--r--databases/tarantool/files/patch-test_tarantool30
-rw-r--r--databases/tarantool/files/pkg-message.in10
-rw-r--r--databases/tarantool/files/tarantool.in29
-rw-r--r--databases/tarantool/pkg-descr5
-rw-r--r--databases/tarantool/pkg-plist21
17 files changed, 1006 insertions, 0 deletions
diff --git a/databases/Makefile b/databases/Makefile
index 95b6d8426798..9881c87086c9 100644
--- a/databases/Makefile
+++ b/databases/Makefile
@@ -803,6 +803,7 @@
SUBDIR += squirrel-sql
SUBDIR += sybtcl
SUBDIR += tablelog
+ SUBDIR += tarantool
SUBDIR += tcl-Mysql
SUBDIR += tdb
SUBDIR += tinycdb
diff --git a/databases/tarantool/Makefile b/databases/tarantool/Makefile
new file mode 100644
index 000000000000..05cc4e32203a
--- /dev/null
+++ b/databases/tarantool/Makefile
@@ -0,0 +1,43 @@
+# New ports collection makefile for: tarantool
+# Date created: 2011-11-25
+# Whom: Gvozdikov Veniamin <g.veniamin@googlemail.com>
+#
+# $FreeBSD$
+#
+
+PORTNAME= tarantool
+PORTVERSION= 1.3.5
+CATEGORIES= databases
+MASTER_SITES= http://launchpadlibrarian.net/71705094/
+DISTNAME= ${PORTNAME}-${PORTVERSION}-src
+
+MAINTAINER= g.veniamin@googlemail.com
+COMMENT= Tarantool, is a high performance key/value storage server
+
+LICENSE= BSD
+
+ONLY_FOR_ARCHS= i386
+
+USE_CMAKE= yes
+ARCH= i386
+USE_RC_SUBR= ${PORTNAME}
+SUB_FILES= pkg-message
+
+.include <bsd.port.pre.mk>
+
+post-patch:
+ @${REINPLACE_CMD} -e 's|%%DOCSDIR%%|${DOCSDIR}|g' \
+ ${WRKSRC}/CMakeLists.txt
+ @${REINPLACE_CMD} -e 's|%%ETCDIR%%|${ETCDIR}|g' \
+ ${WRKSRC}/test/CMakeLists.txt
+ @${RM} ${WRKSRC}/test/lib/server.py \
+ ${WRKSRC}/test/lib/silverbox.py \
+ ${WRKSRC}/test/lib/tarantool_admin.py \
+ ${WRKSRC}/test/lib/tarantool_feeder_server.py \
+ ${WRKSRC}/test/lib/tarantool_server.py \
+ ${WRKSRC}/test/lib/*.orig
+
+post-install:
+ @${CAT} ${PKGMESSAGE}
+
+.include <bsd.port.post.mk>
diff --git a/databases/tarantool/distinfo b/databases/tarantool/distinfo
new file mode 100644
index 000000000000..1eaf253b1299
--- /dev/null
+++ b/databases/tarantool/distinfo
@@ -0,0 +1,2 @@
+SHA256 (tarantool-1.3.5-src.tar.gz) = c78eb302eabac7b6ae04a8eadf8b2819e992d2913cdafe1a86222148982351ec
+SIZE (tarantool-1.3.5-src.tar.gz) = 829327
diff --git a/databases/tarantool/files/patch-CMakeLists.txt b/databases/tarantool/files/patch-CMakeLists.txt
new file mode 100644
index 000000000000..dfce258c4ca2
--- /dev/null
+++ b/databases/tarantool/files/patch-CMakeLists.txt
@@ -0,0 +1,11 @@
+--- CMakeLists.txt.orig 2011-11-25 15:33:08.997444924 +0000
++++ CMakeLists.txt 2011-11-25 15:33:30.428593855 +0000
+@@ -197,7 +197,7 @@
+ add_subdirectory(test)
+
+ install (FILES README LICENSE doc/silverbox-protocol.txt
+- DESTINATION doc)
++ DESTINATION %%DOCSDIR%%)
+
+ include (cmake/tarantool_cpack.cmake)
+ #
diff --git a/databases/tarantool/files/patch-test_CMakeLists.txt b/databases/tarantool/files/patch-test_CMakeLists.txt
new file mode 100644
index 000000000000..63afe8fbb2d6
--- /dev/null
+++ b/databases/tarantool/files/patch-test_CMakeLists.txt
@@ -0,0 +1,9 @@
+--- test/CMakeLists.txt.orig 2011-12-11 16:16:40.594230551 +0000
++++ test/CMakeLists.txt 2011-12-11 16:19:44.915010706 +0000
+@@ -9,5 +9,4 @@
+
+ install (PROGRAMS tarantool DESTINATION bin)
+ install (DIRECTORY lib DESTINATION bin)
+-install (FILES box/tarantool.cfg box/00000000000000000001.snap
+- DESTINATION bin)
++install (FILES box/tarantool.cfg DESTINATION %%ETCDIR%%)
diff --git a/databases/tarantool/files/patch-test_box_tarantool.cfg b/databases/tarantool/files/patch-test_box_tarantool.cfg
new file mode 100644
index 000000000000..35329ad4bd32
--- /dev/null
+++ b/databases/tarantool/files/patch-test_box_tarantool.cfg
@@ -0,0 +1,17 @@
+--- test/box/tarantool.cfg.orig 2011-12-13 01:02:02.069760259 +0000
++++ test/box/tarantool.cfg 2011-12-13 01:03:52.550055101 +0000
+@@ -1,11 +1,11 @@
+ slab_alloc_arena = 0.1
+
+-pid_file = "box.pid"
+-
++pid_file = "/var/run/tarantool.pid"
++work_dir = "/var/db/tarantool"
+
+ # Use -a not -a to work correctly on FreeBSD
+ #
+-logger="tee -a tarantool.log"
++logger="cat >> /var/log/tarantool.log"
+
+ primary_port = 33013
+ secondary_port = 33014
diff --git a/databases/tarantool/files/patch-test_lib_sql.g b/databases/tarantool/files/patch-test_lib_sql.g
new file mode 100644
index 000000000000..cbb53b447328
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_sql.g
@@ -0,0 +1,57 @@
+--- test/lib/sql.g.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/lib/sql.g 2011-12-13 00:41:37.729004939 +0000
+@@ -5,10 +5,6 @@
+
+ %%
+
+-# The grammar below solely covers the functionality provided by
+-# Tarantool binary protocol, from which follow all the
+-# limitations. For reference please see doc/box-protocol.txt.
+-
+ parser sql:
+
+ ignore: '\\s+'
+@@ -25,8 +21,6 @@
+ token WHERE: 'where'
+ token VALUES: 'values'
+ token SET: 'set'
+- token OR: 'or'
+- token LIMIT: 'limit'
+ token END: '\\s*$'
+
+ rule sql: (insert {{ stmt = insert }} |
+@@ -37,27 +31,19 @@
+
+ rule insert: INSERT [INTO] ident VALUES value_list
+ {{ return sql_ast.StatementInsert(ident, value_list) }}
+- rule update: UPDATE ident SET update_list opt_simple_where
+- {{ return sql_ast.StatementUpdate(ident, update_list, opt_simple_where) }}
+- rule delete: DELETE FROM ident opt_simple_where
+- {{ return sql_ast.StatementDelete(ident, opt_simple_where) }}
+- rule select: SELECT '\*' FROM ident opt_where opt_limit
+- {{ return sql_ast.StatementSelect(ident, opt_where, opt_limit) }}
++ rule update: UPDATE ident SET update_list opt_where
++ {{ return sql_ast.StatementUpdate(ident, update_list, opt_where) }}
++ rule delete: DELETE FROM ident opt_where
++ {{ return sql_ast.StatementDelete(ident, opt_where) }}
++ rule select: SELECT '\*' FROM ident opt_where
++ {{ return sql_ast.StatementSelect(ident, opt_where) }}
+ rule ping: PING
+ {{ return sql_ast.StatementPing() }}
+ rule predicate: ident '=' constant
+ {{ return (ident, constant) }}
+- rule opt_simple_where: {{ return None }}
++ rule opt_where: {{ return None }}
+ | WHERE predicate
+ {{ return predicate }}
+- rule opt_where: {{ return None }}
+- | WHERE disjunction
+- {{ return disjunction }}
+- rule disjunction: predicate {{ disjunction = [predicate] }}
+- [(OR predicate {{ disjunction.append(predicate) }})+]
+- {{ return disjunction }}
+- rule opt_limit: {{ return 0xffffffff }}
+- | LIMIT NUM {{ return int(NUM) }}
+ rule value_list: '\(' expr {{ value_list = [expr] }}
+ [("," expr {{ value_list.append(expr) }} )+]
+ '\)' {{ return value_list }}
diff --git a/databases/tarantool/files/patch-test_lib_sql.py b/databases/tarantool/files/patch-test_lib_sql.py
new file mode 100644
index 000000000000..30b56f1df11e
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_sql.py
@@ -0,0 +1,92 @@
+--- test/lib/sql.py.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/lib/sql.py 2011-12-13 00:23:04.673107891 +0000
+@@ -30,8 +30,6 @@
+ ('WHERE', re.compile('where')),
+ ('VALUES', re.compile('values')),
+ ('SET', re.compile('set')),
+- ('OR', re.compile('or')),
+- ('LIMIT', re.compile('limit')),
+ ('END', re.compile('\\s*$')),
+ ]
+ def __init__(self, str,*args,**kw):
+@@ -76,16 +74,16 @@
+ ident = self.ident(_context)
+ SET = self._scan('SET', context=_context)
+ update_list = self.update_list(_context)
+- opt_simple_where = self.opt_simple_where(_context)
+- return sql_ast.StatementUpdate(ident, update_list, opt_simple_where)
++ opt_where = self.opt_where(_context)
++ return sql_ast.StatementUpdate(ident, update_list, opt_where)
+
+ def delete(self, _parent=None):
+ _context = self.Context(_parent, self._scanner, 'delete', [])
+ DELETE = self._scan('DELETE', context=_context)
+ FROM = self._scan('FROM', context=_context)
+ ident = self.ident(_context)
+- opt_simple_where = self.opt_simple_where(_context)
+- return sql_ast.StatementDelete(ident, opt_simple_where)
++ opt_where = self.opt_where(_context)
++ return sql_ast.StatementDelete(ident, opt_where)
+
+ def select(self, _parent=None):
+ _context = self.Context(_parent, self._scanner, 'select', [])
+@@ -94,8 +92,7 @@
+ FROM = self._scan('FROM', context=_context)
+ ident = self.ident(_context)
+ opt_where = self.opt_where(_context)
+- opt_limit = self.opt_limit(_context)
+- return sql_ast.StatementSelect(ident, opt_where, opt_limit)
++ return sql_ast.StatementSelect(ident, opt_where)
+
+ def ping(self, _parent=None):
+ _context = self.Context(_parent, self._scanner, 'ping', [])
+@@ -109,8 +106,8 @@
+ constant = self.constant(_context)
+ return (ident, constant)
+
+- def opt_simple_where(self, _parent=None):
+- _context = self.Context(_parent, self._scanner, 'opt_simple_where', [])
++ def opt_where(self, _parent=None):
++ _context = self.Context(_parent, self._scanner, 'opt_where', [])
+ _token = self._peek('WHERE', 'END', context=_context)
+ if _token == 'END':
+ return None
+@@ -119,38 +116,6 @@
+ predicate = self.predicate(_context)
+ return predicate
+
+- def opt_where(self, _parent=None):
+- _context = self.Context(_parent, self._scanner, 'opt_where', [])
+- _token = self._peek('WHERE', 'LIMIT', 'END', context=_context)
+- if _token != 'WHERE':
+- return None
+- else: # == 'WHERE'
+- WHERE = self._scan('WHERE', context=_context)
+- disjunction = self.disjunction(_context)
+- return disjunction
+-
+- def disjunction(self, _parent=None):
+- _context = self.Context(_parent, self._scanner, 'disjunction', [])
+- predicate = self.predicate(_context)
+- disjunction = [predicate]
+- if self._peek('OR', 'LIMIT', 'END', context=_context) == 'OR':
+- while 1:
+- OR = self._scan('OR', context=_context)
+- predicate = self.predicate(_context)
+- disjunction.append(predicate)
+- if self._peek('OR', 'LIMIT', 'END', context=_context) != 'OR': break
+- return disjunction
+-
+- def opt_limit(self, _parent=None):
+- _context = self.Context(_parent, self._scanner, 'opt_limit', [])
+- _token = self._peek('LIMIT', 'END', context=_context)
+- if _token == 'END':
+- return 0xffffffff
+- else: # == 'LIMIT'
+- LIMIT = self._scan('LIMIT', context=_context)
+- NUM = self._scan('NUM', context=_context)
+- return int(NUM)
+-
+ def value_list(self, _parent=None):
+ _context = self.Context(_parent, self._scanner, 'value_list', [])
+ self._scan("'\\('", context=_context)
diff --git a/databases/tarantool/files/patch-test_lib_sql_ast.py b/databases/tarantool/files/patch-test_lib_sql_ast.py
new file mode 100644
index 000000000000..e7b5bec34c89
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_sql_ast.py
@@ -0,0 +1,45 @@
+--- test/lib/sql_ast.py.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/lib/sql_ast.py 2011-12-13 00:23:04.673107891 +0000
+@@ -242,22 +242,16 @@
+ class StatementSelect(StatementPing):
+ reqeust_type = SELECT_REQUEST_TYPE
+
+- def __init__(self, table_name, where, limit):
++ def __init__(self, table_name, where):
+ self.namespace_no = table_name
+- self.index_no = None
+- self.key_list = []
+- if not where:
+- self.index_no = 0
+- self.key_list = ["",]
++ if where:
++ (self.index_no, key) = where
++ self.key = [key]
+ else:
+- for (index_no, key) in where:
+- self.key_list.append(key)
+- if self.index_no == None:
+- self.index_no = index_no
+- elif self.index_no != index_no:
+- raise RuntimeError("All key values in a disjunction must refer to the same index")
++ self.index_no = 0
++ self.key = [""]
+ self.offset = 0
+- self.limit = limit
++ self.limit = 0xffffffff
+
+ def pack(self):
+ buf = ctypes.create_string_buffer(PACKET_BUF_LEN)
+@@ -266,10 +260,8 @@
+ self.index_no,
+ self.offset,
+ self.limit,
+- len(self.key_list))
+- offset = SELECT_REQUEST_FIXED_LEN
+- for key in self.key_list:
+- (buf, offset) = pack_tuple([key], buf, offset)
++ 1)
++ (buf, offset) = pack_tuple(self.key, buf, SELECT_REQUEST_FIXED_LEN)
+
+ return buf[:offset]
+
diff --git a/databases/tarantool/files/patch-test_lib_tarantool_connection.py b/databases/tarantool/files/patch-test_lib_tarantool_connection.py
new file mode 100644
index 000000000000..0df906fbde11
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_tarantool_connection.py
@@ -0,0 +1,158 @@
+--- test/lib/tarantool_connection.py.orig 1970-01-01 00:00:00.000000000 +0000
++++ test/lib/tarantool_connection.py 2011-12-13 00:23:04.673107891 +0000
+@@ -0,0 +1,155 @@
++__author__ = "Konstantin Osipov <kostja.osipov@gmail.com>"
++
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions
++# are met:
++# 1. Redistributions of source code must retain the above copyright
++# notice, this list of conditions and the following disclaimer.
++# 2. Redistributions in binary form must reproduce the above copyright
++# notice, this list of conditions and the following disclaimer in the
++# documentation and/or other materials provided with the distribution.
++#
++# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
++# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++# SUCH DAMAGE.
++
++import socket
++import sys
++import string
++import cStringIO
++import yaml
++import re
++import sql
++import struct
++import errno
++
++is_admin_re = re.compile("^\s*(show|save|exec|exit|reload|help)", re.I)
++
++class AdminConnection:
++ def __init__(self, host, port):
++ self.host = host
++ self.port = port
++ self.is_connected = False
++ self.stream = cStringIO.StringIO()
++
++ def connect(self):
++ self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
++ self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
++ self.socket.connect((self.host, self.port))
++ self.is_connected = True
++
++ def disconnect(self):
++ if self.is_connected:
++ self.socket.close()
++ self.is_connected = False
++
++ def reconnect(self):
++ self.disconnect()
++ self.connect()
++
++ def opt_reconnect(self):
++ """ On a socket which was disconnected, recv of 0 bytes immediately
++ returns with no data. On a socket which is alive, it returns EAGAIN.
++ Make use of this property and detect whether or not the socket is
++ dead. Reconnect a dead socket, do nothing if the socket is good."""
++ try:
++ if self.socket.recv(0, socket.MSG_DONTWAIT) == '':
++ self.reconnect()
++ except socket.error as e:
++ if e.errno == errno.EAGAIN:
++ pass
++ else:
++ self.reconnect()
++
++ def execute(self, command):
++ self.opt_reconnect()
++ return self.execute_no_reconnect(command)
++
++ def execute_no_reconnect(self, command):
++ self.socket.sendall(command)
++
++ bufsiz = 4096
++ res = ""
++
++ while True:
++ buf = self.socket.recv(bufsiz)
++ if not buf:
++ break
++ res = res + buf;
++ if (res.rfind("\r\n...\r\n") >= 0):
++ break
++
++ # validate yaml by parsing it
++ yaml.load(res)
++
++ return res
++
++ def write(self, fragment):
++ """This is to support print >> admin, "command" syntax.
++ For every print statement, write is invoked twice: one to
++ write the command itself, and another to write \n. We should
++ accumulate all writes until we receive \n. When we receive it,
++ we execute the command, and rewind the stream."""
++
++ newline_pos = fragment.rfind("\n")
++ while newline_pos >= 0:
++ self.stream.write(fragment[:newline_pos+1])
++ statement = self.stream.getvalue()
++ sys.stdout.write(statement)
++ sys.stdout.write(self.execute(statement))
++ fragment = fragment[newline_pos+1:]
++ newline_pos = fragment.rfind("\n")
++ self.stream.seek(0)
++ self.stream.truncate()
++
++ self.stream.write(fragment)
++
++ def __enter__(self):
++ self.connect()
++ return self
++
++ def __exit__(self, type, value, tb):
++ self.disconnect()
++
++class DataConnection(AdminConnection):
++
++ def recvall(self, length):
++ res = ""
++ while len(res) < length:
++ buf = self.socket.recv(length - len(res))
++ res = res + buf
++ return res
++
++ def execute_no_reconnect(self, command):
++ statement = sql.parse("sql", command)
++ if statement == None:
++ return "You have an error in your SQL syntax\n"
++
++ payload = statement.pack()
++ header = struct.pack("<lll", statement.reqeust_type, len(payload), 0)
++
++ self.socket.sendall(header)
++ if len(payload):
++ self.socket.sendall(payload)
++
++ IPROTO_HEADER_SIZE = 12
++
++ header = self.recvall(IPROTO_HEADER_SIZE)
++
++ response_len = struct.unpack("<lll", header)[1]
++
++ if response_len:
++ response = self.recvall(response_len)
++ else:
++ response = None
++
++ return statement.unpack(response) + "\n"
++
diff --git a/databases/tarantool/files/patch-test_lib_tarantool_silverbox_server.py b/databases/tarantool/files/patch-test_lib_tarantool_silverbox_server.py
new file mode 100644
index 000000000000..aa466bacc604
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_tarantool_silverbox_server.py
@@ -0,0 +1,266 @@
+--- test/lib/tarantool_silverbox_server.py.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/lib/tarantool_silverbox_server.py 2011-12-13 00:23:04.673107891 +0000
+@@ -1,35 +1,234 @@
++import os
++import stat
+ import shutil
+ import subprocess
+-import yaml
+-import ConfigParser
+-from tarantool_server import TarantoolServer, TarantoolConfigFile
+-from tarantool_admin import TarantoolAdmin
+-from silverbox import Silverbox
+-
+-class TarantoolSilverboxServer(TarantoolServer):
+- def __new__(cls, core="tarantool", module="silverbox"):
+- return TarantoolServer.__new__(cls)
+-
+- def __init__(self, core="tarantool", module="silverbox"):
+- TarantoolServer.__init__(self, core, module)
+-
+- def configure(self, config):
+- TarantoolServer.configure(self, config)
+- with open(self.config) as fp:
+- dummy_section_name = "tarantool"
+- config = ConfigParser.ConfigParser()
+- config.readfp(TarantoolConfigFile(fp, dummy_section_name))
+- self.primary_port = int(config.get(dummy_section_name, "primary_port"))
+- self.admin_port = int(config.get(dummy_section_name, "admin_port"))
+- self.port = self.admin_port
+- self.admin = TarantoolAdmin("localhost", self.admin_port)
+- self.sql = Silverbox("localhost", self.primary_port)
+-
+- def init(self):
+-# init storage
+- subprocess.check_call([self.binary, "--init_storage"],
+- cwd = self.vardir,
++import pexpect
++import sys
++import signal
++import time
++import socket
++import daemon
++import glob
++
++def wait_until_connected(host, port):
++ """Wait until the server is started and accepting connections"""
++ is_connected = False
++ while not is_connected:
++ try:
++ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
++ sock.connect((host, port))
++ is_connected = True
++ sock.close()
++ except socket.error as e:
++ time.sleep(0.001)
++
++
++def prepare_gdb(args):
++ """Prepare server startup arguments to run under gdb."""
++ if "TERM" in os.environ:
++ term = os.environ["TERM"]
++ else:
++ term = "xterm"
++
++ if term not in ["xterm", "rxvt", "urxvt", "gnome-terminal", "konsole"]:
++ raise RuntimeError("--gdb: unsupported terminal {0}".format(term))
++
++ args = [ term, "-e ", "gdb", "-ex", "break main", "-ex", "run"] + args
++ return args
++
++
++def prepare_valgrind(args, valgrind_opts):
++ "Prepare server startup arguments to run under valgrind."
++ args = ([ "valgrind", "--log-file=valgrind.log", "--quiet" ] +
++ valgrind_opts.split(" ") + args)
++ return args
++
++
++def check_tmpfs_exists():
++ return os.uname()[0] in 'Linux' and os.path.isdir("/dev/shm")
++
++def create_tmpfs_vardir(vardir):
++ os.mkdir(os.path.join("/dev/shm", vardir))
++ os.symlink(os.path.join("/dev/shm", vardir), vardir)
++
++class TarantoolSilverboxServer:
++ """Server represents a single server instance. Normally, the
++ program operates with only one server, but in future we may add
++ replication slaves. The server is started once at the beginning
++ of each suite, and stopped at the end."""
++
++ def __init__(self, args, suite_ini):
++ """Set server options: path to configuration file, pid file, exe, etc."""
++ self.args = args
++ self.suite_ini = suite_ini
++ self.path_to_pidfile = os.path.join(args.vardir, suite_ini["pidfile"])
++ self.path_to_exe = None
++ self.abspath_to_exe = None
++ self.is_started = False
++
++ def install(self, silent = False):
++ """Start server instance: check if the old one exists, kill it
++ if necessary, create necessary directories and files, start
++ the server. The server working directory is taken from 'vardir',
++ specified in the prgoram options.
++ Currently this is implemented for tarantool_silverbox only."""
++
++ vardir = self.args.vardir
++
++ if not silent:
++ print "Installing the server..."
++
++ if self.path_to_exe == None:
++ self.path_to_exe = self.find_exe()
++ self.abspath_to_exe = os.path.abspath(self.path_to_exe)
++
++ if not silent:
++ print " Found executable at " + self.path_to_exe + "."
++
++ if not silent:
++ print " Creating and populating working directory in " +\
++ vardir + "..."
++
++ if os.access(vardir, os.F_OK):
++ if not silent:
++ print " Found old vardir, deleting..."
++ self.kill_old_server()
++ for filename in (glob.glob(os.path.join(vardir, "*.snap")) +
++ glob.glob(os.path.join(vardir, "*.inprogress")) +
++ glob.glob(os.path.join(vardir, "*.xlog")) +
++ glob.glob(os.path.join(vardir, "*.cfg")) +
++ glob.glob(os.path.join(vardir, "*.log")) +
++ glob.glob(os.path.join(vardir, "*.core.*")) +
++ glob.glob(os.path.join(vardir, "core"))):
++ os.remove(filename)
++ else:
++ if (self.args.mem == True and check_tmpfs_exists() and
++ os.path.basename(vardir) == vardir):
++ create_tmpfs_vardir(vardir)
++ else:
++ os.mkdir(vardir)
++
++ shutil.copy(self.suite_ini["config"], self.args.vardir)
++
++ subprocess.check_call([self.abspath_to_exe, "--init_storage"],
++ cwd = self.args.vardir,
+ # catch stdout/stderr to not clutter output
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE)
+
++ p = subprocess.Popen([self.abspath_to_exe, "--version"],
++ cwd = self.args.vardir,
++ stdout = subprocess.PIPE)
++ version = p.stdout.read().rstrip()
++ p.wait()
++
++ if not silent:
++ print "Starting {0} {1}.".format(os.path.basename(self.abspath_to_exe),
++ version)
++
++ def start(self, silent = False):
++
++ if self.is_started:
++ if not silent:
++ print "The server is already started."
++ return
++
++ if not silent:
++ print "Starting the server..."
++
++ args = [self.abspath_to_exe]
++
++ if (self.args.start_and_exit and
++ not self.args.valgrind and not self.args.gdb):
++ args.append("--daemonize")
++ if self.args.gdb:
++ args = prepare_gdb(args)
++ elif self.args.valgrind:
++ args = prepare_valgrind(args, self.args.valgrind_opts)
++
++ if self.args.start_and_exit and self.args.valgrind:
++ pid = os.fork()
++ if pid > 0:
++ os.wait()
++ else:
++ with daemon.DaemonContext(working_directory = self.args.vardir):
++ os.execvp(args[0], args)
++ else:
++ self.server = pexpect.spawn(args[0], args[1:], cwd = self.args.vardir)
++ if self.args.start_and_exit:
++ self.server.wait()
++
++# wait until the server is connectedk
++ if self.args.gdb and self.args.start_and_exit:
++ time.sleep(1)
++ elif not self.args.start_and_exit and not self.args.gdb:
++ self.server.expect_exact("entering event loop")
++ else:
++ wait_until_connected(self.suite_ini["host"], self.suite_ini["port"])
++
++# Set is_started flag, to nicely support cleanup during an exception.
++ self.is_started = True
++
++
++ def stop(self, silent = False):
++ """Stop server instance. Do nothing if the server is not started,
++ to properly shut down the server in case of an exception during
++ start up."""
++ if self.is_started:
++ if not silent:
++ print "Stopping the server..."
++ if self.args.gdb:
++ self.kill_old_server(True)
++ self.server.terminate()
++ self.server.expect(pexpect.EOF)
++ self.is_started = False
++ elif not silent:
++ print "The server is not started."
++
++ def restart(self):
++ self.stop(True)
++ self.start(True)
++
++ def test_option(self, option_list_str):
++ args = [self.abspath_to_exe] + option_list_str.split()
++ print " ".join([os.path.basename(self.abspath_to_exe)] + args[1:])
++ output = subprocess.Popen(args,
++ cwd = self.args.vardir,
++ stdout = subprocess.PIPE,
++ stderr = subprocess.STDOUT).stdout.read()
++ print output
++
++
++ def find_exe(self):
++ """Locate server executable in the bindir. We just take
++ the first thing looking like an exe in there."""
++
++ print " Looking for server binary in {0}...".format(self.args.bindir)
++ if (os.access(self.args.bindir, os.F_OK) == False or
++ stat.S_ISDIR(os.stat(self.args.bindir).st_mode) == False):
++ raise RuntimeError("Directory " + self.args.bindir + " doesn't exist")
++
++ for f in os.listdir(self.args.bindir):
++ f = os.path.join(self.args.bindir, f)
++ st_mode = os.stat(f).st_mode
++ if stat.S_ISREG(st_mode) and st_mode & stat.S_IXUSR:
++ return f
++
++ raise RuntimeError("Can't find server executable in " + self.args.bindir)
++
++ def kill_old_server(self, silent = False):
++ """Kill old server instance if it exists."""
++ if os.access(self.path_to_pidfile, os.F_OK) == False:
++ return # Nothing to do
++ pid = 0
++ with open(self.path_to_pidfile) as f:
++ pid = int(f.read())
++ if not silent:
++ print " Found old server, pid {0}, killing...".format(pid)
++ try:
++ os.kill(pid, signal.SIGTERM)
++ while os.kill(pid, 0) != -1:
++ time.sleep(0.001)
++ except OSError:
++ pass
++
diff --git a/databases/tarantool/files/patch-test_lib_test_suite.py b/databases/tarantool/files/patch-test_lib_test_suite.py
new file mode 100644
index 000000000000..f406b086507d
--- /dev/null
+++ b/databases/tarantool/files/patch-test_lib_test_suite.py
@@ -0,0 +1,210 @@
+--- test/lib/test_suite.py.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/lib/test_suite.py 2011-12-13 00:23:04.673107891 +0000
+@@ -10,13 +10,21 @@
+ import filecmp
+ import shlex
+ import time
+-from server import Server
++from tarantool_silverbox_server import TarantoolSilverboxServer
++from tarantool_connection import AdminConnection, DataConnection
+ import tarantool_preprocessor
+ import re
+ import cStringIO
+ import string
+ import traceback
+
++class TestRunException(RuntimeError):
++ """A common exception to use across the program."""
++ def __init__(self, message):
++ self.message = message
++ def __str__(self):
++ return self.message
++
+ class FilteredStream:
+ """Helper class to filter .result file output"""
+ def __init__(self, filename):
+@@ -66,12 +74,12 @@
+ """Initialize test properties: path to test file, path to
+ temporary result file, path to the client program, test status."""
+ self.name = name
+- self.args = args
+- self.suite_ini = suite_ini
+ self.result = name.replace(".test", ".result")
+- self.tmp_result = os.path.join(self.args.vardir,
++ self.tmp_result = os.path.join(suite_ini["vardir"],
+ os.path.basename(self.result))
+ self.reject = name.replace(".test", ".reject")
++ self.args = args
++ self.suite_ini = suite_ini
+ self.is_executed = False
+ self.is_executed_ok = None
+ self.is_equal_result = None
+@@ -81,7 +89,7 @@
+ """Return true if this test was run successfully."""
+ return self.is_executed and self.is_executed_ok and self.is_equal_result
+
+- def run(self, server):
++ def run(self):
+ """Execute the test assuming it's a python program.
+ If the test aborts, print its output to stdout, and raise
+ an exception. Else, comprare result and reject files.
+@@ -91,10 +99,18 @@
+
+ diagnostics = "unknown"
+ save_stdout = sys.stdout
++ admin = AdminConnection(self.suite_ini["host"],
++ self.suite_ini["admin_port"])
++ sql = DataConnection(self.suite_ini["host"],
++ self.suite_ini["port"])
++ server = self.suite_ini["server"]
+ try:
++ admin.connect()
++ sql.connect()
+ sys.stdout = FilteredStream(self.tmp_result)
+- stdout_fileno = sys.stdout.stream.fileno()
+- execfile(self.name, dict(locals(), **server.__dict__))
++ server = self.suite_ini["server"]
++ vardir = self.suite_ini["vardir"]
++ execfile(self.name, globals(), locals())
+ self.is_executed_ok = True
+ except Exception as e:
+ traceback.print_exc(e)
+@@ -103,6 +119,8 @@
+ if sys.stdout and sys.stdout != save_stdout:
+ sys.stdout.close()
+ sys.stdout = save_stdout;
++ admin.disconnect()
++ sql.disconnect()
+
+ self.is_executed = True
+
+@@ -111,7 +129,7 @@
+
+ if self.args.valgrind:
+ self.is_valgrind_clean = \
+- check_valgrind_log(server.valgrind_log) == False
++ check_valgrind_log(self.suite_ini["valgrind_log"]) == False
+
+ if self.is_executed_ok and self.is_equal_result and self.is_valgrind_clean:
+ print "[ pass ]"
+@@ -133,12 +151,12 @@
+ where = ": wrong test output"
+ elif not self.is_valgrind_clean:
+ os.remove(self.reject)
+- self.print_diagnostics(server.valgrind_log,
++ self.print_diagnostics(self.suite_ini["valgrind_log"],
+ "Test failed! Last 10 lines of valgrind.log:")
+ where = ": there were warnings in valgrind.log"
+
+- if not self.args.is_force:
+- raise RuntimeError("Failed to run test " + self.name + where)
++ if not self.suite_ini["is_force"]:
++ raise TestRunException("Failed to run test " + self.name + where)
+
+
+ def print_diagnostics(self, logfile, message):
+@@ -167,6 +185,20 @@
+ for line in diff:
+ sys.stdout.write(line)
+
++class TarantoolConfigFile:
++ """ConfigParser can't read files without sections, work it around"""
++ def __init__(self, fp, section_name):
++ self.fp = fp
++ self.section_name = "[" + section_name + "]"
++ def readline(self):
++ if self.section_name:
++ section_name = self.section_name
++ self.section_name = None
++ return section_name
++ # tarantool.cfg puts string values in quote
++ return self.fp.readline().replace("\"", '')
++
++
+ class TestSuite:
+ """Each test suite contains a number of related tests files,
+ located in the same directory on disk. Each test file has
+@@ -186,13 +218,15 @@
+ self.args = args
+ self.tests = []
+ self.ini = {}
+-
+- self.ini["core"] = "tarantool"
+- self.ini["module"] = "silverbox"
++ self.ini["suite_path"] = suite_path
++ self.ini["host"] = "localhost"
++ self.ini["is_force"] = self.args.is_force
++ self.ini["vardir"] = args.vardir
++ self.ini["valgrind_log"] = os.path.join(args.vardir, "valgrind.log")
+
+ if os.access(suite_path, os.F_OK) == False:
+- raise RuntimeError("Suite \"" + suite_path +\
+- "\" doesn't exist")
++ raise TestRunException("Suite \"" + suite_path +\
++ "\" doesn't exist")
+
+ # read the suite config
+ config = ConfigParser.ConfigParser()
+@@ -203,6 +237,16 @@
+ self.ini["disabled"] = dict.fromkeys(self.ini["disabled"].split(" "))
+ else:
+ self.ini["disabled"] = dict()
++# import the necessary module for test suite client
++
++# now read the server config, we need some properties from it
++
++ with open(self.ini["config"]) as fp:
++ dummy_section_name = "tarantool_silverbox"
++ config.readfp(TarantoolConfigFile(fp, dummy_section_name))
++ self.ini["pidfile"] = config.get(dummy_section_name, "pid_file")
++ self.ini["admin_port"] = int(config.get(dummy_section_name, "admin_port"))
++ self.ini["port"] = int(config.get(dummy_section_name, "primary_port"))
+
+ print "Collecting tests in \"" + suite_path + "\": " +\
+ self.ini["description"] + "."
+@@ -216,17 +260,9 @@
+ def run_all(self):
+ """For each file in the test suite, run client program
+ assuming each file represents an individual test."""
+- try:
+- server = Server(self.ini["core"], self.ini["module"])
+- except Exception as e:
+- print e
+- raise RuntimeError("Unknown server: core = {0}, module = {1}".format(
+- self.ini["core"], self.ini["module"]))
+- server.deploy(self.ini["config"],
+- server.find_exe(self.args.builddir, silent=False),
+- self.args.vardir,
+- self.args.mem, self.args.start_and_exit, self.args.gdb, self.args.valgrind,
+- silent=False)
++ server = TarantoolSilverboxServer(self.args, self.ini)
++ server.install()
++ server.start()
+ if self.args.start_and_exit:
+ print " Start and exit requested, exiting..."
+ exit(0)
+@@ -247,7 +283,7 @@
+ if os.path.basename(test.name) in self.ini["disabled"]:
+ print "[ skip ]"
+ else:
+- test.run(server)
++ test.run()
+ if not test.passed():
+ failed_tests.append(test.name)
+
+@@ -255,11 +291,9 @@
+ if len(failed_tests):
+ print "Failed {0} tests: {1}.".format(len(failed_tests),
+ ", ".join(failed_tests))
+- server.stop(silent=False)
+- server.cleanup()
++ server.stop();
+
+- if self.args.valgrind and check_valgrind_log(server.valgrind_log):
++ if self.args.valgrind and check_valgrind_log(self.ini["valgrind_log"]):
+ print " Error! There were warnings/errors in valgrind log file:"
+- print_tail_n(server.valgrind_log, 20)
+- return 1
+- return len(failed_tests)
++ print_tail_n(self.ini["valgrind_log"], 20)
++
diff --git a/databases/tarantool/files/patch-test_tarantool b/databases/tarantool/files/patch-test_tarantool
new file mode 100644
index 000000000000..21db488e780c
--- /dev/null
+++ b/databases/tarantool/files/patch-test_tarantool
@@ -0,0 +1,30 @@
+--- test/tarantool.orig 2011-05-14 12:16:32.000000000 +0000
++++ test/tarantool 2011-12-13 01:12:12.696699437 +0000
+@@ -1,4 +1,4 @@
+-#! /usr/bin/python
++#! /usr/bin/env python
+ """A simplistic client for tarantool/silverbox: administrative
+ console and SQL client.
+
+@@ -32,8 +32,8 @@
+ import socket
+ import sys
+ import string
+-from lib.tarantool_admin import TarantoolAdmin, is_admin_re
+-from lib.box import Box
++from lib.tarantool_connection import AdminConnection, DataConnection, \
++ is_admin_re
+
+ class Options:
+ def __init__(self):
+@@ -94,8 +94,8 @@
+ def main():
+ init_readline_history()
+ options = Options()
+- admin_con = TarantoolAdmin(options.args.host, options.args.admin_port)
+- data_con = Box(options.args.host, options.args.port)
++ admin_con = AdminConnection(options.args.host, options.args.admin_port)
++ data_con = DataConnection(options.args.host, options.args.port)
+ try:
+ admin_con.connect()
+ data_con.connect()
diff --git a/databases/tarantool/files/pkg-message.in b/databases/tarantool/files/pkg-message.in
new file mode 100644
index 000000000000..53ca686eb6c7
--- /dev/null
+++ b/databases/tarantool/files/pkg-message.in
@@ -0,0 +1,10 @@
+#########################################################
+#
+#
+# After install you'll need init storage:
+#
+# %%PREFIX%%/bin/tarantool_silverbox --init-storage \
+# -c %%ETCDIR%%/tarantool.cfg
+#
+#
+#########################################################
diff --git a/databases/tarantool/files/tarantool.in b/databases/tarantool/files/tarantool.in
new file mode 100644
index 000000000000..65383b03acbf
--- /dev/null
+++ b/databases/tarantool/files/tarantool.in
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# $FreeBSD$
+
+# PROVIDE: tarantool
+# REQUIRE: LOGIN
+# KEYWORD: shutdown
+#
+# tarantool_enable="YES"
+# tarantool_config=""
+#
+
+. /etc/rc.subr
+
+name="tarantool"
+
+tarantool_enable=${tarantool_enable:-"NO"}
+tarantool_config=${tarantool_config:-"%%ETCDIR%%/$name.cfg"}
+
+rcvar=`set_rcvar`
+
+load_rc_config "$name"
+
+command="%%PREFIX%%/bin/tarantool_silverbox"
+command_args="--daemonize --config ${tarantool_config}"
+pidfile="/var/run/$name.pid"
+
+run_rc_command "$1"
+
diff --git a/databases/tarantool/pkg-descr b/databases/tarantool/pkg-descr
new file mode 100644
index 000000000000..720806dee134
--- /dev/null
+++ b/databases/tarantool/pkg-descr
@@ -0,0 +1,5 @@
+Tarantool/Box, or simply Tarantool, is a high performance key/value
+storage server. The code is available for free under the terms of
+BSD license. Supported platforms are GNU/Linux and FreeBSD.
+
+WWW: http://tarantool.org/
diff --git a/databases/tarantool/pkg-plist b/databases/tarantool/pkg-plist
new file mode 100644
index 000000000000..763fbd12d6f1
--- /dev/null
+++ b/databases/tarantool/pkg-plist
@@ -0,0 +1,21 @@
+bin/lib/__init__.py
+bin/lib/sql.g
+bin/lib/sql.py
+bin/lib/sql_ast.py
+bin/lib/tarantool_connection.py
+bin/lib/tarantool_preprocessor.py
+bin/lib/tarantool_silverbox_server.py
+bin/lib/test_suite.py
+bin/lib/yapps/__init__.py
+bin/lib/yapps/runtime.py
+bin/tarantool
+bin/tarantool_feeder
+bin/tarantool_silverbox
+%%DOCSDIR%%/LICENSE
+%%DOCSDIR%%/README
+%%DOCSDIR%%/silverbox-protocol.txt
+%%ETCDIR%%/tarantool.cfg
+@dirrm %%DOCSDIR%%
+@dirrm %%ETCDIR%%
+@dirrm bin/lib/yapps
+@dirrm bin/lib