diff --git a/doc/sphinx/_templates/layout.html b/doc/sphinx/_templates/layout.html index 70168830a6eb8721de1078cbf978c4d40a1269e5..b1b5d9ed714e7d11fe625b8b66ab63cc5047bee2 100644 --- a/doc/sphinx/_templates/layout.html +++ b/doc/sphinx/_templates/layout.html @@ -5,7 +5,7 @@ {% block header_scripts %} <link rel="stylesheet" href="/doc/_static/sphinx_design.css" /> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> - <script type="text/javascript" src="../_static/headers.js"></script> + <script type="text/javascript" src="/doc/_static/headers.js"></script> {{ super() }} {% endblock header_scripts %} diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index a7cf79d0601437496f34229d2afaa3a32372d31a..9f057c24bf46ecf4be668b7a62f931b98d976304 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -33,8 +33,10 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', - 'ext.filters' + 'ext.filters', + 'ext.lua' ] +primary_domain = 'lua' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/sphinx/ext/lua.py b/doc/sphinx/ext/lua.py new file mode 100644 index 0000000000000000000000000000000000000000..a38fb526ddf510eee3fb59f1f4e6a313477a0b9e --- /dev/null +++ b/doc/sphinx/ext/lua.py @@ -0,0 +1,847 @@ +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. + +# Previously Haka Domain (https://github.com/haka-security/haka) +# Written by Haka team + +import re + +from docutils import nodes +from docutils.parsers.rst import directives + +from sphinx import addnodes +from sphinx.roles import XRefRole +from sphinx.locale import l_, _ +from sphinx.domains import Domain, ObjType, Index +from sphinx.directives import ObjectDescription +from sphinx.util.nodes import make_refnode +from sphinx.util.compat import Directive +from sphinx.util.docfields import Field, GroupedField, TypedField +from sphinx.writers.html import HTMLTranslator + + +# Fix parameter display when using something different than '()' + +def new_visit_desc_parameterlist(self, node): + if hasattr(node, 'param_class'): + param_class = ' class="param_start_%s"' % node.param_class + else: + param_class = '' + + if hasattr(node, 'param_start'): + value = node.param_start + else: + value = '(' + + self.body.append('<big%s>%s</big>' % (param_class, value)) + self.first_param = 1 + self.optional_param_level = 0 + # How many required parameters are left. + self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) + self.param_separator = node.child_text_separator + +def new_depart_desc_parameterlist(self, node): + if hasattr(node, 'param_class'): + param_class = ' class="param_end_%s"' % node.param_class + else: + param_class = '' + + if hasattr(node, 'param_end'): + value = node.param_end + else: + value = ')' + + self.body.append('<big%s>%s</big>' % (param_class, value)) + +HTMLTranslator.visit_desc_parameterlist = new_visit_desc_parameterlist +HTMLTranslator.depart_desc_parameterlist = new_depart_desc_parameterlist + +def _desc_parameterlist(argstart, argend): + node = addnodes.desc_parameterlist() + node.param_start = argstart + node.param_end = argend + return node + +def _pseudo_parse_arglist(signode, argstart, arglist, argend): + """"Parse" a list of arguments separated by commas. + + Arguments can have "optional" annotations given by enclosing them in + brackets. Currently, this will split at any comma, even if it's inside a + string literal (e.g. default argument value). + """ + paramlist = _desc_parameterlist(argstart, argend) + stack = [paramlist] + for argument in arglist.split(','): + argument = argument.strip() + ends_open = ends_close = 0 + while argument.startswith('['): + stack.append(addnodes.desc_optional()) + stack[-2] += stack[-1] + argument = argument[1:].strip() + + while argument.startswith(']'): + stack.pop() + argument = argument[1:].strip() + + while argument.endswith(']'): + ends_close += 1 + argument = argument[:-1].strip() + + while argument.endswith('['): + ends_open += 1 + argument = argument[:-1].strip() + + if argument: + stack[-1] += addnodes.desc_parameter(argument, argument) + while ends_open: + stack.append(addnodes.desc_optional()) + stack[-2] += stack[-1] + ends_open -= 1 + + while ends_close: + stack.pop() + ends_close -= 1 + + if len(stack) != 1: + raise IndexError + + signode += paramlist + + +# Lua objects + +class LuaObject(ObjectDescription): + """ + Description of a general Lua object. + """ + option_spec = { + 'noindex': directives.flag, + 'annotation': directives.unchanged, + 'module': directives.unchanged, + 'objtype': directives.unchanged, + 'idxtype': directives.unchanged, + 'idxctx': directives.unchanged, + } + + lua_signature_re = re.compile( + r'''^ ([\w\.\:/\-]+[:.])? # class name(s) + ([<>\w/\-/]+) \s* # thing name + (?: ([({])(.*)([)}]))? # optional: arguments + (?:\s* -> \s* (.*))? # optional: return annotation + $ # and nothing more + ''', re.VERBOSE) + + def needs_arglist(self): + """May return true if an empty argument list is to be generated even if + the document contains none.""" + return False + + def needs_module(self): + """May return true if the module name should be displayed.""" + return self.context == None + + def needs_class(self): + """May return true if the module name should be displayed.""" + return self.context == None + + def build_objtype(self): + return self.options.get('objtype') or "%s" % (self.__class__.typename) + + def build_context(self, context): + if context: + return context[:-1], context[-1] + else: + return None, None + + def parse_signature(self, sig): + m = self.__class__.lua_signature_re.match(sig) + if m is None: + raise ValueError + + return m.groups() + + def build_parameters(self, signode): + if not self.arglist: + if self.needs_arglist(): + # for callables, add an empty parameter list + listnode = _desc_parameterlist(self.argstart, self.argend) + signode += listnode + else: + _pseudo_parse_arglist(signode, self.argstart, self.arglist, self.argend) + + def build_signode(self, signode): + if self.context: + context = self.context + self.contextsep + signode += addnodes.desc_addname(context, context) + + signode += addnodes.desc_name(self.name, self.name) + self.build_parameters(signode) + + if self.retann: + signode += addnodes.desc_returns(self.retann, self.retann) + + def handle_signature(self, sig, signode): + context, name, argstart, arglist, argend, retann = self.parse_signature(sig) + + self.context, self.contextsep = self.build_context(context) + self.module = self.options.get('module', self.env.temp_data.get('lua:module')) + self.clsname = self.options.get('class', self.env.temp_data.get('lua:class')) + self.objtype = self.build_objtype() + self.idxtype = self.options.get('idxtype') or self.options.get('objtype') + self.name = name + self.argstart = argstart + self.arglist = arglist + self.argend = argend + self.retann = retann + + add_module = True + fullname = name + + signode['module'] = self.module + signode['class'] = self.context + signode['fullname'] = fullname + + prefix = "%s " % (self.objtype) + signode += addnodes.desc_annotation(prefix, prefix) + + if self.clsname and self.needs_class(): + clsname = '%s:' % (self.clsname) + signode += addnodes.desc_addname(clsname, clsname) + elif self.module and self.needs_module(): + modname = '%s.' % (self.module) + signode += addnodes.desc_addname(modname, modname) + + self.build_signode(signode) + + anno = self.options.get('annotation') + if anno: + signode += addnodes.desc_annotation(' ' + anno, ' ' + anno) + + return {'fullname': fullname, 'context': self.context, + 'objtype': self.objtype, 'idxctx': self.options.get('idxctx') or ""} + + def add_target_and_index(self, names, sig, signode): + idxctx = self.options.get('idxctx') + + ids = ['lua-%s' % (self.__class__.typename)] + if idxctx: ids.append(idxctx) + if self.context: ids.append(self.context) + elif self.clsname and self.needs_class(): ids.append(self.clsname) + elif self.module and self.needs_module(): ids.append(self.module) + ids.append(names['fullname']) + + fullid = '.'.join(ids) +# self.state_machine.reporter.warning(fullid) + + fullname = [] + if self.clsname and self.needs_class(): fullname.append(self.clsname) + elif self.module and self.needs_module(): fullname.append(self.module) + if self.context: fullname.append(self.context) + fullname.append(names['fullname']) + fullname = '.'.join(fullname) + + # We need to escape the '<>' to avoid display issue in HTML + fullname = fullname.replace("<", "<") + fullname = fullname.replace(">", ">") + + if fullid not in self.state.document.ids: + objtype = self.objtype + if objtype == "": objtype = "%s" % (self.__class__.typename) + + signode['names'].append(fullname) + signode['ids'].append(fullid) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + objects = self.env.domaindata['lua']['objects'] + objects[fullname] = (self.env.docname, objtype, fullid) + + indextext = self.get_index_text(names) + if indextext: + self.indexnode['entries'].append(('single', indextext, + fullid, '')) + + def get_index_name(self, names): + return names['fullname'] + + def get_index_type(self): + return None + + def get_index_text(self, names): + ret = [] + + idxtype = self.idxtype or self.get_index_type() + if idxtype: ret.append(idxtype) + + if self.context: ret.append("in %s" % (self.context)) + if self.module and self.needs_module(): ret.append("in module %s" % (self.module)) + + return "%s (%s)" % (self.get_index_name(names), ' '.join(ret)) + + +class LuaClass(LuaObject): + doc_field_types = [ + Field('extend', label=l_('Extends'), has_arg=False, + names=('extend',)), + ] + + typename = l_("object") + + def get_index_type(self): + return "%s" % (self.__class__.typename) + + def before_content(self): + LuaObject.before_content(self) + if self.names: + self.env.temp_data['lua:class'] = self.names[0]['fullname'] + + def after_content(self): + LuaObject.after_content(self) + if self.names: + self.env.temp_data['lua:class'] = None + +class LuaFunction(LuaObject): + typename = l_("function") + + doc_field_types = [ + TypedField('parameter', label=l_('Parameters'), + names=('param', 'parameter', 'arg', 'argument'), + typerolename='obj', typenames=('paramtype', 'type', 'ptype')), + TypedField('returnvalues', label=l_('Returns'), + names=('return', 'ret'), typerolename='obj', + typenames=('rtype', 'type')), + Field('returnvalue', label=l_('Returns'), has_arg=False, + names=('returns')), + Field('returntype', label=l_('Return type'), has_arg=False, + names=('returntype',)), + ] + + def build_objtype(self): + return self.options.get('objtype') or "" + + def needs_arglist(self): + return True + + def get_index_name(self, names): + return '%s()' % (names['fullname']) + +class LuaMethod(LuaFunction): + option_spec = dict( + abstract=directives.flag, + **LuaObject.option_spec + ) + + def build_objtype(self): + if 'abstract' in self.options: + return "abstract %s" % (self.options.get('objtype') or "") + else: + return self.options.get('objtype') or "" + + + def build_context(self, context): + if context: + return "%s" % (context[:-1]), context[-1] + else: + return None, None + +class LuaData(LuaObject): + typename = l_("data") + + option_spec = dict( + readonly=directives.flag, + **LuaObject.option_spec + ) + + doc_field_types = [ + Field('type', label=l_('Type'), has_arg=False, + names=('type',)), + ] + + def build_objtype(self): + if 'readonly' in self.options: + return "const %s" % (self.options.get('objtype') or "") + else: + return self.options.get('objtype') or "" + +class LuaAttribute(LuaData): + typename = l_("attribute") + + lua_class_re = re.compile( + r'''([\w\./\-]+):([\w\./\-]+)? + ''', re.VERBOSE) + + def build_context(self, context): + if context: + m = LuaAttribute.lua_class_re.match(context[:-1]) + if m: + classname, subcontext = m.groups() + if subcontext: + return "<%s>.%s" % (classname, subcontext), '.' + else: + return "<%s>" % (classname), '.' + else: + return "<%s>" % (context[:-1]), '.' + else: + return None, None + +class LuaOperator(LuaObject): + typename = l_("operator") + + doc_field_types = [ + TypedField('parameter', label=l_('Parameters'), + names=('param', 'parameter', 'arg', 'argument'), + typerolename='obj', typenames=('paramtype', 'type', 'ptype')), + TypedField('returnvalues', label=l_('Returns'), + names=('return', 'ret'), typerolename='obj', + typenames=('rtype', 'type')), + Field('returnvalue', label=l_('Returns'), has_arg=False, + names=('returns')), + Field('returntype', label=l_('Return type'), has_arg=False, + names=('returntype',)), + ] + + lua_signature_unary_re = re.compile( + r'''^ ([\+\-\*/<>=\#]+) \s* # operator + ([\w\./\-]+) # class name(s) + (?:\s* -> \s* (.*))? # optional: return annotation + $ # and nothing more + ''', re.VERBOSE) + + lua_signature_binary_re = re.compile( + r'''^ ([\w\./\-]+) # class name(s) + \s* ([\+\-\*/<>=]+) \s* # operator + ([\w\./\-]+)? # class name(s) + (?:\s* -> \s* (.*))? # optional: return annotation + $ # and nothing more + ''', re.VERBOSE) + + lua_signature_index_re = re.compile( + r'''^ ([\w\./\-]+) # class name(s) + (\[)(.*)(\]) # arguments + (?:\s* -> \s* (.*))? # optional: return annotation + $ # and nothing more + ''', re.VERBOSE) + + lua_signature_newindex_re = re.compile( + r'''^ ([\w\./\-]+) # class name(s) + (\[)(.*)(\]) # arguments + \s* = \s* (.*) # return annotation + $ # and nothing more + ''', re.VERBOSE) + + lua_signature_convert_re = re.compile( + r'''^ ([\w/\-/]+) \s* # thing name + \( ([\w\./\-]+) \) # class name(s) + (?:\s* -> \s* (.*))? # optional: return annotation + $ # and nothing more + ''', re.VERBOSE) + + def parse_signature(self, sig): + m = LuaOperator.lua_signature_unary_re.match(sig) + if m: + name, context, retann = m.groups() + self.type = 'unary' + return context, name, None, None, None, retann + + m = LuaOperator.lua_signature_binary_re.match(sig) + if m: + context, name, _, retann = m.groups() + self.type = 'binary' + return context, name, None, None, None, retann + + m = LuaOperator.lua_signature_index_re.match(sig) + if m: + context, argstart, arglist, argend, retann = m.groups() + self.type = 'index' + return context, '[]', argstart, arglist, argend, retann + + m = LuaOperator.lua_signature_newindex_re.match(sig) + if m: + context, argstart, arglist, argend, retann = m.groups() + self.type = 'newindex' + return context, '[]', argstart, arglist, argend, retann + + m = LuaOperator.lua_signature_convert_re.match(sig) + if m: + name, context, retann = m.groups() + self.type = 'convert' + return context, name, None, None, None, retann + + raise ValueError + + def build_context(self, context): + if context: + return "<%s>" % (context), '' + else: + return None, None + + def build_objtype(self): + return self.options.get('objtype') or "" + + def build_signode(self, signode): + if self.type == 'unary': + signode += addnodes.desc_name(self.name, self.name) + + context = self.context + self.contextsep + signode += addnodes.desc_addname(context, context) + + if self.retann: + signode += addnodes.desc_returns(self.retann, self.retann) + + elif self.type == 'binary': + context = self.context + self.contextsep + name = " %s " % (self.name) + + signode += addnodes.desc_addname(context, context) + signode += addnodes.desc_name(name, name) + signode += addnodes.desc_addname(context, context) + + if self.retann: + signode += addnodes.desc_returns(self.retann, self.retann) + + elif self.type == 'index' or self.type == 'newindex': + context = self.context + self.contextsep + signode += addnodes.desc_addname(context, context) + + self.build_parameters(signode) + + if self.retann: + if self.type == 'newindex': + retann = " = %s" % (self.retann) + signode += addnodes.desc_type(retann, retann) + else: + signode += addnodes.desc_returns(self.retann, self.retann) + + elif self.type == 'convert': + context = self.context + self.contextsep + + signode += addnodes.desc_name(self.name, self.name) + + paramlist = _desc_parameterlist('(', ')') + paramlist.append(addnodes.desc_addname(context, context)) + signode.append(paramlist) + + if self.retann: + signode += addnodes.desc_returns(self.retann, self.retann) + + + +class LuaModule(Directive): + """ + Directive to mark description of a new module. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + 'platform': lambda x: x, + 'synopsis': lambda x: x, + 'noindex': directives.flag, + 'deprecated': directives.flag, + } + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + noindex = 'noindex' in self.options + env.temp_data['lua:module'] = modname + ret = [] + if not noindex: + env.domaindata['lua']['modules'][modname] = \ + (env.docname, self.options.get('synopsis', ''), + self.options.get('platform', ''), 'deprecated' in self.options) + + ids = "lua-module.%s" % (modname) + + # make a duplicate entry in 'objects' to facilitate searching for + # the module in LuaDomain.find_obj() + env.domaindata['lua']['objects'][modname] = (env.docname, 'module', ids) + targetnode = nodes.target('', '', ids=[ids], + ismod=True) + self.state.document.note_explicit_target(targetnode) + # the platform and synopsis aren't printed; in fact, they are only + # used in the modindex currently + ret.append(targetnode) + indextext = _('%s (module)') % modname + inode = addnodes.index(entries=[('single', indextext, + ids, '')]) + ret.append(inode) + return ret + +class LuaCurrentModule(Directive): + """ + This directive is just to tell Sphinx that we're documenting + stuff in module foo, but links to module foo won't lead here. + """ + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + if modname == 'None': + env.temp_data['lua:module'] = None + else: + env.temp_data['lua:module'] = modname + return [] + + +class LuaXRefRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + refnode['lua:module'] = env.temp_data.get('lua:module') + refnode['lua:class'] = env.temp_data.get('lua:class') + if not has_explicit_title: + title = title.lstrip('.') # only has a meaning for the target + target = target.lstrip('~') # only has a meaning for the title + # if the first character is a tilde, don't display the module/class + # parts of the contents + if title[0:1] == '~': + title = title[1:] + dot = max(title.rfind('.'), title.rfind(':')) + if dot != -1: + title = title[dot+1:] + # if the first character is a dot, search more specific namespaces first + # else search builtins first + if target[0:1] == '.': + target = target[1:] + refnode['refspecific'] = True + return title, target + +class LuaModuleIndex(Index): + """ + Index subclass to provide the Lua module index. + """ + name = 'modindex' + localname = l_('Lua Module Index') + shortname = l_('modules') + + def generate(self, docnames=None): + content = {} + # list of prefixes to ignore + ignores = self.domain.env.config['modindex_common_prefix'] + ignores = sorted(ignores, key=len, reverse=True) + # list of all modules, sorted by module name + modules = sorted(self.domain.data['modules'].items(), + key=lambda x: x[0].lower()) + # sort out collapsable modules + prev_modname = '' + num_toplevels = 0 + for modname, (docname, synopsis, platforms, deprecated) in modules: + if docnames and docname not in docnames: + continue + + for ignore in ignores: + if modname.startswith(ignore): + modname = modname[len(ignore):] + stripped = ignore + break + else: + stripped = '' + + # we stripped the whole module name? + if not modname: + modname, stripped = stripped, '' + + entries = content.setdefault(modname[0].lower(), []) + + package = modname.split('.')[0] + if package != modname: + # it's a submodule + if prev_modname == package: + # first submodule - make parent a group head + entries[-1][1] = 1 + elif not prev_modname.startswith(package): + # submodule without parent in list, add dummy entry + entries.append([stripped + package, 1, '', '', '', '', '']) + subtype = 2 + else: + num_toplevels += 1 + subtype = 0 + + qualifier = deprecated and _('Deprecated') or '' + entries.append([stripped + modname, subtype, docname, + 'module-' + stripped + modname, platforms, + qualifier, synopsis]) + prev_modname = modname + + # apply heuristics when to collapse modindex at page load: + # only collapse if number of toplevel modules is larger than + # number of submodules + collapse = len(modules) - num_toplevels < num_toplevels + + # sort by first letter + content = sorted(content.items()) + + return content, collapse + + +# Lua domain + +class LuaDomain(Domain): + """Lua language domain.""" + name = 'lua' + label = 'Lua' + object_types = { + 'class': ObjType(l_('class'), 'class', 'obj'), + 'attribute': ObjType(l_('attribute'), 'data', 'obj'), + 'function': ObjType(l_('function'), 'func', 'obj'), + 'method': ObjType(l_('method'), 'func', 'obj'), + 'operator': ObjType(l_('operator'), 'func', 'obj'), + 'module': ObjType(l_('module'), 'mod', 'obj'), + 'data': ObjType(l_('data'), 'data', 'obj'), + } + + directives = { + 'class': LuaClass, + 'function': LuaFunction, + 'method': LuaMethod, + 'operator': LuaOperator, + 'data': LuaData, + 'attribute': LuaAttribute, + 'module': LuaModule, + 'currentmodule': LuaCurrentModule, + } + roles = { + 'data': LuaXRefRole(), + 'func': LuaXRefRole(fix_parens=True), + 'class': LuaXRefRole(), + 'mod': LuaXRefRole(), + } + initial_data = { + 'objects': {}, # fullname -> docname, objtype + 'modules': {}, # modname -> docname, synopsis, platform, deprecated + 'inheritance': {}, # class -> [ derived ] + } + indices = [ + LuaModuleIndex, + ] + + def clear_doc(self, docname): + for fullname, (fn, _, _) in list(self.data['objects'].items()): + if fn == docname: + del self.data['objects'][fullname] + for modname, (fn, _, _, _) in list(self.data['modules'].items()): + if fn == docname: + del self.data['modules'][modname] + + def find_obj(self, env, modname, classname, name, type, searchmode=0): + # skip parens + if name[-2:] == '()': + name = name[:-2] + + if not name: + return [] + + name = name.replace("<", "<") + name = name.replace(">", ">") + + objects = self.data['objects'] + matches = [] + + newname = None + if searchmode == 1: + objtypes = self.objtypes_for_role(type) + if modname and classname: + fullname = modname + '.' + classname + '.' + name + if fullname in objects and objects[fullname][1] in objtypes: + newname = fullname + if not newname: + if modname and modname + '.' + name in objects and \ + objects[modname + '.' + name][1] in objtypes: + newname = modname + '.' + name + elif name in objects and objects[name][1] in objtypes: + newname = name + else: + # "fuzzy" searching mode + searchname = '.' + name + matches = [(oname, objects[oname]) for oname in objects + if oname.endswith(searchname) + and objects[oname][1] in objtypes] + else: + # NOTE: searching for exact match, object type is not considered + if name in objects: + newname = name + elif type == 'mod': + # only exact matches allowed for modules + return [] + elif classname and classname + '.' + name in objects: + newname = classname + '.' + name + elif modname and modname + '.' + name in objects: + newname = modname + '.' + name + elif modname and classname and \ + modname + '.' + classname + '.' + name in objects: + newname = modname + '.' + classname + '.' + name + # special case: builtin exceptions have module "exceptions" set + elif type == 'exc' and '.' not in name and \ + 'exceptions.' + name in objects: + newname = 'exceptions.' + name + # special case: object methods + elif type in ('func', 'meth') and '.' not in name and \ + 'object.' + name in objects: + newname = 'object.' + name + if newname is not None: + matches.append((newname, objects[newname])) + return matches + + def resolve_xref(self, env, fromdocname, builder, + type, target, node, contnode): + + modname = node.get('lua:module') + clsname = node.get('lua:class') + searchmode = node.hasattr('refspecific') and 1 or 0 + matches = self.find_obj(env, modname, clsname, target, + type, searchmode) + + if not matches: + # If type is 'obj', we don't want to display any WARNING. + # Otherwise, we have too many due to unknown type used + # as parameter type in function description (number, string...). + if type != "obj": + env.warn_node( + 'no target found for cross-reference ' + '%r' % (target), node) + return None + elif len(matches) > 1: + env.warn_node( + 'more than one target found for cross-reference ' + '%r: %s' % (target, ', '.join(match[0] for match in matches)), + node) + name, obj = matches[0] + + return make_refnode(builder, fromdocname, obj[0], obj[2], + contnode, name) + + def get_objects(self): + for modname, info in self.data['modules'].items(): + yield (modname, modname, 'module', info[0], 'module-' + modname, 0) + for refname, (docname, type, id) in self.data['objects'].items(): + yield (refname, refname, type, docname, id, 1) + + +def setup(app): + app.add_domain(LuaDomain) diff --git a/doc/sphinx/reference/box/box_index.rst b/doc/sphinx/reference/box/box_index.rst new file mode 100644 index 0000000000000000000000000000000000000000..8efeecd0c6a313358e282171201220f981838b77 --- /dev/null +++ b/doc/sphinx/reference/box/box_index.rst @@ -0,0 +1,638 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `box.index` +------------------------------------------------------------------------------- + +The ``box.index`` package provides read-only access for index definitions and +index keys. Indexes are contained in ``box.space.space-name.index`` array within +each space object. They provide an API for ordered iteration over tuples. This +API is a direct binding to corresponding methods of index objects of type +``box.index`` in the storage engine. + +.. module:: box.index + +.. data:: box.space.space-name.index.index-name.unique + + true if the index is unique. + + :rtype: boolean + +.. data:: box.space.space-name.index.index-name.type + + Index type, 'TREE' or 'HASH' or 'BITSET' or 'RTREE'. + + :rtype: string + +.. data:: box.space.space-name.index.index-name.parts + + An array describing index key fields. + + :rtype: table + + .. code-block:: lua + + tarantool> box.space.tester.index.primary + --- + unique: true + parts: + 0: + type: NUM + fieldno: 1 + id: 0 + space_id: 513 + name: primary + type: TREE + ... + +.. function:: box.space.space-name.index[.index-name]:pairs(bitset-value | field-value..., iterator-type) + + This method provides iteration support within an index. Parameter type is + used to identify the semantics of iteration. Different index types support + different iterators. The remaining arguments of the function are varying + and depend on the iteration type. For example, a TREE index maintains a + strict order of keys and can return all tuples in ascending or descending + order, starting from the specified key. Other index types, however, do not + support ordering. + + To understand consistency of tuples returned by an iterator, it's essential + to know the principles of the Tarantool transaction processing subsystem. An + iterator in Tarantool does not own a consistent read view. Instead, each + procedure is granted exclusive access to all tuples and spaces until it + encounters a "context switch": by causing a write to disk, network, or by an + explicit call to :func:`fiber.yield`. When the execution flow returns + to the yielded procedure, the data set could have changed significantly. + Iteration, resumed after a yield point, does not preserve the read view, + but continues with the new content of the database. + + :param type: iteration strategy as defined in tables below + :return: this method returns an iterator closure, i.e. a function which can + be used to get the next value on each invocation + :rtype: function, tuple + + :except: Selected iteration type is not supported in the subject index type, + or supplied parameters do not match iteration type. + + Complexity Factors: Index size, Index type, Number of tuples accessed. + + .. container:: table + + **TREE iterator types** + + +---------------+-----------+---------------------------------------------+ + | Type | Arguments | Description | + +===============+===========+=============================================+ + | box.index.ALL | none | Iterate over all tuples in an index. Tuples | + | or 'ALL' | | are returned in ascending order of the key. | + +---------------+-----------+---------------------------------------------+ + | box.index.EQ | field | Equality iterator: iterate over all tuples | + | or 'EQ' | values | where field values = key values. Parts of a | + | | | multi-part key need to be separated by | + | | | commas. | + | | | | + | | | If the number of field values is less than | + | | | the number of parts of a multi-part key, | + | | | the missing field values are considered to | + | | | be matching. | + | | | | + | | | If there are multiple matches, then tuples | + | | | are returned in ascending order by key. | + +---------------+-----------+---------------------------------------------+ + | box.index.GT | field | Keys match if key values are greater than | + | or 'GT' | values | field values. If the number of field values | + | | | is less than the number of parts of a | + | | | multi-part key, the missing field values | + | | | are considered to be matching. If the field | + | | | value is ``nil``, iteration starts from the | + | | | smallest key in the index. Tuples are | + | | | returned in ascending order by key. | + +---------------+-----------+---------------------------------------------+ + | box.index.REQ | field | Reverse equality iterator. Matching is | + | or 'REQ' | values | determined in the same way as for | + | | | ``box.index.EQ``, but, if there are multiple| + | | | matches, then tuples are returned in | + | | | descending order by key, | + +---------------+-----------+---------------------------------------------+ + | box.index.GE | field | Keys match if key values are greater than | + | or 'GE' | values | or equal to field values. Tuples are | + | | | returned in ascending order by key. If the | + | | | field value is ``nil``, iteration starts | + | | | from the first key in the index. | + +---------------+-----------+---------------------------------------------+ + | box.index.LT | field | Keys match if key values are less than | + | or 'LT' | values | field values. Tuples are returned in | + | | | descending order by key. If the field value | + | | | is ``nil``, iteration starts from the last | + | | | key in the index. | + +---------------+-----------+---------------------------------------------+ + | box.index.LE | field | Keys match if key values are less than or | + | or 'LE' | values | equal to field values. Tuples are returned | + | | | in descending order by key. If the field | + | | | value is ``nil``, iteration starts from | + | | | the last key in the index. | + +---------------+-----------+---------------------------------------------+ + + **HASH iterator types** + + +---------------+-----------+---------------------------------------------+ + | Type | Arguments | Description | + +===============+===========+=============================================+ + | box.index.ALL | none | Iterate over all tuples in an index. Tuples | + | or 'ALL' | | are returned in ascending order of the key. | + +---------------+-----------+---------------------------------------------+ + | box.index.EQ | field | Equality iterator: iterate over all tuples | + | or 'EQ' | values | matching the key. Parts of a multi-part | + | | | key need to be separated by commas. | + | | | | + | | | A HASH index only supports exact match: | + | | | all parts of a key participating in the | + | | | index must be provided. | + | | | | + | | | HASH indexes are always unique. | + +---------------+-----------+---------------------------------------------+ + | box.index.GT | field | Keys match if hashed key values are greater | + | or 'GT' | values | than hashed field values. If the number of | + | | | field values is less than the number of | + | | | parts of a multi-part key, the result is an | + | | | error. Tuples are returned in ascending | + | | | order by hashed key, so the order will | + | | | appear to be random. Provided that the | + | | | space is not being updated, the 'GT' | + | | | iterator can be used to retrieve all | + | | | tuples piece by piece, by supplying the | + | | | last returned value from the previous | + | | | range as the start field value for an | + | | | iterator over the next range. | + +---------------+-----------+---------------------------------------------+ + + **BITSET iterator types** + + +----------------------------+-----------+---------------------------------------------+ + | Type | Arguments | Description | + +============================+===========+=============================================+ + | box.index.ALL | none | Iterate over all tuples in an index. Tuples | + | or 'ALL' | | are returned in ascending order of the | + | | | key's bitset, and so will appear to be | + | | | unordered. | + +----------------------------+-----------+---------------------------------------------+ + | box.index.EQ | field | Equality iterator: iterate over all tuples | + | or 'EQ' | values | matching the field values. If there are | + | | | multiple field values, they need to be | + | | | separated by commas. | + | | | | + | | | BITSET indexes are always unique. | + +----------------------------+-----------+---------------------------------------------+ + | box.index.BITS_ALL_SET | field | Keys match if all of the bits specified in | + | | values | 'bit mask' are set. | + +----------------------------+-----------+---------------------------------------------+ + | box.index.BITS_ANY_SET | field | Keys match if any of the bits specified in | + | | values | 'bit mask' is set. | + +----------------------------+-----------+---------------------------------------------+ + | box.index.BITS_ALL_NOT_SET | field | Keys match if none of the bits specified in | + | | values | 'bit mask' is set. | + +----------------------------+-----------+---------------------------------------------+ + + .. _rtree-iterator: + + **RTREE iterator types** + + +--------------------+-----------+---------------------------------------------+ + | Type | Arguments | Description | + +====================+===========+=============================================+ + | box.index.ALL | none | All keys match. Tuples are returned in | + | or 'ALL' | | ascending order of the primary key. | + +--------------------+-----------+---------------------------------------------+ + | box.index.EQ | field | Keys match if the rectangle defined by the | + | or 'EQ' | values | field values is the same as the rectangle | + | | | defined by the key -- where "key" means | + | | | "the key in the RTREE index" and | + | | | "rectangle" means "rectangle as explained | + | | | in section RTREE_. | + +--------------------+-----------+---------------------------------------------+ + | box.index.GT | field | Keys match if all points of the rectangle | + | or 'GT' | values | defined by the field values are within the | + | | | rectangle defined by the key. | + +--------------------+-----------+---------------------------------------------+ + | box.index.GE | field | Keys match if all points of the rectangle | + | or 'GE' | values | defined by the field values are within, or | + | | | at the side of, the rectangle defined by | + | | | the key. | + +--------------------+-----------+---------------------------------------------+ + | box.index.LT | field | Keys match if all points of the rectangle | + | or 'LT' | values | defined by the key are within the rectangle | + | | | defined by the field values. | + +--------------------+-----------+---------------------------------------------+ + | box.index.LE | field | Keys match if all points of the rectangle | + | or 'LE' | values | defined by the key are within, or at the | + | | | side of, the rectangle defined by the field | + | | | values. | + +--------------------+-----------+---------------------------------------------+ + | box.index.OVERLAPS | field | Keys match if all points of the rectangle | + | or 'OVERLAPS' | values | defined by the key are within, or at the | + | | | side of, the rectangle defined by the field | + | | | values. | + +--------------------+-----------+---------------------------------------------+ + | box.index.NEIGHBOR | field | Keys match if all points of the rectangle | + | or 'NEIGHBOR' | values | defined by the key are within, or at the | + | | | side of, the rectangle defined by the field | + | | | values. | + +--------------------+-----------+---------------------------------------------+ + + .. code-block:: lua + + tarantool> s = box.schema.space.create('space17') + --- + ... + tarantool> s:create_index('primary', {parts = {1, 'STR', 2, 'STR'}}) + --- + ... + tarantool> s:insert{'C', 'C'} + --- + - ['C', 'C'] + ... + tarantool> s:insert{'B', 'A'} + --- + - ['B', 'A'] + ... + tarantool> s:insert{'C', '!'} + --- + - ['C', '!'] + ... + tarantool> s:insert{'A', 'C'} + --- + - ['A', 'C'] + ... + tarantool> console = require('console'); console.delimiter('!') + --- + ... + tarantool> function example() + > for _, tuple in + > s.index.primary:pairs(nil, {iterator = box.index.ALL}) do + > print(tuple) + > end + > end! + --- + ... + tarantool> console.delimiter('')! + --- + ... + tarantool> example() + ['A', 'C'] + ['B', 'A'] + ['C', '!'] + ['C', 'C'] + --- + ... + tarantool> s:drop() + --- + ... + +.. function:: box.space.space-name[.index.index-name]:select({[field-value [, field-value ...]]}, {[option [, option ...]]}) + + This is is an alternative to box.space...select() which goes via a + particular index and can make use of additional parameters that specify the + iterator type, and the limit (that is, the maximum number of tuples to + return) and the offset (that is, which tuple to start with in the list). + + :param lua-value field-value(s): values to be matched against the index key. + :param lua-value option(s): any or all of iterator=iterator-type + limit=maximum-number-of-tuples, + offset=start-tuple-number. + + :return: the tuple or tuples that match the field values. + :rtype: tuple set as a Lua table + + .. code-block:: lua + + -- Create a space named tester. + -- Create a unique index 'primary', which won't be needed for this example. + -- Create a non-unique index 'secondary' with an index on the second field. + -- Insert three tuples, values in field[2] equal to 'X', 'Y', and 'Z'. + -- Select all tuples where the secondary index keys are greater than 'X'. + box.schema.space.create('tester') + box.space.tester:create_index('primary', {parts = {1, 'NUM' }}) + box.space.tester:create_index('secondary', {type = 'tree', unique = false, parts = {2, 'STR'}}) + box.space.tester:insert{1,'X','Row with field[2]=X'} + box.space.tester:insert{2,'Y','Row with field[2]=Y'} + box.space.tester:insert{3,'Z','Row with field[2]=Z'} + box.space.tester.index.secondary:select({'X'}, {iterator = 'GT', limit = 1000}) + + The result will be a table of tuple and will look like this: + + .. code-block:: yaml + + --- + - - [2, 'Y', 'Row with field[2]=Y'] + - [3, 'Z', 'Row with field[2]=Z'] + ... + + .. NOTE:: + + [.index.index-name] is optional. If it is omitted, then the assumed + index is the first (primary-key) index. Therefore, for the example + above, ``box.space.tester:select({1}, {iterator = 'GT'})`` would have + returned the same two rows, via the 'primary' index. + + .. NOTE:: + + ``iterator = iterator type`` is optional. If it is omitted, then + ``iterator = 'EQ'`` is assumed. + + .. NOTE:: + + ``field-value [, field-value ...]`` is optional. If it is omitted, + then every key in the index is considered to be a match, regardless of + iterator type. Therefore, for the example above, + ``box.space.tester:select{}`` will select every tuple in the tester + space via the first (primary-key) index. + + .. NOTE:: + + ``box.space.space-name.index.index-name:select(...)[1]``. can be + replaced by ``box.space.space-name.index.index-name:get(...)``. + That is, get can be used as a convenient shorthand to get the first + tuple in the tuple set that would be returned by select. However, + if there is more than one tuple in the tuple set, then get returns + an error. + +.. function:: box.space.space-name.index.index-name:min([key-value]) + + Find the minimum value in the specified index. + + :return: the tuple for the first key in the index. If optional + ``key-value`` is supplied, returns the first key which + is greater than or equal to ``key-value``. + :rtype: tuple + :except: index is not of type 'TREE'. + + Complexity Factors: Index size, Index type. + + .. code-block:: lua + + tarantool> box.space.tester.index.primary:min() + --- + - ['Alpha!', 55, 'This is the first tuple!'] + ... + +.. function:: box.space.space-name.index.index-name:max([key-value]) + + Find the maximum value in the specified index. + + :return: the tuple for the last key in the index. If optional ``key-value`` + is supplied, returns the last key which is less than or equal to + ``key-value``. + :rtype: tuple + :except: index is not of type 'TREE'. + + Complexity Factors: Index size, Index type. + + .. code-block:: lua + + tarantool> box.space.tester.index.primary:max() + --- + - ['Gamma!', 55, 'This is the third tuple!'] + ... + + +.. function:: box.space.space-name.index.index-name:random(random-value) + + Find a random value in the specified index. This method is useful when it's + important to get insight into data distribution in an index without having + to iterate over the entire data set. + + :param integer random-value: an arbitrary non-negative integer. + :return: the tuple for the random key in the index. + :rtype: tuple + + Complexity Factors: Index size, Index type. + + .. code-block:: lua + + tarantool> box.space.tester.index.secondary:random(1) + --- + - ['Beta!', 66, 'This is the second tuple!'] + ... + +.. function:: box.space.space-name.index.index-name:count(key-value, options) + + Iterate over an index, counting the number of + tuples which equal the provided search criteria. + + :param lua-value key-value: the value which must match the key(s) in the + specified index. The type may be a list of + field-values, or a tuple containing only + the field-values. + + :return: the number of matching index keys. The ``index`` function + is only applicable for the memtx storage engine. + :rtype: number + + .. code-block:: lua + + tarantool> box.space.tester.index.primary:count(999) + --- + - 0 + ... + tarantool> box.space.tester.index.primary:count('Alpha!', { iterator = 'LE' }) + --- + - 1 + ... + +.. function:: box.space.space-name.index.index-name:alter{options} + + Alter an index. + + :param table options: options list for create_index(). + :return: nil + + :except: If index-name doesn't exist. + :except: The first index cannot be changed to {unique = false}. + :except: The alter function is only applicable for the memtx storage engine. + + .. code-block:: lua + + tarantool> box.space.space55.index.primary:alter({type = 'HASH'}) + --- + ... + +.. function:: space-name.index.index-name:drop() + + Drop an index. Dropping a primary-key index has + a side effect: all tuples are deleted. + + :return: nil. + :except: If index-name doesn't exist. + + .. code-block:: lua + + tarantool> box.space.space55.index.primary:drop() + --- + ... + +.. function:: space-name.index.index-name:rename(index-name) + + Rename an index. + + :param string index-name: new name for index. + :return: nil + :except: If index-name doesn't exist. + + .. code-block:: lua + + tarantool> box.space.space55.index.primary:rename('secondary') + --- + ... + + Complexity Factors: Index size, Index type, Number of tuples accessed. + + +=========================================================== + Example +=========================================================== + +This example will work with the sandbox configuration described in the preface. +That is, there is a space named tester with a numeric primary key. The example +function will: + +* select a tuple whose key value is 1000; +* return an error if the tuple already exists and already has 3 fields; +* Insert or replace the tuple with: + * field[1] = 1000 + * field[2] = a uuid + * field[3] = number of seconds since 1970-01-01; +* Get field[3] from what was replaced; +* Format the value from field[3] as yyyy-mm-dd hh:mm:ss.ffff; +* Return the formatted value. + +The function uses Tarantool box functions box.space...select, +box.space...replace, fiber.time, uuid.str(). The function uses +Lua functions os.date() and string.sub(). + +.. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local a, b, c, table_of_selected_tuples, replaced_tuple, time_field + local formatted_time_field + local fiber = require('fiber') + table_of_selected_tuples = box.space.tester:select{1000} + if table_of_selected_tuples ~= nil then + if table_of_selected_tuples[1] ~= nil then + if #table_of_selected_tuples[1] == 3 then + box.error({code=1, reason='This tuple already has 3 fields'}) + end + end + end + replaced_tuple = box.space.tester:replace + {1000, require('uuid').str(), tostring(fiber.time())} + time_field = tonumber(replaced_tuple[3]) + formatted_time_field = os.date("%Y-%m-%d %H:%M:%S", time_field) + c = time_field % 1 + d = string.sub(c, 3, 6) + formatted_time_field = formatted_time_field .. '.' .. d + return formatted_time_field + end! + console.delimiter('')! + +... And here is what happens when one invokes the function: + +.. code-block:: lua + + tarantool> box.space.tester:delete(1000) + --- + - 1000: {'264ee2da03634f24972be76c43808254', '1391037015.6809'} + ... + tarantool> example(1000) + --- + - 2014-01-29 16:11:51.1582 + ... + tarantool> example(1000) + --- + - error: 'This tuple already has 3 fields' + ... + +.. _RTREE: + +=========================================================== + Package `box.index` for RTREE +=========================================================== + +The :mod:`box.index` package may be used for spatial searches if the index type +is RTREE. There are operations for searching ``rectangles``. Rectangles are +described according to their X-axis (horizontal axis) and Y-axis (vertical axis) +coordinates in a grid of arbitrary size. Here is a picture of four rectangles on +a grid with 11 horizontal points and 11 vertical points: + +:: + + X AXIS + 1 2 3 4 5 6 7 8 9 10 11 + 1 + 2 #-------+ <-Rectangle#1 + Y AXIS 3 | | + 4 +-------# + 5 #-----------------------+ <-Rectangle#2 + 6 | | + 7 | #---+ | <-Rectangle#3 + 8 | | | | + 9 | +---# | + 10 +-----------------------# + 11 # <-Rectangle#4 + +The rectangles are defined according to this scheme: {X-axis coordinate of top +left, Y-axis coordinate of top left, X-axis coordinate of bottom right, Y-axis +coordinate of bottom right} -- or more succinctly: {x1,y1,x2,y2}. So in the +picture ... Rectangle#1 starts at position 1 on the X axis and position 2 on +the Y axis, and ends at position 3 on the X axis and position 4 on the Y axis, +so its coordinates are {1,2,3,4}. Rectangle#2's coordinates are {3,5,9,10}. +Rectangle#3's coordinates are {4,7,5,9}. And finally Rectangle#4's coordinates +are {10,11,10,11}. Rectangle#4 is actually a "point" since it has zero width +and zero height, so it could have been described with only two digits: {10,11}. + +Some relationships between the rectangles are: "Rectangle#1's nearest neighbor +is Rectangle#2", and "Rectangle#3 is entirely inside Rectangle#2". + +Now let us create a space and add an RTREE index. + +.. code-block:: lua + + s = box.schema.create_space('rectangles') + i = s:create_index('primary',{type='HASH',parts={1,'NUM'}}) + r = s:create_index('spatial',{type='RTREE',unique=false,parts={2,'ARRAY'}}) + +Field#1 doesn't matter, we just make it because we need a primary-key index. +(RTREE indexes cannot be unique and therefore cannot be primary-key indexes.) +The second field must be an "array", which means its values must represent +{x,y} points or {x1,y1,x2,y2} rectangles. Now let us populate the table by +inserting two tuples, containing the coordinates of Rectangle#2 and Rectangle#4. + +.. code-block:: lua + + s:insert{1, {3,5,9,10}} + s:insert{2, {10,11}} + +And now, following the description of `RTREE iterator types`_, we can search the +rectangles with these requests: + +.. _RTREE iterator types: rtree-iterator_ + +.. code-block:: lua + + r:select({10,11,10,11},{iterator='EQ'}) -- Request#1 (returns 1 tuple) + r:select({4,7,5,9},{iterator='GT'}) -- Request#2 (returns 1 tuple) + r:select({1,2,3,4},{iterator='NEIGHBOR'}) -- Request#3 (returns 2 tuples) + +Request#1 returns 1 tuple because the point {10,11} is the same as the rectangle +{10,11,10,11} ("Rectangle#4" in the picture). Request#2 returns 1 tuple because +the rectangle {4,7,5,9}, which was "Rectangle#3" in the picture, is entirely +within{3,5,9,10} which was Rectangle#2. Request#3 returns 2 tuples, because the +NEIGHBOR iterator always returns all tuples, and the first returned tuple will +be {3,5,9,10} ("Rectangle#2" in the picture) because it is the closest neighbor +of {1,2,3,4} ("Rectangle#1" in the picture). + +More examples of spatial searching are online in the file `R tree index quick +start and usage`_. + +.. _R tree index quick start and usage: https://github.com/tarantool/tarantool/wiki/R-tree-index-quick-start-and-usage + diff --git a/doc/sphinx/reference/box/box_schema.rst b/doc/sphinx/reference/box/box_schema.rst new file mode 100644 index 0000000000000000000000000000000000000000..d603aefc25886fc20809ef52c18146d72d58fbeb --- /dev/null +++ b/doc/sphinx/reference/box/box_schema.rst @@ -0,0 +1,64 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `box.schema` +------------------------------------------------------------------------------- + +.. module:: box.schema + +The ``box.schema`` package has one data-definition function: ``space.create()``. + +.. function:: space.create(space-name [, {options} ]) + + Create a space. + + :param string space-name: name of space, which should not be a number and should not contain special characters + :param table options: + + :return: space object + :rtype: userdata + + .. container:: table + + **Options for box.schema.space.create** + + +---------------+--------------------+---------+---------------------+ + | Name | Effect | Type | Default | + +===============+====================+=========+=====================+ + | temporary | space is temporary | boolean | false | + +---------------+--------------------+---------+---------------------+ + | id | unique identifier | number | last space's id, +1 | + +---------------+--------------------+---------+---------------------+ + | field_count | fixed field count | number | 0 i.e. not fixed | + +---------------+--------------------+---------+---------------------+ + | if_not_exists | no error if | boolean | false | + | | duplicate name | | | + +---------------+--------------------+---------+---------------------+ + | engine | storage package | string | 'memtx' | + +---------------+--------------------+---------+---------------------+ + | user | user name | string | current user's name | + +---------------+--------------------+---------+---------------------+ + +================================================= + Example +================================================= + +.. code-block:: lua + + tarantool> s = box.schema.space.create('space55') + --- + ... + tarantool> s = box.schema.space.create('space55', {id = 555, temporary = false}) + --- + - error: Space 'space55' already exists + ... + tarantool> s = box.schema.space.create('space55', {if_not_exists = true}) + --- + ... + +After a space is created, usually the next step is to `create an index`_ for it, +and then it is available for insert, select, and all the other :mod:`box.space` +functions. + +.. _create an index: :func:`box.space.space-name:create_index` diff --git a/doc/sphinx/reference/box/box_space.rst b/doc/sphinx/reference/box/box_space.rst new file mode 100644 index 0000000000000000000000000000000000000000..d32c28da35fbc6db76857877de7c2ebfc4144f97 --- /dev/null +++ b/doc/sphinx/reference/box/box_space.rst @@ -0,0 +1,788 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `box.space` +------------------------------------------------------------------------------- + +.. module:: box.space + +The ``box.space`` package has the data-manipulation functions ``select``, +``insert``, ``replace``, ``update``, ``delete``, ``get``, ``put``. It also has +members, such as id, and whether or not a space is enabled. Package source code +is available in file +`src/box/lua/box.lua <https://github.com/tarantool/tarantool/blob/master/src/box/lua/box.lua>`_. + +A list of all ``box.space`` functions follows, then comes a list of all +``box.space`` members. + +.. function:: box.space.space-name:create_index(index-name [, {options} ]) + + Create an index. It is **mandatory** to create an index for a tuple set + before trying to insert tuples into it, or select tuples from it. The + first created index, which will be used as the primary-key index, must be + **unique**. + + :param string index-name: name of index, which should not be a number and + should not contain special characters; + :param table options: + + :return: index object + :rtype: userdata + + .. container:: table + + **Options for box.space...create_index** + + +---------------+--------------------+-----------------------------+---------------------+ + | Name | Effect | Type | Default | + +===============+====================+=============================+=====================+ + | type | type of index | string | 'TREE' | + | | | ('HASH', 'TREE', | | + | | | 'BITSET', 'RTREE') | | + | | | | | + | | | | | + | | | | | + +---------------+--------------------+-----------------------------+---------------------+ + | id | unique identifier | number | last index's id, +1 | + +---------------+--------------------+-----------------------------+---------------------+ + | unique | index is unique | boolean | true | + +---------------+--------------------+-----------------------------+---------------------+ + | if_not_exists | no error if | boolean | false | + | | duplicate name | | | + +---------------+--------------------+-----------------------------+---------------------+ + | parts | field-numbers + | ``{field_no, 'NUM'|'STR'}`` | ``{1, 'NUM'}`` | + | | types | | | + +---------------+--------------------+-----------------------------+---------------------+ + + Possible errors: too many parts. A type options other than TREE, or a + unique option other than unique, or a parts option with more than one + field component, is only applicable for the memtx storage engine. + + .. code-block:: lua + + tarantool> s = box.space.space55 + --- + ... + tarantool> s:create_index('primary', {unique = true, parts = {1, 'NUM', 2, 'STR'}}) + --- + ... + +.. function:: box.space.space-name:insert{field-value [, field-value ...]} + + Insert a tuple into a space. + + :param userdata space-name: + :param lua-value field-value(s): fields of the new tuple. + :return: the inserted tuple + :rtype: tuple + + :except: If a tuple with the same unique-key value already exists, + returns ``ER_TUPLE_FOUND``. + + .. code-block:: lua + + box.space.tester:insert{5000,'tuple number five thousand'} + +.. function:: box.space.space-name:select{field-value [, field-value ...]} + + Search for a tuple or a set of tuples in the given space. + + :param userdata space-name: + :param lua-value field-value(s): values to be matched against the index + key, which may be multi-part. + + :return: the tuples whose primary-key fields are equal to the passed + field-values. If the number of passed field-values is less + than the number of fields in the primary key, then only the + passed field-values are compared, so ``select{1,2}`` will match + a tuple whose primary key is ``{1,2,3}``. + :rtype: tuple + + :except: No such space; wrong type. + + Complexity Factors: Index size, Index type. + + .. code-block:: lua + + tarantool> s = box.schema.space.create('tmp', {temporary=true}) + --- + ... + tarantool> s:create_index('primary',{parts = {1,'NUM', 2, 'STR'}}) + --- + ... + tarantool> s:insert{1,'A'} + --- + - [1, 'A'] + ... + tarantool> s:insert{1,'B'} + --- + - [1, 'B'] + ... + tarantool> s:insert{1,'C'} + --- + - [1, 'C'] + ... + tarantool> s:insert{2,'D'} + --- + - [2, 'D'] + ... + tarantool> -- must equal both primary-key fields + tarantool> s:select{1,'B'} + --- + - - [1, 'B'] + ... + tarantool> -- must equal only one primary-key field + tarantool> s:select{1} + --- + - - [1, 'A'] + - [1, 'B'] + - [1, 'C'] + ... + tarantool> -- must equal 0 fields, so returns all tuples + tarantool> s:select{} + --- + - - [1, 'A'] + - [1, 'B'] + - [1, 'C'] + - [2, 'D'] + ... + + For examples of complex ``select``s, where one can specify which index to + search and what condition to use (for example "greater than" instead of + "equal to") and how many tuples to return, see the later section + ``box.space.space-name[.index.index-name]:select``. + +.. function:: box.space.space-name:get{field-value [, field-value ...]} + + Search for a tuple in the given space. + + :param userdata space-name: + :param lua-value field-value(s): values to be matched against the index + key, which may be multi-part. + :return: the selected tuple. + :rtype: tuple + + :except: If space-name does not exist. + + Complexity Factors: Index size, Index type, + Number of indexes accessed, WAL settings. + + .. code-block:: lua + + tarantool> box.space.tester:get{1} + +.. function:: box.space.space-name:drop() + + Drop a space. + + :return: nil + :except: If space-name does not exist. + + Complexity Factors: Index size, Index type, + Number of indexes accessed, WAL settings. + + .. code-block:: lua + + tarantool> box.space.space_that_does_not_exist:drop() + +.. function:: box.space.space-name:rename(space-name) + + Rename a space. + + :param string space-name: new name for space. + + :return: nil + :except: If space-name does not exist. + + .. code-block:: lua + + tarantool> box.space.space55:rename('space56') + --- + ... + tarantool> box.space.space56:rename('space55') + --- + ... + +.. function:: box.space.space-name:replace{field-value [, field-value ...]} + box.space.space-name:put{field-value [, field-value ...]} + + Insert a tuple into a space. If a tuple with the same primary key already + exists, ``box.space...:replace()`` replaces the existing tuple with a new + one. The syntax variants ``box.space...:replace()`` and + ``box.space...:put()`` have the same effect; the latter is sometimes used + to show that the effect is the converse of ``box.space...:get()``. + + :param userdata space-name: + :param lua-value field-value(s): fields of the new tuple. + + :return: the inserted tuple. + :rtype: tuple + + :except: If a different tuple with the same unique-key + value already exists, returns ``ER_TUPLE_FOUND``. + (This would only happen if there was a secondary + index. By default secondary indexes are unique.) + + Complexity Factors: Index size, Index type, + Number of indexes accessed, WAL settings. + + .. code-block:: lua + + tarantool> box.space.tester:replace{5000, 'New value'} + +.. function:: box.space.space-name:update(key, {{operator, field_no, value}, ...}) + + Update a tuple. + + The ``update`` function supports operations on fields — assignment, + arithmetic (if the field is unsigned numeric), cutting and pasting + fragments of a field, deleting or inserting a field. Multiple + operations can be combined in a single update request, and in this + case they are performed atomically and sequentially. Each operation + requires specification of a field number. When multiple operations + are present, the field number for each operation is assumed to be + relative to the most recent state of the tuple, that is, as if all + previous operations in a multi-operation update have already been + applied. In other words, it is always safe to merge multiple update + invocations into a single invocation, with no change in semantics. + + :param userdata space-name: + :param lua-value key: primary-key field values, must be passed as a Lua + table if key is multi-part + :param table {operator, field_no, value}: a group of arguments for each + operation, indicating what the operation is, what field the + operation will apply to, and what value will be applied. For + some operations the field number can be -1, meaning the last + field in the tuple. Possible operators are: “+†for addition, + “-†for subtraction, “&†for bitwise AND, “|†for bitwise OR, + “^†for bitwise exclusive OR (XOR), “:†for string splice, “!†+ for insert, “#†for delete. Thus in the instruction + ``s:update(44, {{'+',1,55},{'=',3,'x'}})`` the primary-key + value is 44, the operators are '+' and '=' meaning "add a value + to a field and then assign a value to a field", the first + affected field is field 1 and the value which will be added to + it is 55, the second affected field is field 3 and the value + which will be assigned to it is 'x'. + + :return: the updated tuple. + :rtype: tuple + + :except: it is illegal to modify a primary-key field. + + Complexity Factors: Index size, Index type, number of indexes accessed, WAL + settings. + + .. code-block:: lua + + -- Assume that the initial state of the database is ... + -- tester has one tuple set and one primary key whose type is 'NUM'. + -- There is one tuple, with field[1] = 999 and field[2] = 'A'. + + -- In the following update ... + -- The first argument is tester, that is, the affected space is tester + -- The second argument is 999, that is, the affected tuple is identified by + -- primary key value = 999 + -- The third argument is '=', that is, there is one operation, assignment + -- to a field + -- The fourth argument is 2, that is, the affected field is field[2] + -- The fifth argument is 'B', that is, field[2] contents change to 'B' + -- Therefore, after the following update, field[1] = 999 and field[2] = 'B'. + box.space.tester:update(999, {{'=', 2, 'B'}}) + + -- In the following update, the arguments are the same, except that ... + -- the key is passed as a Lua table (inside braces). This is unnecessary + -- when the primary key has only one field, but would be necessary if the + -- primary key had more than one field. + -- Therefore, after the following update, field[1] = 999 and field[2] = 'B' + -- (no change). + box.space.tester:update({999}, {{'=', 2, 'B'}}) + + -- In the following update, the arguments are the same, except that ... + -- The fourth argument is 3, that is, the affected field is field[3]. + -- It is okay that, until now, field[3] has not existed. It gets added. + -- Therefore, after the following update, field[1] = 999, field[2] = 'B', + -- field[3] = 1. + box.space.tester:update({999}, {{'=', 3, 1}}) + + -- In the following update, the arguments are the same, except that ... + -- The third argument is '+', that is, the operation is addition rather + -- than assignment. + -- Since field[3] previously contained 1, this means we're adding 1 to 1. + -- Therefore, after the following update, field[1] = 999, field[2] = 'B', + -- field[3] = 2. + box.space.tester:update({999}, {{'+', 3, 1}}) + + -- In the following update ... + -- The idea is to modify two fields at once. + -- The formats are '|' and '=', that is, there are two operations, OR and + -- assignment. + -- The fourth and fifth arguments mean that field[3] gets ORed with 1. + -- The seventh and eighth arguments mean that field[2] gets assigned 'C'. + -- Therefore, after the following update, field[1] = 999, field[2] = 'C', + -- field[3] = 3. + box.space.tester:update({999}, {{'|', 3, 1}, {'=', 2, 'C'}}) + + -- In the following update ... + -- The idea is to delete field[2], then subtract 3 from field[3], but ... + -- after the delete, there is a renumbering -- so field[3] becomes field[2] + -- before we subtract 3 from it, and that's why the seventh argument is 2 not 3. + -- Therefore, after the following update, field[1] = 999, field[2] = 0. + box.space.tester:update({999}, {{'-- ', 2, 1}, {'-', 2, 3}}) + + -- In the following update ... + -- We're making a long string so that splice will work in the next example. + -- Therefore, after the following update, field[1] = 999, field[2] = 'XYZ'. + box.space.tester:update({999}, {{'=', 2, 'XYZ'}}) + + -- In the following update ... + -- The third argument is ':', that is, this is the example of splice. + -- The fourth argument is 2 because the change will occur in field[2]. + -- The fifth argument is 2 because deletion will begin with the second byte. + -- The sixth argument is 1 because the number of bytes to delete is 1. + -- The seventh argument is '!!' because '!!' is to be added at this position. + -- Therefore, after the following update, field[1] = 999, field[2] = 'X!!Z'. + box.space.tester:update({999}, {{':', 2, 2, 1, '!!'}}) + +.. function:: box.space.space-name:delete{field-value [, field-value ...]} + + Delete a tuple identified by a primary key. + + :param userdata space-name: + :param lua-value field-value(s): values to match against keys + in the primary index. + + :return: the deleted tuple + :rtype: tuple + + Complexity Factors: Index size, Index type + + .. code-block:: lua + + tarantool> box.space.tester:delete(0) + --- + - [0, 'My first tuple'] + ... + tarantool> box.space.tester:delete(0) + --- + ... + tarantool> box.space.tester:delete('a') + --- + - error: 'Supplied key type of part 0 does not match index part type: + expected NUM' + ... + +.. data:: space-name.id + + Ordinal space number. Spaces can be referenced by either name or + number. Thus, if space 'tester' has id = 800, then + ``box.space.tester:insert{0}`` and ``box.space[800]:insert{0}`` + are equivalent requests. + + :rtype: number + +.. data:: space-name.enabled + + Whether or not this space is enabled. + The value is false if there is no index. + + :rtype: boolean + +.. data:: space-name.field_count + + The required field count for all tuples in this space. The field_count + can be set initially with + ``box.schema.space.create... field_count = new-field-count-value ...``. + The default value is 0, which means there is no required field count. + + :rtype: number + +.. data:: space-name.index[] + + A container for all defined indexes. An index is a Lua object of type + :mod:`box.index` with methods to search tuples and iterate over them in + predefined order. + + :rtype: table + + .. code-block: lua + + tarantool> box.space.tester.id + --- + - 512 + ... + tarantool> box.space.tester.field_count + --- + - 0 + ... + tarantool> box.space.tester.index.primary.type + --- + - TREE + ... + +.. function:: box.space.space-name:len() + + .. NOTE:: + + The ``len()`` function is only applicable for the memtx storage engine. + + :return: Number of tuples in the space. + + .. code-block:: lua + + tarantool> box.space.tester:len() + --- + - 2 + ... + +.. function:: box.space.space-name:truncate() + + Deletes all tuples. + + Complexity Factors: Index size, Index type, Number of tuples accessed. + + :return: nil + + .. code-block:: lua + + tarantool> box.space.tester:truncate() + --- + ... + tarantool> box.space.tester:len() + --- + - 0 + ... + +.. function:: box.space.space-name:inc{field-value [, field-value ...]} + + Increments a counter in a tuple whose primary key matches the + ``field-value(s)``. The field following the primary-key fields + will be the counter. If there is no tuple matching the + ``field-value(s)``, a new one is inserted with initial counter + value set to 1. + + :param userdata space-name: + :param lua-value field-value(s): values which must match the primary key. + :return: the new counter value + :rtype: number + + Complexity Factors: Index size, Index type, WAL settings. + + .. code-block:: lua + + tarantool> s = box.schema.space.create('forty_second_space') + --- + ... + tarantool> s:create_index('primary', {unique = true, parts = {1, 'NUM', 2, 'STR'}}) + --- + ... + tarantool> box.space.forty_second_space:inc{1,'a'} + --- + - 1 + ... + tarantool> box.space.forty_second_space:inc{1,'a'} + --- + - 2 + ... + +.. function:: box.space.space-name:dec{field-value [, field-value ...]} + + Decrements a counter in a tuple whose primary key matches the + ``field-value(s)``. The field following the primary-key fields + will be the counter. If there is no tuple matching the + ``field-value(s)``, a new one is not inserted. If the counter value drops + to zero, the tuple is deleted. + + :param userdata space-name: + :param lua-value field-value(s): values which must match the primary key. + :return: the new counter value + :rtype: number + + Complexity Factors: Index size, Index type, WAL settings. + + .. code-block:: lua + + tarantool> s = box.schema.space.create('space19') + --- + ... + tarantool> s:create_index('primary', {unique = true, parts = {1, 'NUM', 2, 'STR'}}) + --- + ... + tarantool> box.space.space19:insert{1,'a',1000} + --- + - [1, 'a', 1000] + ... + tarantool> box.space.space19:dec{1,'a'} + --- + - 999 + ... + tarantool> box.space.space19:dec{1,'a'} + --- + - 998 + ... + +.. function:: box.space.space-name:auto_increment{field-value [, field-value ...]} + + Insert a new tuple using an auto-increment primary key. The space specified + by space-name must have a NUM primary key index of type TREE. The + primary-key field will be incremented before the insert. + + :param userdata space-name: + :param lua-value field-value(s): values for the tuple's fields, + other than the primary-key field. + + :return: the inserted tuple. + :rtype: tuple + + Complexity Factors: Index size, Index type, + Number of indexes accessed, WAL settings. + + :except: index has wrong type or primary-key indexed field is not a number. + + .. code-block:: lua + + tarantool> box.space.tester:auto_increment{'Fld#1', 'Fld#2'} + --- + - [1, 'Fld#1', 'Fld#2'] + ... + tarantool> box.space.tester:auto_increment{'Fld#3'} + --- + - [2, 'Fld#3'] + ... + +.. function:: box.space.space-name:pairs() + + A helper function to prepare for iterating over all tuples in a space. + + :return: function which can be used in a for/end loop. Within the loop, a value is returned for each iteration. + :rtype: function, tuple + + .. code-block:: lua + + tarantool> s = box.schema.space.create('space33') + --- + ... + tarantool> -- index 'X' has default parts {1,'NUM'} + tarantool> s:create_index('X', {}) + --- + ... + tarantool> s:insert{0,'Hello my '}; s:insert{1,'Lua world'} + --- + ... + tarantool> tmp = ''; for k, v in s:pairs() do tmp = tmp .. v[2] end + --- + ... + tarantool> tmp + --- + - Hello my Lua world + ... + +.. data:: _schema + + ``_schema`` is a system tuple set. Its single tuple contains these fields: + ``'version', major-version-number, minor-version-number``. + + The following function will display all fields in all tuples of ``_schema``. + + .. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local ta = {}, i, line + for k, v in box.space._schema:pairs() do + i = 1 + line = '' + while i <= #v do line = line .. v[i] .. ' ' i = i + 1 end + table.insert(ta, line) + end + return ta + end! + console.delimiter('')! + + + Here is what ``example()`` returns in a typical installation: + + .. code-block:: lua + + tarantool> example() + --- + - - 'cluster 1ec4e1f8-8f1b-4304-bb22-6c47ce0cf9c6 ' + - 'max_id 520 ' + - 'version 1 6 ' + ... + +.. data:: _space + + ``_space`` is a system tuple set. Its tuples contain these fields: + ``id, uid, space-name, engine, field_count, temporary``. + + The following function will display all simple fields + in all tuples of ``_space``. + + .. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local ta = {}, i, line + for k, v in box.space._space:pairs() do + i = 1 + line = '' + while i <= #v do + if type(v[i]) ~= 'table' then + line = line .. v[i] .. ' ' + end + i = i + 1 + end + table.insert(ta, line) + end + return ta + end! + console.delimiter('')! + + Here is what ``example()`` returns in a typical installation: + + .. code-block:: lua + + tarantool> example() + --- + - - '272 1 _schema memtx 0 ' + - '280 1 _space memtx 0 ' + - '288 1 _index memtx 0 ' + - '296 1 _func memtx 0 ' + - '304 1 _user memtx 0 ' + - '312 1 _priv memtx 0 ' + - '320 1 _cluster memtx 0 ' + - '512 1 tester memtx 0 ' + - '513 1 origin sophia 0 ' + - '514 1 archive memtx 0 ' + ... + +.. data:: _index + + ``_index`` is a system tuple set. Its tuples contain these fields: + ``space-id index-id index-name index-type index-is-unique + index-field-count [tuple-field-no, tuple-field-type ...]``. + + The following function will display all fields in all tuples of _index. + + .. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local ta = {}, i, line + for k, v in box.space._index:pairs() do + i = 1 + line = '' + while i <= #v do line = line .. v[i] .. ' ' i = i + 1 end + table.insert(ta, line) + end + return ta + end! + console.delimiter('')! + + Here is what ``example()`` returns in a typical installation: + + .. code-block:: lua + + tarantool> example() + --- + - - '272 0 primary tree 1 1 0 str ' + - '280 0 primary tree 1 1 0 num ' + - '280 1 owner tree 0 1 1 num ' + - '280 2 name tree 1 1 2 str ' + - '288 0 primary tree 1 2 0 num 1 num ' + - '288 2 name tree 1 2 0 num 2 str ' + - '296 0 primary tree 1 1 0 num ' + - '296 1 owner tree 0 1 1 num ' + - '296 2 name tree 1 1 2 str ' + - '304 0 primary tree 1 1 0 num ' + - '304 1 owner tree 0 1 1 num ' + - '304 2 name tree 1 1 2 str ' + - '312 0 primary tree 1 3 1 num 2 str 3 num ' + - '312 1 owner tree 0 1 0 num ' + - '312 2 object tree 0 2 2 str 3 num ' + - '320 0 primary tree 1 1 0 num ' + - '320 1 uuid tree 1 1 1 str ' + - '512 0 primary tree 1 1 0 num ' + - '513 0 first tree 1 1 0 NUM ' + - '514 0 first tree 1 1 0 STR ' + ... + +.. data:: _user + + ``_user`` is a new system tuple set for + support of the `authorization feature`_. + +.. data:: _priv + + ``_priv`` is a new system tuple set for + support of the `authorization feature`_. + +.. data:: _cluster + + ``_cluster`` is a new system tuple set + for support of the `replication feature`_. + +.. _authorization feature: http://tarantool.org/doc/user_guide.html#authentication +.. _replication feature: http://tarantool.org/doc/user_guide.html#replication + +================================================= + Example +================================================= + +This function will illustrate how to look at all the spaces, and for each +display: approximately how many tuples it contains, and the first field of +its first tuple. The function uses Tarantool ``box.space`` functions ``len()`` +and ``pairs()``. The iteration through the spaces is coded as a scan of the +``_space`` system tuple set, which contains metadata. The third field in +``_space`` contains the space name, so the key instruction +"``space_name = v[3]``" means "``space_name`` = the ``space_name`` field in +the tuple of ``_space`` that we've just fetched with ``pairs()``". The function +returns a table. + +.. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local tuple_count, space_name, line + local ta = {} + for k, v in box.space._space:pairs() do + space_name = v[3] + if box.space[space_name].index[0] ~= nil then + tuple_count = box.space[space_name]:len() + else + tuple_count = 0 + end + line = space_name .. ' tuple_count =' .. tuple_count + if tuple_count > 0 then + for k1, v1 in box.space[space_name]:pairs() do + line = line .. '. first field in first tuple = ' .. v1[1] + break + end + end + table.insert(ta, line) + end + return ta + end! + console.delimiter('')! + +... And here is what happens when one invokes the function: + +.. code-block:: lua + + tarantool> example() + --- + - - _schema tuple_count =3. first field in first tuple = cluster + - _space tuple_count =15. first field in first tuple = 272 + - _index tuple_count =25. first field in first tuple = 272 + - _func tuple_count =1. first field in first tuple = 1 + - _user tuple_count =4. first field in first tuple = 0 + - _priv tuple_count =6. first field in first tuple = 1 + - _cluster tuple_count =1. first field in first tuple = 1 + - tester tuple_count =2. first field in first tuple = 1 + - origin tuple_count =0 + - archive tuple_count =13. first field in first tuple = test_0@tarantool.org + - space55 tuple_count =0 + - tmp tuple_count =0 + - forty_second_space tuple_count =1. first field in first tuple = 1 + ... diff --git a/doc/sphinx/reference/box/index.rst b/doc/sphinx/reference/box/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..f2726d03d96c1bb9614381dba1d71356c9c46c74 --- /dev/null +++ b/doc/sphinx/reference/box/index.rst @@ -0,0 +1,87 @@ +------------------------------------------------------------------------------- + The `box` library +------------------------------------------------------------------------------- + +As well as executing Lua chunks or defining their own functions, users can exploit +the Tarantool server's storage functionality with the ``box`` Lua library. + +===================================================================== + Packages of the box library +===================================================================== + +The contents of the box library can be inspected at runtime with ``box``, with +no arguments. The packages inside the box library are: + +.. toctree:: + :maxdepth: 1 + + box_schema + box_tuple + box_space + box_index + net_box + box_cfg + box_info + box_slab + box_stat + +Every package contains one or more Lua functions. A few packages contain +members as well as functions. The functions allow data definition (create +alter drop), data manipulation (insert delete update select replace), and +introspection (inspecting contents of spaces, accessing server configuration). + + +.. container:: table + + **Complexity Factors that may affect data + manipulation functions in the box library** + + +-------------------+-----------------------------------------------------+ + | Index size | The number of index keys is the same as the number | + | | of tuples in the data set. For a TREE index, if | + | | there are more keys then the lookup time will be | + | | greater, although of course the effect is not | + | | linear. For a HASH index, if there are more keys | + | | then there is more RAM use, but the number of | + | | low-level steps tends to remain constant. | + +-------------------+-----------------------------------------------------+ + | Index type | Typically a HASH index is faster than a TREE index | + | | if the number of tuples in the tuple set is greater | + | | than one. | + +-------------------+-----------------------------------------------------+ + | Number of indexes | Ordinarily only one index is accessed to retrieve | + | accessed | one tuple. But to update the tuple, there must be N | + | | accesses if the tuple set has N different indexes. | + +-------------------+-----------------------------------------------------+ + | Number of tuples | A few requests, for example select, can retrieve | + | accessed | multiple tuples. This factor is usually less | + | | important than the others. | + +-------------------+-----------------------------------------------------+ + | WAL settings | The important setting for the write-ahead log is | + | | `wal_mode`_. If the setting causes no writing or | + | | delayed writing, this factor is unimportant. If the | + | | settings causes every data-change request to wait | + | | for writing to finish on a slow device, this factor | + | | is more important than all the others. | + +-------------------+-----------------------------------------------------+ + +.. _wal_mode: http://tarantool.org/doc/user_guide.html#wal_mode + +In the discussion of each data-manipulation function there will be a note about +which Complexity Factors might affect the function's resource usage. + +===================================================================== + The two storage engines: memtx and sophia +===================================================================== + +A storage engine is a set of very-low-level routines which actually store and +retrieve tuple values. Tarantool offers a choice of two storage engines: memtx +(the in-memory storage engine) and sophia (the on-disk storage engine). +To specify that the engine should be sophia, add a clause: ``engine = 'sophia'``. +The manual concentrates on memtx because it is the default and has been around +longer. But sophia is a working key-value engine and will especially appeal to +users who like to see data go directly to disk, so that recovery time might be +shorter and database size might be larger. For architectural explanations and +benchmarks, see sphia.org. On the other hand, sophia lacks some functions and +options that are available with memtx. Where that is the case, the relevant +description will contain the words "only applicable for the memtx storage engine". diff --git a/doc/sphinx/reference/fiber-ipc.rst b/doc/sphinx/reference/fiber-ipc.rst index 56b232e6f6bdbf1ef476ecf852c919a6f8ea941e..6b89ed1985b7154b061df06660d1874feb60f988 100644 --- a/doc/sphinx/reference/fiber-ipc.rst +++ b/doc/sphinx/reference/fiber-ipc.rst @@ -30,7 +30,7 @@ other Lua object. Use object-oriented syntax, for example :return: new channel. :rtype: userdata -.. class:: channel +.. class:: channel_object .. method:: put(message[, timeout]) diff --git a/doc/sphinx/reference/index.rst b/doc/sphinx/reference/index.rst index 5ef7cc4520053df59cfb5eb52e16eb2156613c6f..7782683952061970d7bcfc493969aaeade61abf7 100644 --- a/doc/sphinx/reference/index.rst +++ b/doc/sphinx/reference/index.rst @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- - Library Reference + Reference Manual ------------------------------------------------------------------------------- Lua_ is a light-weight, multi-paradigm, embeddable language. Stored procedures @@ -28,3 +28,4 @@ Lua functions can run in the background and perform administrative tasks. pickle other expirationd + box/index diff --git a/doc/sphinx/reference/socket.rst b/doc/sphinx/reference/socket.rst index 67f3c076dc6b126cec95ca0989d3ba4ef9b79e67..0ffdf70b0c96fbc3ca86472dd44fc1dd35e42ba3 100644 --- a/doc/sphinx/reference/socket.rst +++ b/doc/sphinx/reference/socket.rst @@ -61,7 +61,7 @@ are ``errno``, ``error``. | teardown +-------------+ | | close | +----------------+-------------+ - | | errno | + | | error | | error checking +-------------+ | | errno | +----------------+-------------+ @@ -149,13 +149,12 @@ the function invocations will look like ``sock:function_name(...)``. socket.tcp_server('localhost', 3302, function () end). - .. class:: socket_object .. method:: sysconnect(host, port) Connect a socket to a remote host. The argument values are the same as - in the `Linux man page <http://man7.org/linux/man-pages/man2/connect.2.html>`_. + in the Linux man page [1]_. The host must be an IP address. Parameters: @@ -181,6 +180,7 @@ the function invocations will look like ``sock:function_name(...)``. sock:sysconnect('127.0.0.1', 80) .. method:: send(data) + write(data) Send data over a connected socket. @@ -188,11 +188,6 @@ the function invocations will look like ``sock:function_name(...)``. :return: true if success, false if error. :rtype: boolean - .. NOTE:: - - The function ``sock:write(...)`` has - the same parameters and same effect. - .. method:: syswrite(size) Write as much as possible data to the socket buffer if non-blocking. @@ -243,7 +238,7 @@ the function invocations will look like ``sock:function_name(...)``. .. method:: bind(host [, port]) Bind a socket to the given host/port. A UDP socket after binding - can be used to receive data (see :meth:`socket_object.recvfrom()`). + can be used to receive data (see :func:`socket_object.recvfrom`). A TCP socket can be used to accept new connections, after it has been put in listen mode. @@ -264,7 +259,7 @@ the function invocations will look like ``sock:function_name(...)``. may be ``SOMAXCONN``. :return: true for success, false for error. - :rtype: boolean + :rtype: boolean. .. method:: accept() @@ -340,7 +335,7 @@ the function invocations will look like ``sock:function_name(...)``. .. method:: setsockopt(level, name, value) Set socket flags. The argument values are the same as in the - `Linux man page <http://man7.org/linux/man-pages/man2/setsockopt.2.html>`_. + Linux man page [2]_. The ones that Tarantool accepts are: * SO_ACCEPTCONN @@ -379,7 +374,7 @@ the function invocations will look like ``sock:function_name(...)``. .. method:: linger([active]) Set or clear the SO_LINGER flag. For a description of the flag, see - `Linux man page <http://man7.org/linux/man-pages/man1/loginctl.1.html>`_. + Linux man page [3]_. :param boolean active: @@ -514,3 +509,8 @@ computer to communicate with itself, but shows that the system works. .. _luasocket: https://github.com/diegonehab/luasocket + +.. [1] http://man7.org/linux/man-pages/man2/connect.2.html +.. [2] http://man7.org/linux/man-pages/man2/setsockopt.2.html +.. [3] http://man7.org/linux/man-pages/man1/loginctl.1.html +