diff --git a/CMakeLists.txt b/CMakeLists.txt index f53dc6beb8919961ede4cb46921f3cc3299ea123..e9ccc2fad1fdd7b24fa808edf96e355ef77e7a7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ add_custom_target(tags COMMAND ctags -R -f tags # set (CPACK_PACKAGE_VERSION_MAJOR "1") set (CPACK_PACKAGE_VERSION_MINOR "5") -set (CPACK_PACKAGE_VERSION_PATCH "2") +set (CPACK_PACKAGE_VERSION_PATCH "3") set (PACKAGE_VERSION "") diff --git a/cfg/core_cfg.cfg_tmpl b/cfg/core_cfg.cfg_tmpl index 9c695ebef3606f2f2159cbbc4acb0dbf8554b02a..6a33bebd66ea6176852fe793958980e6ac1e377b 100644 --- a/cfg/core_cfg.cfg_tmpl +++ b/cfg/core_cfg.cfg_tmpl @@ -22,8 +22,8 @@ admin_port=0, ro # Replication clients should use this port (bind_ipaddr:replication_port). replication_port=0, ro -# Log verbosity, possible values: ERROR=1, CRIT=2, WARN=3, INFO=4(default), DEBUG=5 -log_level=4 +# Log verbosity, possible values: SYS_ERROR = 1, ERROR=2, CRIT=3, WARN=4, INFO=5(default), DEBUG=6 +log_level=5 # Size of slab arena in GB slab_alloc_arena=1.0, ro diff --git a/cfg/tarantool_box_cfg.c b/cfg/tarantool_box_cfg.c index e00e8273183a7f2ad831521da9b6d5480b84c648..ecdc20c629643d96f2c47b15a8d38662c06ccad1 100644 --- a/cfg/tarantool_box_cfg.c +++ b/cfg/tarantool_box_cfg.c @@ -80,7 +80,7 @@ fill_default_tarantool_cfg(tarantool_cfg *c) { c->coredump = false; c->admin_port = 0; c->replication_port = 0; - c->log_level = 4; + c->log_level = 5; c->slab_alloc_arena = 1; c->slab_alloc_minimal = 64; c->slab_alloc_factor = 2; diff --git a/cfg/tarantool_box_cfg.h b/cfg/tarantool_box_cfg.h index 2c883bbdca2e158df55844ea6026cb1fa055c2a9..994ff17cb8d6d151fff6a2a7c3ebe4ea2f595ed3 100644 --- a/cfg/tarantool_box_cfg.h +++ b/cfg/tarantool_box_cfg.h @@ -73,7 +73,7 @@ typedef struct tarantool_cfg { /* Replication clients should use this port (bind_ipaddr:replication_port). */ int32_t replication_port; - /* Log verbosity, possible values: ERROR=1, CRIT=2, WARN=3, INFO=4(default), DEBUG=5 */ + /* Log verbosity, possible values: SYS_ERROR = 1, ERROR=2, CRIT=3, WARN=4, INFO=5(default), DEBUG=6 */ int32_t log_level; /* Size of slab arena in GB */ diff --git a/client/tarantool/tc_print_xlog.c b/client/tarantool/tc_print_xlog.c index 1af117de59494c0c29767f06fb65a42b840f153b..4422714a4fa654fe3aaaef5ba5f526263b5c643e 100644 --- a/client/tarantool/tc_print_xlog.c +++ b/client/tarantool/tc_print_xlog.c @@ -81,6 +81,7 @@ tc_printer_xlog_tarantool(struct tnt_log_row *row, tc_print_tuple(&r->r.insert.t); break; case TNT_OP_DELETE: + case TNT_OP_DELETE_1_3: tc_print_tuple(&r->r.del.t); break; case TNT_OP_UPDATE: @@ -104,6 +105,7 @@ tc_printer_xlog_lua(struct tnt_log_row *row, tc_print_lua_fields(&r->r.insert.t); break; case TNT_OP_DELETE: + case TNT_OP_DELETE_1_3: tc_printf("delete("); tc_printf("%"PRIu32", ", r->r.del.h.ns); tc_print_lua_tuple(&r->r.del.t); diff --git a/client/tarantool/tc_query.c b/client/tarantool/tc_query.c index 16909a8b1740ceea706a7f6626df60687d717e41..f662388dc494c341255b76c4d9a918cf8a66f5ab 100644 --- a/client/tarantool/tc_query.c +++ b/client/tarantool/tc_query.c @@ -53,6 +53,7 @@ char *tc_query_type(uint32_t type) { case TNT_OP_PING: return "Ping"; case TNT_OP_INSERT: return "Insert"; case TNT_OP_DELETE: return "Delete"; + case TNT_OP_DELETE_1_3: return "Delete_1_3"; case TNT_OP_UPDATE: return "Update"; case TNT_OP_SELECT: return "Select"; case TNT_OP_CALL: return "Call"; diff --git a/doc/user/configuration-reference.xml b/doc/user/configuration-reference.xml index 5fe4fe8d28f5aa8508504ec0e925a31351a70dc3..6e12b4bfe355cae109eb0652811133b0fb27c4cb 100644 --- a/doc/user/configuration-reference.xml +++ b/doc/user/configuration-reference.xml @@ -548,7 +548,7 @@ tarantool_box: primary@sessions pri: 33013 sec: 33014 adm: 33015</programlisting <entry>Do not flush the write ahead log to disk more often than once in wal_fsync_delay seconds. By default the delay is zero, which means there is no flushing after writes - (the meaning of wait_fsync_delay=0 meay change in later versions). + (the meaning of wal_fsync_delay=0 may change in later versions). Setting the delay may be necessary to increase write throughput, but may lead to several last updates being lost in case of a power failure. Such failure, however, diff --git a/doc/user/errcode.xml b/doc/user/errcode.xml index 5087ec601cc9569c53f08b7e4a7a322dc8d8b2c9..9d620e2c4202fcee25831ce77e65824f5004cf67 100644 --- a/doc/user/errcode.xml +++ b/doc/user/errcode.xml @@ -80,7 +80,7 @@ file <filename xlink:href="https://github.com/tarantool/tarantool/blob/master/in <varlistentry> <term xml:id="ER_UPDATE_FIELD" xreflabel="ER_UPDATE_FIELD">ER_UPDATE_FIELD</term> - <listitem><para>A error occured during update of a field. + <listitem><para>An error occurred during update of a field. </para></listitem> </varlistentry> diff --git a/doc/user/lua-tutorial.xml b/doc/user/lua-tutorial.xml index 35f1ddc88d79e6e4f780bf29643cdc57a0dd9f9d..07bf19e5b68659c4121ed6ab06fa51b73299967f 100644 --- a/doc/user/lua-tutorial.xml +++ b/doc/user/lua-tutorial.xml @@ -4,8 +4,10 @@ <title>Lua tutorial</title> +<section xml:id="lua-tutorial-insert"> +<title>Insert one million tuples with a Lua stored procedure</title> + <para> -<bridgehead renderas="sect4">Insert one million tuples with a Lua stored procedure</bridgehead> This is an exercise assignment: <quote>Insert one million tuples. Each tuple should have a constantly-increasing numeric primary-key field and a random alphabetic 10-character string field.</quote> @@ -31,7 +33,7 @@ in with the tarantool client while reading along. We are going to use the "tarantool_sandbox" that was created in section <olink targetptr="getting-started-start-stop">Starting Tarantool and making your first database</olink>. So there is a single space, and a numeric primary key, -a runnning tarantool_box server, and a running tarantool client. +a running tarantool_box server, and a running tarantool client. </para> <para> @@ -311,7 +313,7 @@ lua function main_function() end! </programlisting> The new line here is box.replace(0,t). The first parameter is 0, because the -insertion is going to be to space[0]. The second parmeter is the tuple value. +insertion is going to be to space[0]. The second parameter is the tuple value. To be perfectly correct we could have said box.insert(0,t) here, rather than box.replace(0,t), but "replace" means <quote>insert even if there is already a tuple whose primary-key value is a duplicate</quote>, and that makes it easier to re-run @@ -377,7 +379,7 @@ lua function main_function() end_time = os.clock() end! call main_function()! -lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')! +lua print('insert done in ' .. end_time - start_time .. ' seconds')! </programlisting> The Lua os.clock() function will return the number of seconds since the start. @@ -401,7 +403,7 @@ the statement that created main_function(), and the statement that invokes main_ <programlisting> -#NB: If you have already said "setopt delimiter = '!'", do not say it again -- skip this statement. +#Skip the following statement if you have already said "setopt delimiter = '!'" setopt delimiter = '!' lua function string_function() @@ -426,7 +428,7 @@ lua function main_function() end_time = os.clock() end! call main_function()! -lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')! +lua print('insert done in ' .. end_time - start_time .. ' seconds')! </programlisting> The screen now looks like this: @@ -459,9 +461,9 @@ localhost> <userinput>lua function main_function()</userinput> ... localhost> <userinput>call main_function()!</userinput> Call OK, 0 rows affected -localhost> <userinput>lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')!</userinput> +localhost> <userinput>lua print('insert done in ' .. end_time - start_time .. ' seconds')!</userinput> --- -inserted 1 million tuples in 41.66 seconds +insert done in 41.66 seconds ... localhost> </programlisting> @@ -481,6 +483,242 @@ tuples took 42 seconds. The host computer was a Toshiba laptop with a 2.2-GHz Intel Core Duo CPU. </para> +</section> + +<section xml:id="lua-tutorial-sum"> +<title>Sum a JSON field for all tuples</title> + +<para> +This is an exercise assignment: <quote>Assume that inside every tuple there +is a string formatted as JSON. Inside that string there is a JSON numeric +field. For each tuple, find the numeric field's value and add it to a +'sum' variable. At end, return the 'sum' variable.</quote> +</para> + +<para> +The purpose of the exercise is to show one way to read and process tuples. +This is harder than the first exercise because the function is useful. +A function which is useful, and therefore is going to be used more than +once by more than one person, has to be robust and understandable. +So here is the function. It's best to start by looking at each line -- +there are only twelve lines so it will only take a few minutes to guess what they do. +Then it will take somewhat longer to read the detailed +comments about the function, and follow the links wherever necessary. +Once again, to further enhance learning, type the statements +in with the tarantool client while reading along. At the very end there +is an example that shows how to make a few tuples and invoke the function. +</para> + +<programlisting language="lua"> +SETOPT DELIMITER='!' +lua function sum_json_field(field_name) + local v, t, sum, field_value, is_valid_json, lua_table --[[1]] + sum = 0 --[[2]] + v = box.space[0].index[0]:iterator(box.index.ALL) --[[3]] + for t in v do --[[4]] + is_valid_json, lua_table = pcall(box.cjson.decode, t[1]) --[[5]] + if is_valid_json then --[[6]] + field_value = lua_table[field_name] --[[7]] + if type(field_value) == "number" then sum = sum + field_value end --[[8]] + end --[[9]] + end --[[10]] + return sum --[[11]] + end! +SETOPT DELIMITER=''! +</programlisting> + +<para> +SPACES. There is one space after every comma (line 3, line 5). There is one space +before and one space after every operator such as '<code>=</code>' or '<code>==</code>' or '<code>+</code>' (line 2, +line 3, line 5, line 7, line 8). There are no spaces around parentheses. +Each indentation is two spaces (actually Tarantool developers often use four +spaces but we follow the unofficial <link xlink:href="http://lua-users.org/wiki/LuaStyleGuide">Lua Style Guide</link> here). +Indentation starts within a function, and within every block that is introduced +by "<code>for</code>" or "<code>if</code>", and ends when the block ends with "<code>end</code>" (lines 4 to 10, lines 6 to 9). +</para> + +<para> +COMMENTS. Every comment begins with "<code>--[[</code>" and ends with "<code>]]</code>". Although this example uses comments to +indicate line numbers, the normal practice is to put comments when the +meaning of the code would not be clear by merely looking at the code. +</para> + +<para> +LINE 1: WHY "LOCAL". This line declares all the variables that will be used +in the function. Actually it's not necessary to declare all variables at the start, +and in a long function it would be better to declare variables just before using +them. In fact it's not even necessary to declare variables at all, but an +undeclared variable is "global". That's not desirable for any of the variables +that are declared in line 1, because all of them are for use only within the +function. +</para> + +<para> +LINE 1: NAMES. Single-letter variable names like <code>'v</code>' are okay when they're +strictly for use as an iterator -- '<code>v</code>' is going to be the thing that goes +up in the "<code>for t in v do</code>" statement in line 4. Terse names like '<code>sum</code>' +are okay for local variables when there's only one sum and the name is +not an abbreviation. The prefix "is_" in the name "<code>is_valid_json</code>" is +there because the variable will get a Boolean (true/false) value and +will be true only for a string that "is valid [according to] JSON [format rules]". +</para> + +<para> +LINE 2: INITIALIZING. The only variable that needs initializing is <code>sum</code>, which +must start at zero, so line 2 is "<code>sum = 0</code>". It's easier to do initialization +on the declaration line, that is, we could have said "<code>local sum = 0</code>". We +chose to put it on a separate line to make sure that it's visible. +</para> + +<para> +LINE 3: WHY INDEX ITERATOR". Our job is to go through all the rows and there are two ways +to do it: with <olink targetptr="box.select_range">box.select_range()</olink> or with +<olink targetptr="box.index.iterator">index[].iterator</olink>. We preferred +index[].iterator because it works regardless of the index type, that is, +it works with HASH, TREE, and BITSET indexes. +</para> + +<para> +LINE 3: MEANING. The value zero is hard-coded so this will only work for space[0] +and index[0] -- we're making some hopeful assumptions here. The meaning is "variable <code>v</code> gets +the iterator for the primary index of the first space". +</para> + +<para> +LINE 4: START THE MAIN LOOP. Everything inside this "<code>for</code>" loop will be repeated +as long as there is another index key. A tuple is fetched and can be referenced +with variable <code>t</code>. +</para> + +<para> +LINE 5: WHY "PCALL". If we simply said "<code>lua_table = box.cjson.decode(t[1]))</code>", +then the function would abort with an error if it encountered something wrong +with the JSON string -- a missing colon, for example. By putting the function +inside "<code>pcall</code>" (<link xlink:href="http://www.lua.org/pil/8.4.html">protected call</link>), we're saying: we want to intercept that sort +of error, so if there's a problem just set <code>is_valid_json = false</code> and we +will know what to do about it later. +</para> + +<para> +LINE 5: MEANING. The function is <olink targetptr="box.cjson">box.cjson.decode</olink> which means decode a JSON +string, and the parameter is <code>t[1]</code> which is a reference to a JSON string. +Once again there's a bit of hard coding here, we're assuming that the second +field in the tuple is where the JSON string was inserted. For example, we're assuming a tuple looks like <programlisting>field[0]: 444 +field[1]: '{"Hello": "world", "Quantity": 15}' +</programlisting>meaning that the tuple's first field, the primary key field, is a number +while the tuple's second field, the JSON string, is a string. Thus the +entire statement means "decode <code>t[1]</code> (the tuple's second field) as a JSON +string; if there's an error set <code>is_valid_json = false</code>; if there's no error +set <code>is_valid_json = true</code> and set <code>lua_table</code> = a Lua table which has the +decoded string". +</para> + +<para> +LINE 6. This "<code>if</code>" statement means "if the <code>box.cjson.decode</code> function failed, +don't execute the next indented lines", so <code>sum</code> will be unchanged if +<code>box.cjson.decode</code> failed. Although "<code>if is_valid_json == true</code>" would be clearer, the +usual style is to say "<code>if is_valid_json</code>" and let "<code>== true</code>" be assumed. +</para> + +<para> +LINE 7. At last we are ready to get the JSON field value from the Lua +table that came from the JSON string. +The value in <code>field_name</code>, which is the parameter for the whole function, +must be a name of a JSON field. For example, inside the JSON string +'{"Hello": "world", "Quantity": 15}', there are two JSON fields: "Hello" +and "Quantity". If the whole function is invoked with <code>sum_json_field("Quantity")</code>, +then <code>field_value = lua_table[field_name]</code> is effectively the same as +<code>field_value = lua_table["Quantity"]</code> or even <code>field_value = lua_table.Quantity</code>. +Those are just three different ways of saying: for the Quantity field +in the Lua table, get the value and put it in variable <code>field_value</code>. +</para> + +<para> +LINE 8: WHY "IF". Suppose that the JSON string is well formed but the +JSON field is not a number, or is missing. In that case, the function +would be aborted when there was an attempt to add it to the sum. +By first checking <code>type(field_value) == "number"</code>, we avoid that abortion. +Again, as in line 5, this is slightly paranoid -- anyone who knows +that the database is in perfect shape can skip this kind of thing. +Incidentally the "<code>if ... end</code>" statement is so short that it fits on +a single line, which is acceptable but optional practice. +</para> + +<para> +LINE 8: MEANING. The meat, the whole reason for the function's existence, +is in the words "<code>sum = sum + field_value</code>". This addition of <code>field_value</code> +to <code>sum</code> will happen for every tuple, provided the field is there and is +numeric. +</para> + +<para> +LINE 9. This "<code>end</code>" statement matches the "<code>if is_valid_json</code>" statement +in line 6. +</para> + +<para> +LINE 10. This "<code>end</code>" statement matches the "<code>for t in v do</code>" statement +in line 4. The effect is that another iteration of the loop will take +place, unless there are no more tuples. +</para> + +<para> +LINE 11: This is after the end of the "<code>for t in v do</code>" loop. Return <code>sum</code> to the caller. +This effectively ends the execution of the whole function, so all the +local variables are destroyed and the function's caller gets the result. +</para> + +<para> +LINE 12: This "<code>end</code>" statement matches the start of the function. +</para> + +<para> +And the function is complete. Time to test it. +Starting with an empty database, defined the same way as the +sandbox database that was introduced in +<olink +targetptr="getting-started-start-stop"><quote>Starting Tarantool and making your first database</quote></olink>, +add some tuples where the first field is a number and the second field is a string. +</para> +<programlisting> +INSERT INTO t0 VALUES (444,'{"Item": "widget", "Quantity": 15}') +INSERT INTO t0 VALUES (445,'{"Item": "widget", "Quantity": 7}') +INSERT INTO t0 VALUES (446,'{"Item": "golf club", "Quantity": "sunshine"}') +INSERT INTO t0 VALUES (447,'{"Item": "waffle iron", "Quantit": 3}') +</programlisting> +<para> +Since this is a test, there are deliberate errors. The "golf club" and +the "waffle iron" do not have numeric Quantity fields, so must be ignored. +Therefore the real sum of the Quantity field in the JSON strings should be: +15 + 7 = 22. +</para> + +<para> +Invoke the function with either <code>CALL sum_json_field("Quantity")</code> or +<code>lua sum_json_field("Quantity")</code>. +<programlisting language="lua"> +<prompt>localhost></prompt> <userinput>lua sum_json_field("Quantity")</userinput> +--- + - 22 +... +</programlisting> +</para> + +<para> +It works. We'll just leave, as exercises for future improvement, the possibility +that the "hard coding" assumptions could be removed, that there might have to be +an overflow check if some field values are huge, and that the function should +contain a "yield" instruction if the count of tuples is huge. +</para> + +<para> +What has been shown is that a 12-line Lua function can scan a database and +process JSON strings, in a way that's useful, robust, and -- now that +this tutorial exercise is over -- understandable. +</para> + +</section> + </appendix> <!-- diff --git a/doc/user/persistence-architecture.xml b/doc/user/persistence-architecture.xml index bcb4e57ab6e7b4ce9ca215bfa2b9f99b890f0d9c..4a7335799d7ede7cd416abfcd32a64fdb1b478e9 100644 --- a/doc/user/persistence-architecture.xml +++ b/doc/user/persistence-architecture.xml @@ -30,24 +30,24 @@ On the left are the hexadecimal bytes that one would see with <programlisting><prompt>$ </prompt><userinput>hexdump work_dir/00000000000000000002.xlog</userinput></programlisting> and on the right are comments. <programlisting> -Hex dump of WAL file Comment --------------------- ------- -58 4c 4f 47 0a File header: "XLOG\n" -30 2e 31 31 0a 0a File header: "0.11\n\n" = version -ed ab 0b ba Magic row marker always = 0xba0babed for version 0.11 -10 2a 1b 5e Record header: crc32 checksum of header fields -02 00 00 00 00 00 00 00 Record header: 2 = (64-bit int) log sequence number -99 c8 f1 d9 32 a8 d4 41 Record header: (double) unix time-since-epoch for transaction -1d 00 00 00 Record header: 29 = length of rest of record, non-inclusive -60 45 94 cd Record header: crc32 checksum of data fields -fe ff Tag = XLOG which is #defined as 65534 -02 00 99 f9 7f 00 00 01 Session cookie, comes from struct sockaddr_in *addr -0d 00 00 00 Data change request code = insert which is #defined as 13 -00 00 Space number = 0 -02 00 00 00 Flags = BOX_ADD which is defined as 2 -01 00 00 00 Tuple: Count of fields in tuple = 1 -04 Tuple: field[0] length = 4 because value is 4 bytes long -01 00 00 00 Tuple: field[0] value = 1 +Hex dump of WAL file Comment +-------------------- ------- +58 4c 4f 47 0a File header: "XLOG\n" +30 2e 31 31 0a 0a File header: "0.11\n\n" = version +ed ab 0b ba Magic row marker always = 0xba0babed if version 0.11 +10 2a 1b 5e Record header: crc32 checksum of header fields +02 00 00 00 00 00 00 00 Record header: 2 = (64-bit int) log sequence number +99 c8 f1 d9 32 a8 d4 41 Record header: UNIX time-since-epoch for transaction +1d 00 00 00 Record header: 29 = length of rest of record +60 45 94 cd Record header: crc32 checksum of data fields +fe ff Tag = XLOG which is #defined as 65534 +02 00 99 f9 7f 00 00 01 Session cookie, comes from struct sockaddr_in *addr +0d 00 00 00 Data change request code = insert. #defined as 13 +00 00 Space number = 0 +02 00 00 00 Flags = BOX_ADD which is defined as 2 +01 00 00 00 Tuple: Count of fields in tuple = 1 +04 Tuple: field[0] length = 4 (value is 4 bytes long) +01 00 00 00 Tuple: field[0] value = 1 </programlisting> </para> diff --git a/doc/user/plugins.xml b/doc/user/plugins.xml index 661b86b6e1f217502f3867b712ef2aab5b78ec04..4c36b7960daa67a811ecbaafe557c4bc4ffae3bd 100644 --- a/doc/user/plugins.xml +++ b/doc/user/plugins.xml @@ -74,7 +74,7 @@ OK # Check that the mysql client can connect using some factory defaults: # port = 3306, user = 'root', user password = '', database = 'test'. # These can be changed, provided one changes them in all places. -<prompt>$ </prompt><userinput>~/mysql-5.5/bin/mysql --port=3306 --host=127.0.0.1 --user=root --database=test</userinput> +<prompt>$ </prompt><userinput>~/mysql-5.5/bin/mysql --port=3306 -h 127.0.0.1 --user=root --database=test</userinput> Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 25 Server version: 5.5.35 MySQL Community Server (GPL) @@ -97,7 +97,8 @@ Bye <prompt>$ </prompt><userinput>cd ~/tarantool-stable</userinput> <prompt>$ </prompt><userinput>make clean</userinput> <prompt>$ </prompt><userinput>rm CMakeCache.txt</userinput> -<prompt>$ </prompt><userinput>cmake . -DWITH_MYSQL=on -DMYSQL_INCLUDE_DIR=~/mysql-5.5/include -DMYSQL_LIBRARIES=~/mysql-5.5/lib/libmysqlclient.so -DENABLE_CLIENT=true</userinput> +<prompt>$ </prompt><userinput>cmake . -DWITH_MYSQL=on -DMYSQL_INCLUDE_DIR=~/mysql-5.5/include\</userinput> +<prompt>> </prompt><userinput> -DMYSQL_LIBRARIES=~/mysql-5.5/lib/libmysqlclient.so -DENABLE_CLIENT=true</userinput> ... -- Found MySQL includes: ~/mysql-5.5/include/mysql.h -- Found MySQL library: ~/mysql-5.5/lib/libmysqlclient.so @@ -127,8 +128,8 @@ Linking CXX shared library libmysql.so # So it's possible to see the message that the plugin was loaded. <prompt>$ </prompt><userinput>~/tarantool-stable/src/box/tarantool_box&</userinput> 2013-12-03 17:46:16.239 [12957] 1/sched C> version 1.5.1-271-g610930e -2013-12-03 17:46:16.241 [12957] 1/sched I> Loading plugin: ~/tarantool-stable/src/plugin/mysql/libmysql.so -2013-12-03 17:46:16.242 [12957] 1/sched I> Plugin 'mysql' was loaded, version: 1 +... Loading plugin: ~/tarantool-stable/src/plugin/mysql/libmysql.so ... +... Plugin 'mysql' was loaded, version: 1 ... 2013-12-03 17:46:16.244 [12957] 1/sched C> entering event loop @@ -149,7 +150,8 @@ plugins: # Lua tutorial in the Tarantool user manual. <prompt>localhost> </prompt><userinput>SETOPT delimiter = '!'</userinput> <prompt>localhost> </prompt><userinput>lua function mysql_select ()</userinput> - <prompt>-> </prompt><userinput> local dbh = box.net.sql.connect('mysql', '127.0.0.1', 3306, 'root', '', 'test')</userinput> + <prompt>-> </prompt><userinput> local dbh = box.net.sql.connect(</userinput> + <prompt>-> </prompt><userinput> 'mysql', '127.0.0.1', 3306, 'root', '', 'test')</userinput> <prompt>-> </prompt><userinput> local test = dbh:select('SELECT * FROM test WHERE s1 = 1')</userinput> <prompt>-> </prompt><userinput> for i, card in pairs(test) do</userinput> <prompt>-> </prompt><userinput> print(card.s2)</userinput> @@ -189,7 +191,8 @@ on the local host 127.0.0.1. </para> <programlisting> -# Check that the include subdirectory exists by looking for /usr/include/postgresql/libpq-fe-h. +# Check that the include subdirectory exists +# by looking for /usr/include/postgresql/libpq-fe-h. <prompt>$ </prompt><userinput>[ -f /usr/include/postgresql/libpq-fe.h ] && echo "OK" || echo "Error"</userinput> OK @@ -220,7 +223,8 @@ INSERT 0 1 <prompt>$ </prompt><userinput>cd ~/tarantool-stable</userinput> <prompt>$ </prompt><userinput>make clean</userinput> <prompt>$ </prompt><userinput>rm CMakeCache.txt</userinput> -<prompt>$ </prompt><userinput>cmake . -DWITH_POSTGRESQL=on -DPostgreSQL_LIBRARY=/usr/lib/libpq.so -DPostgreSQL_INCLUDE_DIR=/usr/include/postgresql -DENABLE_CLIENT=true</userinput> +<prompt>$ </prompt><userinput>cmake . -DWITH_POSTGRESQL=on -DPostgreSQL_LIBRARY=/usr/lib/libpq.so\</userinput> +<prompt>> </prompt><userinput> -DPostgreSQL_INCLUDE_DIR=/usr/include/postgresql -DENABLE_CLIENT=true</userinput> ... -- Found PostgreSQL: /usr/lib/libpq.so -- box.net.sql(pg): INC=/usr/include/postgresql;/usr/include/postgresql @@ -249,8 +253,8 @@ Linking CXX shared library libpg.so # So it's possible to see the message that the plugin was loaded. <prompt>$ </prompt><userinput>~/tarantool-stable/src/box/tarantool_box&</userinput> 2013-12-06 13:01:51.518 [23978] 1/sched C> version 1.5.1-290-g45b93e7 -2013-12-06 13:01:51.520 [23978] 1/sched I> Loading plugin: ~/tarantool-stable/src/plugin/pg/libpg.so -2013-12-06 13:01:51.527 [23978] 1/sched I> Plugin 'postgresql' was loaded, version: 1 +... Loading plugin: ~/tarantool-stable/src/plugin/pg/libpg.so ... +... Plugin 'postgresql' was loaded, version: 1 ... 2013-12-06 13:01:51.531 [23978] 1/sched C> entering event loop @@ -271,7 +275,8 @@ plugins: # Lua tutorial in the Tarantool user manual. <prompt>localhost> </prompt><userinput>SETOPT delimiter = '!'</userinput> <prompt>localhost> </prompt><userinput>lua function postgresql_select ()</userinput> - <prompt>-> </prompt><userinput> local dbh = box.net.sql.connect('pg', '127.0.0.1', 5432, 'postgres', 'postgres', 'postgres')</userinput> + <prompt>-> </prompt><userinput> local dbh = box.net.sql.connect(</userinput> + <prompt>-> </prompt><userinput> 'pg', '127.0.0.1', 5432, 'postgres', 'postgres', 'postgres')</userinput> <prompt>-> </prompt><userinput> local test = dbh:select('SELECT * FROM test WHERE s1 = 1')</userinput> <prompt>-> </prompt><userinput> for i, card in pairs(test) do</userinput> <prompt>-> </prompt><userinput> print(card.s2)</userinput> diff --git a/doc/user/server-administration.xml b/doc/user/server-administration.xml index d69a91f478e4e046aed40ac3a9b9e2cab20929a5..dea506eb9c8a27ea367f9a41b43b0bdd088ef6b8 100644 --- a/doc/user/server-administration.xml +++ b/doc/user/server-administration.xml @@ -468,12 +468,17 @@ to print write-ahead-log contents, or to send write-ahead-log contents to the server. Here is an example of a print-and-play-mode tarantool client session: <programlisting> -<prompt>$ </prompt>tarantool --cat /home/user1/tarantool_test/work_dir/00000000000000000005.xlog --from 22 --to 26 -Insert, lsn: 22, time: 1385327353.345869, len: 33, space: 0, cookie: 127.0.0.1:44787 ['X-1', 100] -Insert, lsn: 23, time: 1385327353.346745, len: 42, space: 0, cookie: 127.0.0.1:44787 ['X-2', 200, 8243105135088135759] -Insert, lsn: 24, time: 1385327353.347352, len: 34, space: 0, cookie: 127.0.0.1:44787 ['X-3', 300, ''] -Update, lsn: 25, time: 1385327353.348209, len: 42, space: 0, cookie: 127.0.0.1:44787 ['X-1'] -Delete, lsn: 26, time: 1385327353.348879, len: 28, space: 0, cookie: 127.0.0.1:44787 ['X-2'] +<prompt>$ </prompt>tarantool --cat 00000000000000000005.xlog --from 22 --to 26 +Insert, lsn: 22, time: 1385327353.345869, len: 33, space: 0, + cookie: 127.0.0.1:44787 ['X-1', 100] +Insert, lsn: 23, time: 1385327353.346745, len: 42, space: 0, + cookie: 127.0.0.1:44787 ['X-2', 200, 8243105135088135759] +Insert, lsn: 24, time: 1385327353.347352, len: 34, space: 0, + cookie: 127.0.0.1:44787 ['X-3', 300, ''] +Update, lsn: 25, time: 1385327353.348209, len: 42, space: 0, + cookie: 127.0.0.1:44787 ['X-1'] +Delete, lsn: 26, time: 1385327353.348879, len: 28, space: 0, + cookie: 127.0.0.1:44787 ['X-2'] <prompt>$ </prompt> </programlisting> </para> @@ -571,7 +576,7 @@ Taking snapshots with tarantar, instead of with SAVE SNAPSHOT, can be better because: <itemizedlist> <listitem><para>tarantar can work even if the tarantool_box server is down - because it works from the existing .xnap and .xlog files, + because it works from the existing .snap and .xlog files, rather than from an in-memory database.</para></listitem> <listitem><para>tarantar saves memory when constructing its own in-memory index to the rows by making SHA-1 hashes for primary keys @@ -720,21 +725,21 @@ tarantool_deploy.sh: done <section xml:id="rpm-based-distros"> <title>Fedora, RHEL, CentOS</title> <para> - tba + TBA </para> </section> <section xml:id="FreeBSD"> <title>FreeBSD</title> <para> - tba + TBA </para> </section> <section xml:id="mac-os-x"> <title>Mac OS X</title> <para> - tba + TBA </para> </section> diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index 6eddd658842450f7bc748ee34eeb48bf0ee3480b..892a609820c1cf9941b08e3cf869a38bda9d7547 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -6,7 +6,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="stored-procedures"> - <title>Writing stored procedures in Lua</title> + <title>Writing stored procedures in Lua</title> <blockquote> <para> <link xlink:href="http://www.lua.org">Lua</link> @@ -31,10 +31,9 @@ operations</link> and <link xlink:href="#tonumber64">64-bit integer arithmetic.</link> </para> </blockquote> -<para> - Procedures can be invoked from the administrative - console and using the binary protocol, for example: - <programlisting><computeroutput>localhost> <userinput>lua function f1() return 'hello' end</userinput> + <para> + Procedures can be defined and invoked interactively, for example:: + <programlisting><computeroutput>localhost> <userinput>lua function f1() return 'hello' end</userinput> --- ... localhost> <userinput>call f1()</userinput> @@ -42,41 +41,47 @@ Call OK, 1 rows affected ['hello'] </computeroutput> </programlisting> - In the language of the administrative console - <olink targetptr="lua-command" /> evaluates an arbitrary - Lua chunk. CALL is an SQL standard statement, so its syntax - was adopted by Tarantool command line client - to invoke the CALL command of the binary protocol. -</para> -<para> - In the example above, "<code>lua function f1() return 'hello' end</code>" defines a Lua procedure - using the text protocol of the administrative port, - and "<code>call f1()</code>" invokes the procedure using the Tarantool client-side SQL - parser plus the binary protocol on the <olink targetptr="primary_port" />. - Since it's possible to execute any Lua chunk in the - administrative console, the newly created <code - language="Lua">function f1()</code> - can be invoked there too: + In the above example, the word <olink targetptr="lua-command"><emphasis>lua</emphasis></olink> + is a signal to send the request, via the <olink targetptr="admin_port">administrative port</olink>, + using the text protocol, for evaluation as a chunk of Lua code. + The word <link linkend="utility-tarantool-call"><emphasis>call</emphasis></link> + is a signal to send the request, via the <olink targetptr="primary_port">primary port</olink>, + using the binary protocol, for execution of a previously-defined procedure. + (CALL is an SQL standard statement so its syntax + was adopted by the Tarantool command-line client.) + </para> + <para> + Thus, "<code>lua function f1() return 'hello' end</code>" sends over + the administrative port as text, and causes definition of the procedure. + Then "<code>call f1()</code>" uses the client-side SQL parser to convert + to binary, sends over the primary port as binary, and causes execution of + the procedure. + </para> + <para> + Since it's possible to execute any Lua chunk via the + administrative port, one can say <code language="Lua">lua f1()</code> + to invoke the function, instead of <code>call f1()</code> ... <programlisting><computeroutput>localhost> <userinput>lua f1()</userinput> --- - hello ... -localhost> <userinput>lua 1+2</userinput> +localhost> <userinput>lua 1 + 2</userinput> --- - 3 ... -localhost> <userinput>lua "hello".." world"</userinput> +localhost> <userinput>lua 'hello' .. ' world' -- '..' means 'concatenate'</userinput> --- - hello world ... </computeroutput></programlisting> - </para> - <para> - Lua procedures could also be called at the time of initialization - using a dedicated <filename xml:id="init.lua" xreflabel="init.lua">init.lua</filename> script, - located in <olink targetptr="script_dir" />. + </para> + <para> + Lua procedures could also be called at the time of initialization + using a dedicated <filename xml:id="init.lua" xreflabel="init.lua">init.lua</filename> script, + located in + <olink targetptr="script_dir"><filename>script_dir</filename></olink>. - An example of such a script is given below: + An example of such a script is given below: <programlisting> <![CDATA[ -- Importing expirationd module @@ -109,66 +114,69 @@ expirationd.run_task("exprd space 0", 0, is_expired, purge, { field_no = 1, ttl = 30 * 60 }) ]]> </programlisting> - </para> - <para> - The initialization script can select and modify data. However, - if the server is a running replica, data change requests from - the start script fail just the same way they would fail if they - were sent from a remote client. - </para> - <para> - Another common task to perform in the initialization script - is to start background fibers for data expiration, re-sharding, - or communication with networked peers. - </para> - <para> - Finally, the script can be used to define Lua <olink - targetptr="triggers">triggers</olink> invoked on various events - within the system. - </para> - <para> - There is a single global instance of the Lua interpreter, which is - shared across all connections. Anything prefixed with - <code>lua </code> on the administrative console is sent - directly to this interpreter. Any change of the interpreter - state is immediately available to all client connections. - </para> - <para> - Each connection, however, is using its own Lua - <emphasis>coroutine</emphasis> — a mechanism akin to - Tarantool <emphasis>fibers</emphasis>. A coroutine has an - own execution stack and a Lua <emphasis>closure</emphasis> - — set of local variables and definitions. - </para> - <para> - The interpreter environment is not restricted when - <olink targetptr="init.lua"/> is loaded. But before the - server starts accepting requests, the standard Lua APIs, such - as for file I/O, process control and module management are - unset, to avoid possible trivial security attacks. - </para> - <para> - In the binary protocol, it's only possible to <emphasis - role="strong">call</emphasis> existing - procedures, but not <emphasis role="strong">define</emphasis> - or <emphasis role="strong">alter</emphasis> them. The - CALL request packet contains the command code for CALL (22), the name - of a procedure to be called, and a tuple for procedure - arguments. Currently, Tarantool tuples are type-agnostic, - thus each field of the tuple is passed into the procedure - as an argument of type <quote>string</quote>. For example: -<programlisting><computeroutput>kostja@atlas:~$ cat arg.lua + </para> + <para> + The initialization script can select and modify data. However, + if the server is a running replica, data change requests from + the start script fail just the same way they would fail if they + were sent from a remote client. + </para> + <para> + Another common task to perform in the initialization script + is to start background fibers for data expiration, re-sharding, + or communication with networked peers. + </para> + <para> + Finally, the script can be used to define Lua <olink + targetptr="triggers">triggers</olink> invoked on various events + within the system. + </para> + <para> + There is a single global instance of the Lua interpreter, which is + shared across all connections. Anything prefixed with + <code>lua </code> on the administrative console is sent + directly to this interpreter. Any changes of the interpreter + state, including global variable values, have immediate + effect on all client connections. + </para> + <para> + However, each connection uses its own Lua + <emphasis>coroutine</emphasis> — a mechanism akin to a + Tarantool <emphasis>fiber</emphasis>. A coroutine has its + own execution stack and its own set of local variables and + definitions, as described in the + <link xlink:href="http://www.lua.org/pil/6.1.html"> + <emphasis>closure</emphasis> section</link> of the Lua manual. + </para> + <para> + The interpreter environment is not restricted when + <olink targetptr="init.lua"/> is loaded. But before the + server starts accepting requests, it disables access to + some of the standard Lua APIs for file I/O, process control, + and module management. This prevents trivial security attacks. + </para> + <para> + With the binary protocol, it's only possible to <emphasis + role="strong">call</emphasis> existing + procedures, but not <emphasis role="strong">define</emphasis> + or <emphasis role="strong">alter</emphasis> them. The + CALL request packet contains the command code for CALL (22), the name + of a procedure to be called, and a tuple for procedure + arguments. Currently, Tarantool tuples are type-agnostic, + thus each field of the tuple is passed into the procedure + as an argument of type <quote>string</quote>. For example: +<programlisting><computeroutput>kostja@atlas:~<prompt>$</prompt> <userinput>cat arg.lua</userinput> function f1(a) local s = a if type(a) == 'string' then s = '' - for i=1, #a, 1 do + for i = 1, #a, 1 do s = s..string.format('0x%x ', string.byte(a, i)) end end return type(a), s end -kostja@atlas:~$ tarantool +kostja@atlas:~<prompt>$</prompt> <userinput>tarantool</userinput> localhost> <userinput>lua dofile('arg.lua')</userinput> --- ... @@ -190,46 +198,38 @@ localhost> <userinput>call f1(1234)</userinput> Call OK, 2 rows affected ['string'] ['0xd2 0x4 0x0 0x0 ']</computeroutput></programlisting> - In the above example, the way the procedure receives its - argument is identical in the two protocols, when the argument is a - string. A numeric field, however, when submitted via the - binary protocol, is seen by the procedure as - a 4-byte blob, not as a Lua <quote>number</quote> type. + In the above example, the way the procedure receives its + argument is identical in the two protocols, when the argument is a + string. A numeric field, however, when submitted via the + binary protocol, is seen by the procedure as + a 4-byte blob, not as a Lua <quote>number</quote> type. </para> - <para>In addition to conventional method invocation, - Lua provides object-oriented syntax. Access to the latter is - available on the administrative console only: + <para> + In addition to conventional method invocation, + Lua provides object-oriented syntax. Use of object-oriented + syntax is possible on the administrative port only: <programlisting><computeroutput>localhost> <userinput>lua box.space[0]:truncate()</userinput> --- ... localhost> <userinput>call box.space[0]:truncate()</userinput> error: 1:15 expected '(' </computeroutput></programlisting> - Since it's impossible to invoke object methods from the binary - protocol, the object-oriented syntax is often used to restrict - certain operations to be used by a system administrator only. - </para> - <para> - Every value, returned from a stored function by means of a - <code>return</code> clause, is converted to a Tarantool tuple. - Tuples are returned as such, in binary form; a Lua scalar, such as - a string or an integer, is converted to a tuple with only - one field. When the returned value is a <emphasis>Lua - table</emphasis>, the resulting tuple contains only table - values, but not keys. - </para> - <para> - When a function in Lua terminates with an error, the error - is sent to the client as <olink targetptr="ER_PROC_LUA" /> - return code, with the original error message preserved. - Similarly, an error which has occurred inside Tarantool (observed on the - client as an error code), when it happens during execution of a - Lua procedure, produces a genuine Lua error: -<programlisting><computeroutput>localhost> <userinput>lua function f1() error("oops") end</userinput> + Since it's impossible to invoke object methods from the binary + protocol, the object-oriented syntax is often used to restrict + certain operations to be used by a system administrator only. + </para> + <para> + When a function in Lua terminates with an error, the error + is sent to the client as <olink targetptr="ER_PROC_LUA" /> + return code, along with the original error message. + Similarly, an error which has occurred inside Tarantool (observed on the + client as an error code), when it happens during execution of a + Lua procedure, produces a genuine Lua error: +<programlisting><computeroutput>localhost> <userinput>lua function f()error('!') end</userinput> --- ... -localhost> <userinput>call f1()</userinput> -Call ERROR, Lua error: [string "function f1() error("oops") end"]:1: oops (ER_PROC_LUA) +localhost> <userinput>call f()</userinput> +Call ERROR, Lua error: [string "function f()error('!') end"]:1: ! (ER_PROC_LUA) localhost> <userinput>call box.insert('99', 1, 'test')</userinput> Call ERROR, Space 99 is disabled (ER_SPACE_DISABLED) localhost> <userinput>lua pcall(box.insert, 99, 1, 'test')</userinput> @@ -238,75 +238,39 @@ localhost> <userinput>lua pcall(box.insert, 99, 1, 'test')</userinput> - Space 99 is disabled ... </computeroutput></programlisting> - </para> - <para> - It's possible not only to invoke trivial Lua code, but call - into Tarantool storage functionality, using the - <code>box</code> - Lua library. The contents of the library can be - inspected at runtime: -<programlisting><computeroutput>localhost> <userinput>lua for k, v in pairs(box) do print(k, ": ", type(v)) end</userinput> ---- -fiber: table -space: table -cfg: table -on_reload_configuration: function -update: function -process: function -delete: function -insert: function -select: function -index: table -unpack: function -replace: function -select_range: function -pack: function -...</computeroutput></programlisting> - As is shown in the listing, the <code>box</code> package contains: - <itemizedlist> - <listitem><para> - high-level functions, such as - <code>process(), update(), select(), select_range(), insert(), - replace(), delete()</code>, to manipulate - tuples and access spaces from Lua. - </para></listitem> - <listitem><para> - libraries, such as <code>cfg, space, fiber, index, tuple</code>, - to access server configuration, create, resume and - interrupt fibers, inspect contents of spaces, indexes - and tuples, send and receive data over the network. - </para></listitem> - </itemizedlist> - </para> + </para> <variablelist> - <title>Global Lua names added by Tarantool</title> + <title>Lua function <code>tonumber64</code></title> <varlistentry> - <term xml:id="tonumber64" xreflabel="tonumber64"> <emphasis role="lua">tonumber64(value)</emphasis></term> + <term xml:id="tonumber64" xreflabel="tonumber64"> <emphasis role="lua">tonumber64(<replaceable>value</replaceable>)</emphasis></term> <listitem> - <para>Convert a given string or a Lua number to a - 64-bit integer. The returned value supports all - arithmetic operations, but uses - 64-bit integer arithmetic, rather than floating-point - arithmetic as in the built-in number type. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + Convert a string or a Lua number to a + 64-bit integer. The result can be used in arithmetic, + and the arithmetic will be 64-bit integer arithmetic + rather than floating-point arithmetic. (Operations on + an unconverted Lua number use floating-point arithmetic.) + The tonumber64() function is added by Tarantool; the name is global. + <bridgehead renderas="sect4">Example</bridgehead> <programlisting> -localhost> <userinput>lua tonumber64('123456789'), tonumber64(123456789)</userinput> +localhost> <userinput>lua 123456789012345, tonumber64(123456789012345)</userinput> --- - - 123456789 - - 123456789 + - 1.2345678901234e+14 + - 123456789012345 ... -localhost> <userinput>lua i=tonumber64(1)</userinput> +localhost> <userinput>lua i = tonumber64('1000000000')</userinput> --- ... -localhost> <userinput>lua type(i), type(i*2), type(i/2), i, i*2, i/2</userinput> +localhost> <userinput>lua type(i), i / 2, i - 2, i * 2, i + 2, i % 2, i ^ 2</userinput> --- - cdata - - cdata - - cdata - - 1 - - 2 + - 500000000 + - 999999998 + - 2000000000 + - 1000000002 - 0 + - 1000000000000000000 ... </programlisting> </para> @@ -314,12 +278,61 @@ localhost> <userinput>lua type(i), type(i*2), type(i/2), i, i*2, i/2</userinput </varlistentry> </variablelist> +<section xml:id="sp-box-library"> + <title>The <code>box</code> library</title> + + <para> + As well as executing Lua chunks or defining their own functions, + users can exploit the Tarantool server's storage functionality + with the <code>box</code> Lua library. + </para> + + <para> + <bridgehead renderas="sect4">Packages of the box library</bridgehead> + + The contents of the <code>box</code> library can be inspected at runtime with + <code>lua for k, v in pairs(box) do print(k, ': ', type(v)) end</code>. + The packages inside the box library are: box, box.tuple, box.cjson, box.space, box.index, box.fiber, + box.session, box.ipc, box.socket, box.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 manipulation (insert delete update select replace), + creating or resuming or interrupting fibers, + introspection (inspecting contents of spaces, accessing server configuration), + and sending or receiving data over a network. + </para> + + <para> + <table> + <title xml:id="function-types">Possible types of the values that a function in the box library can return</title> + <tgroup cols="4" align="left" colsep="1" rowsep="1"> + <thead> + <row><entry>General type</entry> <entry>Specific type</entry><entry>What Lua type() would return</entry> <entry>Example</entry></row> + </thead> + <tbody> + <row><entry xml:id="function-type-number">scalar </entry><entry>number</entry> <entry><link xlink:href="http://www.lua.org/pil/2.3.html">"number"</link></entry> <entry>12345</entry></row> + <row><entry xml:id="function-type-string">scalar </entry><entry>string</entry> <entry><link xlink:href="http://www.lua.org/pil/2.4.html">"string"</link></entry> <entry>'A B C'</entry></row> + <row><entry xml:id="function-type-nil">scalar </entry><entry>nil</entry> <entry><link xlink:href="http://www.lua.org/pil/2.1.html">"nil"</link></entry> <entry>nil</entry></row> + <row><entry xml:id="function-type-lua-table">compound</entry><entry>Lua table</entry> <entry><link xlink:href="http://www.lua.org/pil/2.5.html">"table"</link></entry> <entry>table: 0x410f8b10</entry></row> + <row><entry xml:id="function-type-tuple">compound </entry><entry>tuple</entry> <entry><link xlink:href="http://www.lua.org/pil/28.1.html">"Userdata"</link></entry><entry>12345: {'A B C'}</entry></row> + </tbody> + </tgroup> + </table> + A tuple is returned in binary format (like <code>12345: {'A B C'}</code>) if + the function is called via the administrative port; a tuple is returned in + YAML format (like <code>[12345, 'A B C']</code>) if the function is called + from the primary port; a few functions may return multiple tuples; + for more tuple examples see <code xlink:href="#box.tuple">box.tuple</code>. + A scalar may be converted to a tuple with only one field. + A Lua table may contain all of a tuple's fields except the "key" (the primary-key fields). + </para> +</section> + <section xml:id="sp-box"> <title>Package <code>box</code></title> <variablelist xml:id="box" xreflabel="box"> <varlistentry> <term> - <emphasis role="lua" xml:id="box.process">box.process(op, request)</emphasis> + <emphasis role="lua" xml:id="box.process">box.process(<replaceable>request-code, request-string</replaceable>)</emphasis> </term> <listitem> <para> @@ -329,7 +342,7 @@ localhost> <userinput>lua type(i), type(i*2), type(i/2), i, i*2, i/2</userinput select and delete tuples from within a Lua procedure. </para> <para> - The <code>box.process</code> API is a low-level API, and it expects + The <code>box.process</code> function is a low-level function, and it expects all arguments to be packed in accordance with the binary protocol (excluding the iproto header). Normally, there is no need @@ -338,38 +351,29 @@ localhost> <userinput>lua type(i), type(i*2), type(i/2), i, i*2, i/2</userinput and other convenience wrappers invoke <code>box.process()</code> with correctly packed arguments. - <bridgehead renderas="sect4">Parameters</bridgehead> - <simplelist> - <member><code>op</code> — number, any + </para> + <para> + Parameters: <code>request-code</code> — (type = number), any Tarantool command code, except 22 (CALL). See <link xlink:href="https://github.com/tarantool/tarantool/blob/master/doc/box-protocol.txt"> <filename>doc/box-protocol.txt</filename></link>. - </member> - <member><code>request</code> — command - arguments packed in binary format.</member> - </simplelist> - <bridgehead renderas="sect4">Returns</bridgehead> - This function returns zero or more tuples. In Lua, a - tuple is represented by a - <emphasis>userdata</emphasis> object of type - <code xlink:href="#box.tuple">box.tuple</code>. If - a Lua procedure is called from the administrative - console, returned tuples are printed out in YAML - format. When called from the binary - protocol, the binary format is used. - <bridgehead renderas="sect4">Errors</bridgehead> - Any server error produced by the executed + <code>request-string</code> — command + arguments packed in binary format. + </para> + <para> + Returns: (type = <link xlink:href="#function-types">tuple</link>) zero or more tuples. + </para> + <para> + Possible Errors: Any server error produced by the executed command. </para> <para> - Please note that, since all requests from Lua - enter the core through <emphasis + Please note that, since all the Lua data-manipulation functions + enter the server core through <emphasis role="lua">box.process()</emphasis>, all checks and triggers run by the core automatically apply. For example, if the server is in read-only mode, - an update or delete fails. Analogously, if a - system-wide "instead of" trigger is defined, it - is run. + an update or delete will fail. </para> </listitem> </varlistentry> @@ -377,43 +381,42 @@ localhost> <userinput>lua type(i), type(i*2), type(i/2), i, i*2, i/2</userinput <varlistentry> <term> <emphasis role="lua" xml:id="box.select" xreflabel="box.select"> - box.select(space_no, index_no, ...) + box.select(<replaceable>space-number, index-number, field-value [, field-value ...]</replaceable>) </emphasis> </term> <listitem> <para> Search for a tuple or tuples in the given space. A wrapper around <code>box.process()</code>. - <bridgehead renderas="sect4">Parameters</bridgehead> - <simplelist> - <member><code>space_no</code> — space id, - </member> - <member><code>index_no</code> — index number in the - space, to be used for match</member> - <member><code>...</code>— index key, - possibly multipart. - </member> - </simplelist> - <bridgehead renderas="sect4">Returns</bridgehead> - Returns zero or more tuples. - <bridgehead renderas="sect4">Errors</bridgehead> - Same as in <code>box.process()</code>. Any error + </para> + <para> + Parameters: <code>space-number</code> — = space id, + <code>index-number</code> — = index number in the + space, to be used for matching, + <code>field-value(s)</code>— + = values to be matched against the index key, which may be multipart. + </para> + <para> + Returns: (type = tuple) zero or more tuples. + </para> + <para> + Possible Errors: Same as in <code>box.process()</code>. Any error results in a Lua exception. - <bridgehead renderas="sect4">Example</bridgehead> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting> -localhost> <userinput>call box.insert(0, 'test', 'my first tuple')</userinput> +localhost> <userinput>call box.insert('0', 'test#1', 'my first tuple')</userinput> Call OK, 1 rows affected -['test', 'my first tuple'] -localhost> <userinput>call box.select(0, 0, 'test')</userinput> +['test#1', 'my first tuple'] +localhost> <userinput>call box.select('0', '0', 'test#1')</userinput> Call OK, 1 rows affected -['test', 'my first tuple'] -localhost> <userinput>lua box.insert(5, 'testtest', 'firstname', 'lastname')</userinput> +['test#1', 'my first tuple'] +localhost> <userinput>lua box.insert(5, 'test#2', 'first_name', 'last_name')</userinput> --- - - 'testtest': {'firstname', 'lastname'} + - 'test#2': {'first_name', 'last_name'} ... -localhost> <userinput>lua box.select(5, 1, 'firstname', 'lastname')</userinput> +localhost> <userinput>lua box.select(5, 1, 'first_name', 'last_name')</userinput> --- - - 'testtest': {'firstname', 'lastname'} + - 'test#2': {'first_name', 'last_name'} ... </programlisting> </para> @@ -422,26 +425,29 @@ localhost> <userinput>lua box.select(5, 1, 'firstname', 'lastname')</userinput> <varlistentry> <term> - <emphasis role="lua" xml:id="box.insert">box.insert(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.insert">box.insert(<replaceable>space-number, field-value [, field-value ...])</replaceable></emphasis> </term> <listitem> - <para> - Insert a tuple into a space. Tuple fields - follow <code>space_no</code>. If a tuple with - the same primary key already exists, - <code>box.insert()</code> returns an error. - This function is a - wrapper around <code>box.process()</code>. - <bridgehead renderas="sect4">Returns</bridgehead> - Returns the inserted tuple. - </para> - </listitem> + <para> + Insert a tuple into a space. This function is a wrapper around <code>box.process()</code>. + </para> + <para> + Parameters: <code>space-number</code>, <code> field-value(s)</code> = fields of the new tuple. + </para> + <para> + Returns: (type = tuple) the inserted tuple. + </para> + <para> + Possible errors: If a tuple with the same primary key already exists, + <code>box.insert()</code> returns an error ER_TUPLE_FOUND. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.select_limit" xreflabel="box.select_limit"> - box.select_limit(space_no, index_no, offset, limit, ...) + box.select_limit(<replaceable>space-number, index-number, offset, limit, field-value [, field-value ...]</replaceable>) </emphasis> </term> <listitem> @@ -449,43 +455,52 @@ localhost> <userinput>lua box.select(5, 1, 'firstname', 'lastname')</userinput> Search for tuples in the given space. This is a full version of the built-in SELECT command, in which one can specify offset and limit for a - multi-tuple return. The server may return - multiple tuples when the index is non-unique or a - partial key is used for search. + multi-tuple return. + </para> + <para> + Parameters: <code>space-number</code>, <code>index-number</code>, + <code>offset</code> = where to start within a list of tuples, base 0, <code>limit</code> = maximum number to select, + <code>field-value(s)</code> values to match against keys in the index. + </para> + <para> + Returns: (type = tuple) zero or more tuples. </para> </listitem> </varlistentry> - <varlistentry> <term> - <emphasis role="lua" xml:id="box.replace">box.replace(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.replace">box.replace(<replaceable>space-number, field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> <para> - Insert a tuple into a space. Tuple fields - follow <code>space_no</code>. If a tuple with + Insert a tuple into a space. If a tuple with the same primary key already exists, <code>box.replace()</code> replaces the existing tuple with a new one. This function is a wrapper around <code>box.process()</code>. - <bridgehead renderas="sect4">Returns</bridgehead> - Returns the inserted tuple. + </para> + <para> + Parameters: <code>space-number</code>, <code> field-value(s)</code> = fields of the new tuple. + </para> + <para> + Returns: (type = tuple) the inserted tuple. </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.update">box.update(space_no, key, format, {field_no, value}...)</emphasis> + <emphasis role="lua" xml:id="box.update">box.update(<replaceable>space-number, key, format, {field_no, value}...</replaceable>)</emphasis> </term> <listitem> <para> Update a tuple. - The <code>space_no</code> and <code>key</code> arguments - identify the tuple; if a key is multipart then it is - passed as a Lua table. - The <code>format</code> argument is a sequence of + </para> + <para> + Parameters: <code>space-number</code>, + <code>key</code> = primary-key field values, must be passed as a Lua table if key is multi-part, + <code>format</code> = a sequence of pairs of characters, where the first character in each pair is the operation specifier, and the second character in each pair is the operation argument. @@ -511,8 +526,11 @@ localhost> <userinput>lua box.select(5, 1, 'firstname', 'lastname')</userinput> 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'. - <bridgehead renderas="sect4">Returns</bridgehead> - Returns the updated tuple. + </para> + <para> + Returns: (type = tuple) the updated tuple. + </para> + <para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting> #Assume that the initial state of the database is ... @@ -521,8 +539,10 @@ localhost> <userinput>lua box.select(5, 1, 'firstname', 'lastname')</userinput> #In the following update ... # The first argument is 0, that is, the affected space is space[0] -# The second argument is 999, that is, the affected tuple is identified by primary key value = 999 -# The third argument is '=p', that is, there is one operation, assignment to a field +# The second argument is 999, that is, the affected tuple is identified by +# primary key value = 999 +# The third argument is '=p', that is, there is one operation, assignment +# to a field # The fourth argument is 1, that is, the affected field is field[1] # The fifth argument is 'B', that is, field[1] contents change to 'B' # Therefore, after the following update, field[0] = 999 and field[1] = 'B'. @@ -532,27 +552,33 @@ lua box.update(0, 999, '=p', 1, 'B') # 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[0] = 999 and field[1] = 'B' (no change). +# Therefore, after the following update, field[0] = 999 and field[1] = 'B' +# (no change). lua box.update(0, {999}, '=p', 1, 'B') #In the following update, the arguments are the same, except that ... # The fourth argument is 2, that is the affected field is field[2]. # It is okay that, until now, field[2] has not existed. It gets added. -# Therefore, after the following update, field[0] = 999, field[1] = 'B', field[2] = 1. +# Therefore, after the following update, field[0] = 999, field[1] = 'B', +# field[2] = 1. lua box.update(0, 999, '=p', 2, 1) #In the following update, the arguments are the same, except that ... -# The third argument is '+p', that is, the operation is addition rather than assignment. +# The third argument is '+p', that is, the operation is addition rather +# than assignment. # Since field[2] previously contained 10, this means we're adding 1 to 1. -# Therefore, after the following update, field[0] = 999, field[1] = 'B', field[2] = 2. +# Therefore, after the following update, field[0] = 999, field[1] = 'B', +# field[2] = 2. lua box.update(0, 999, '+p', 2, 1) #In the following update ... # The idea is to modify two fields at once. -# The third argument is '|p=p', that is, there are two operations, OR and assignment. +# The third argument is '|p=p', that is, there are two operations, OR and +# assignment. # The fourth and fifth arguments mean that field[2] gets ORed with 1. # The fifth and sixth arguments mean that field[1] gets assigned 'C'. -# Therefore, after the following update, field[0] = 999, field[1] = 'C', field[2] = 3. +# Therefore, after the following update, field[0] = 999, field[1] = 'C', +# field[2] = 3. lua box.update(0, 999, '|p=p', 2, 1, 1, 'C') #In the following update ... @@ -563,14 +589,15 @@ lua box.update(0, 999, '|p=p', 2, 1, 1, 'C') lua box.update(0, 999, '#p-p', 1, 0, 1, 3) #In the following update ... -# We're making a long string so that the splice will work in the next example. +# We're making a long string so that splice will work in the next example. # Therefore, after the following update, field[0[ = 999, field[1] = 'XYZ'. lua box.update(0, 999, '=p', 1, 'XYZ') #In the following update ... # The third argument is ':p', that is, this is the example of splice. # The fifth argument is actually four arguments packed together ... -# a filler, an offset, the number of bytes to cut (1), and the string to add ('!') +# a filler, an offset, the number of bytes to cut (1), and the string +# to add ('!') # Therefore, after the following update, field[0[ = 999, field[1] = 'X!Z'. lua box.update(0, 999, ':p', 1, box.pack('ppp', 1, 1, '!')) @@ -581,48 +608,61 @@ lua box.update(0, 999, ':p', 1, box.pack('ppp', 1, 1, '!')) <varlistentry> <term> - <emphasis role="lua" xml:id="box.delete">box.delete(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.delete">box.delete(<replaceable>space-number, field-value [, field-value ...]</replaceable>)</emphasis> </term> - <listitem><para> - Delete a tuple identified by a primary key. - <bridgehead renderas="sect4">Returns</bridgehead> - Returns the deleted tuple. - <bridgehead renderas="sect4">Example</bridgehead> + <listitem> + <para> + Delete a tuple identified by a primary key. + </para> + <para> + Parameters: <code>space-number</code>, <code>field-value(s)</code> = values to match against keys in the primary index. + </para> + <para> + Returns: (type = tuple) the deleted tuple. + <bridgehead renderas="sect4">Example</bridgehead> <programlisting> -localhost> <userinput>call box.delete(0, 'test')</userinput> +localhost> <userinput>call box.delete('0', 'test#1')</userinput> Call OK, 1 rows affected ['test', 'my first tuple'] -localhost> <userinput>call box.delete(0, 'test')</userinput> +localhost> <userinput>call box.delete('0', 'test#1')</userinput> Call OK, 0 rows affected -localhost> <userinput>call box.delete(0, 'tes')</userinput> +localhost> <userinput>call box.delete('0', 44)</userinput> Call ERROR, Illegal parameters, key is not u32 (ER_ILLEGAL_PARAMS) </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.select_range" xreflabel="box.select_range"> - box.select_range(space_no, index_no, limit, key, ...) + box.select_range(<replaceable>space-number, index-number, limit, [, field-value ...]</replaceable>) </emphasis> </term> - <listitem><para> - Select a range of tuples, starting from the offset - specified by <code>key</code>. The key can be - multipart. Limit selection with at most - <code>limit</code> tuples. If no key is specified, - start from the first key in the index. - </para> - <para> - For TREE indexes, this returns tuples in sorted order. - For HASH indexes, the order of tuples is unspecified, and - can change significantly if data is inserted or deleted - between two calls to <code>box.select_range()</code>. - If <code>key</code> is <code>nil</code> or unspecified, - the selection starts from the start of the index. - This is a simple wrapper around <code xlink:href="#box.space.select_range">box.space[space_no]:select_range(index_no, ...)</code>. - BITSET index does not support this call. - </para> - <para> + <listitem> + <para> + Select a limited range of tuples, starting with the tuple + specified by <code>field-value(s)</code>. + This is a simple wrapper around <code xlink:href="#box.space.select_range">box.space[space-number]:select_range(index-number, ...)</code>. + </para> + <para> + Parameters: <code>space-number</code>, + <code>index-number</code>, + <code>limit</code> = maximum number to select, + <code>field-value(s)</code> values to match against keys in the specified index. + </para> + <para> + Returns: (type = tuple) For TREE indexes, this returns tuples in sorted order. + For HASH indexes, the order of tuples is unspecified, and + can change significantly if data is inserted or deleted + between two calls to <code>box.select_range()</code>. + If <code>key</code> is <code>nil</code> or unspecified, + the selection starts from the start of the index. + </para> + <para> + Possible errors: + For BITSET indexes, box.select_range() does not work. + </para> + <para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>show configuration</userinput> --- @@ -671,28 +711,31 @@ localhost> <userinput>lua box.select_range(4, 1, 2, '1')</userinput> - '2': {'2'} ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.select_reverse_range" xreflabel="box.select_reverse_range"> - box.select_reverse_range(space_no, index_no, limit, key, ...) + box.select_reverse_range(<replaceable>space-number, index-number, limit, field-value [, field-value ...]</replaceable>) </emphasis> </term> - <listitem><para> - Select a reverse range of tuples, starting from the offset - specified by <code>key</code>. The key can be - multipart. - Limit selection with at most <code>limit</code> - tuples. - If no key is specified, start from the last key in - the index. - </para> - <para> - For TREE indexes, this returns tuples in sorted order. - For other index types this call is not supported. - If <code>key</code> is <code>nil</code> or unspecified, - the selection starts from the end of the index. + <listitem> + <para> + Select a reverse (descending-order) limited range of tuples, starting with the tuple + specified by <code>field-value(s)</code>. + This is a simple wrapper around <code xlink:href="#box.space.select_range">box.space[space-number]:select_range(index-number, ...)</code>. + </para> + <para>Parameters: <code>space-number</code>, + <code>index-number</code>, + <code>limit</code> = maximum number to select, + <code>field-value(s)</code> values to match against keys in the specified index. + </para> + <para> + Returns: For TREE indexes, this returns tuples in descending sorted order. + If <code>key</code> is <code>nil</code> or unspecified, + the selection starts from the end of the index. + For other index types this call is not supported. <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>show configuration</userinput> --- @@ -738,17 +781,23 @@ localhost> <userinput>lua box.select_reverse_range(4, 1, 2, '1')</userinput> - '0': {'0'} ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> + <varlistentry> - <term><emphasis role="lua">box.pack(format, ...)</emphasis></term> + <term><emphasis role="lua">box.pack(<replaceable>format, argument [, argument ...]</replaceable>)</emphasis></term> <listitem><para> To use Tarantool binary protocol primitives from Lua, it's necessary to convert Lua variables to binary - format. This helper function is prototyped after Perl - 'pack'. It takes a format and a list of arguments, and - returns a binary string with all arguments packed - according to the format. + format. The box.pack() helper function is prototyped after Perl + <link xlink:href="http://perldoc.perl.org/functions/pack.html"> + 'pack'</link>. + </para> + <para> + Parameters: <code>format</code> = string containing format specifiers, <code>argument(s)</code> = scalar values to be formatted. + </para> + <para> <bridgehead renderas="sect4">Format specifiers</bridgehead> <simplelist> <member><code>b</code> — converts Lua @@ -815,117 +864,134 @@ localhost> <userinput>lua box.select_reverse_range(4, 1, 2, '1')</userinput> describe operation arguments. </member> </simplelist> - <bridgehead renderas="sect4">Errors</bridgehead> - Unknown format specifier. + </para> + <para> + Returns: a binary string containing all arguments, packed + according to the format specifiers. + </para> + <para> + Possible Errors: Unknown format specifier. + </para> + <para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting> localhost> <userinput>lua box.insert(0, 0, 'hello world')</userinput> --- - 0: {'hello world'} ... -localhost> <userinput>lua box.update(0, 0, "=p", 1, 'bye world')</userinput> +localhost> <userinput>lua box.update(0, 0, '=p', 1, 'bye world')</userinput> --- - 0: {'bye world'} ... -localhost> <userinput>lua box.update(0, 0, ":p", 1, box.pack('ppp', 0, 3, 'hello'))</userinput> +localhost> <userinput>lua box.update(0, 0, ':p', 1, box.pack('ppp', 0, 3, 'hello'))</userinput> --- - 0: {'hello world'} ... -localhost> <userinput>lua box.update(0, 0, "=p", 1, 4)</userinput> +localhost> <userinput>lua box.update(0, 0, '=p', 1, 4)</userinput> --- - 0: {4} ... -localhost> <userinput>lua box.update(0, 0, "+p", 1, 4)</userinput> +localhost> <userinput>lua box.update(0, 0, '+p', 1, 4)</userinput> --- - 0: {8} ... -localhost> <userinput>lua box.update(0, 0, "^p", 1, 4)</userinput> +localhost> <userinput>lua box.update(0, 0, '^p', 1, 4)</userinput> --- - 0: {12} ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.unpack(format, binary)</emphasis></term> - <listitem><para> - Counterpart to <code>box.pack()</code>. + <term><emphasis role="lua">box.unpack(<replaceable>format, binary-string</replaceable>)</emphasis></term> + <listitem> + <para> + Counterpart to <code>box.pack()</code>. + </para> + <para> + Parameters: <code>format</code>, <code>binary-string</code>. + </para> + <para> + Returns: (type = scalar) A list of strings or numbers. + </para> + <para> <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua tuple=box.replace(2, 0)</userinput> +<programlisting>localhost> <userinput>setopt delimiter='!' #this means following commands must end with '!'</userinput> +localhost> <userinput>lua tuple = box.replace(2, 0)!</userinput> --- ... -localhost> <userinput>lua string.len(tuple[0])</userinput> +localhost> <userinput>lua string.len(tuple[0])!</userinput> --- - 4 ... -localhost> <userinput>lua box.unpack('i', tuple[0])</userinput> +localhost> <userinput>lua box.unpack('i', tuple[0])!</userinput> --- - 0 ... -localhost> <userinput>lua box.unpack('bsil', box.pack('bsil', 255, 65535, 4294967295, tonumber64('18446744073709551615')))</userinput> +localhost> <userinput>lua box.unpack('bsi', box.pack('bsi', 255, 65535, 4294967295))!</userinput> --- - 255 - 65535 - 4294967295 +... +localhost> <userinput>lua box.unpack('ls', box.pack('ls',</userinput> + > <userinput> tonumber64('18446744073709551615'), 65535))!</userinput> +--- - 18446744073709551615 + - 65535 ... -localhost> <userinput>lua num, str, num64 = box.unpack('ppp', box.pack('ppp', 666, 'string', tonumber64('666666666666666')))</userinput> +localhost> <userinput>lua num, str, num64 = box.unpack('ppp', box.pack('ppp', 666, </userinput> + > <userinput> 'string',tonumber64('666666666666666')))!</userinput> --- ... -localhost> <userinput>lua print(box.unpack('i', num));</userinput> +localhost> <userinput>lua print(box.unpack('i', num));!</userinput> --- 666 ... -localhost> <userinput>lua print(str);</userinput> +localhost> <userinput>lua print(str);!</userinput> --- string ... -localhost> <userinput>lua print(box.unpack('l', num64))</userinput> +localhost> <userinput>lua print(box.unpack('l', num64))!</userinput> --- 666666666666666 ... -localhost> <userinput>lua box.unpack('=p', box.pack('=p', 1, '666'))</userinput> +localhost> <userinput>lua box.unpack('=p', box.pack('=p', 1, '666'))!</userinput> --- - 1 - 666 +localhost> <userinput>setopt delimiter='' #back to normal: commands end with line feed!</userinput> </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.print(...)</emphasis> - </term> - <listitem><para> -Redefines Lua <code>print()</code> built-in to print either to the log file -(when Lua is used from the binary port) or back to the user (for the -administrative console). -</para><para> -When printing to the log file, INFO log level is used. When printing to -the administrative console, all output is sent directly -to the socket. -</para><para> -Note: the administrative console output must be YAML-compatible. - </para></listitem> - </varlistentry> - <varlistentry> - <term> - <emphasis role="lua">box.dostring(s, ...)</emphasis> + <emphasis role="lua">box.dostring(<replaceable>lua-chunk-string [, lua-chunk-string-argument ...]</replaceable>)</emphasis> </term> <listitem> <para> - Evaluates an arbitrary chunk of Lua code passed in - <code>s</code>. If there is a compilation error, - it is raised as a Lua error. If there is no compilation - error, all arguments which follow <code>s</code> - are passed to the compiled chunk and the chunk is - invoked. + Parse and execute an arbitrary chunk of Lua code. + This function is mainly useful to define and run + Lua code without having to + introduce changes to the global Lua environment. + </para> + <para> + Parameters: <code>lua-chunk-string</code> = string containing Lua code, + <code>lua-chunk-string-argument(s)</code> = zero or more scalar values + which will be appended to, or substitute for, items in the Lua chunk. + </para> + <para> + Returns: whatever is returned by the Lua code chunk. + </para> + <para> + Possible errors: If there is a compilation error, + it is raised as a Lua error. </para> <para> - This function is mainly useful to define and run - an arbitrary piece of Lua code, without having to - introduce changes to the global Lua environment. <bridgehead renderas="sect4">Example</bridgehead> <programlisting> localhost> <userinput>lua box.dostring('abc')</userinput> @@ -941,10 +1007,16 @@ localhost> <userinput>lua box.dostring('return ...', 'hello', 'world')</useri - hello - world ... -localhost> <userinput>lua box.dostring('local f = function(key) t=box.select(0, 0, key); if t ~= nil then return t[0] else return nil end end return f(...)', 0)</userinput> +localhost> <userinput>setopt delimiter='!' #<link linkend="utility-tarantool-setopt">this</link> means ignore line feeds until next '!'</userinput> +localhost> <userinput>lua box.dostring('local f = function(key)</userinput> + -> <userinput> t = box.select(0, 0, key);</userinput> + -> <userinput> if t ~= nil then return t[0] else return nil end</userinput> + -> <userinput> end</userinput> + -> <userinput> return f(...)', 0)!</userinput> --- - nil ... +localhost> <userinput>setopt delimiter=''!</userinput> </programlisting> </para> </listitem> @@ -955,13 +1027,13 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( </term> <listitem> <para> - Returns current system time (in seconds since the epoch) as a Lua + Returns: current system time (in seconds since the epoch) as a Lua number. The time is taken from the event loop clock, which makes this call very cheap, but still useful for constructing artificial tuple keys. - <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.time(),box.time()</userinput> + <bridgehead renderas="sect4">Example</bridgehead> +<programlisting>localhost> <userinput>lua box.time(), box.time()</userinput> --- - 1385758759.2591 - 1385758759.2591 @@ -976,10 +1048,10 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( </term> <listitem> <para> - Returns current system time (in seconds) as a 64-bit - integer. The time is taken from the event loop clock. - <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.time(),box.time64()</userinput> + Returns: current system time (in microseconds since the epoch) as a 64-bit + integer. The time is taken from the event loop clock. + <bridgehead renderas="sect4">Example</bridgehead> +<programlisting>localhost> <userinput>lua box.time(), box.time64()</userinput> --- - 1385758828.9825 - 1385758828982485 @@ -994,14 +1066,13 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( </term> <listitem> <para> - Returns a 128-bit (16-byte) unique id in binary form. + Returns: a 128-bit (16-byte) unique id in binary form. </para> <para> - Requires <emphasis>libuuid</emphasis> library to be - installed. The library is loaded at runtime, - and if the library is not available, this - function returns an error. - <bridgehead renderas="sect4">Example</bridgehead> + Possible errors: The server tries to load the <emphasis>libuuid</emphasis> library + when it starts. If the library is not available, which can happen if it was not + found when the server was built from source, then box.uuid() returns an error. + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.uuid() == box.uuid() -- Comment: == means "are they equal?"</userinput> --- - false @@ -1016,7 +1087,7 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( </term> <listitem> <para> - Returns a 32-byte hexadecimal conversion of a 128-bit + Returns: a 32-byte hexadecimal conversion of a 128-bit unique id, as a string. </para> <bridgehead renderas="sect4">Example</bridgehead> @@ -1029,7 +1100,7 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.raise(errcode, errtext)</emphasis> + <emphasis role="lua">box.raise(<replaceable>errcode-number, errtext-string</replaceable>)</emphasis> </term> <listitem> <para> @@ -1040,56 +1111,79 @@ localhost> <userinput>lua box.dostring('local f = function(key) t=box.select( client as <constant>ER_PROC_LUA</constant>. This function makes it possible to emulate any kind of native exception, such as unique constraint violation, no such space/index, - etc. A complete list of errors is present in <link xlink:href="https://github.com/tarantool/tarantool/blob/master/include/errcode.h">errcode.h</link> - file in the source tree. + etc. A complete list of errors is present in the file <link xlink:href="https://github.com/tarantool/tarantool/blob/master/include/errcode.h"><filename>errcode.h</filename></link> + in the source tree. Lua constants which correspond to Tarantool errors - are defined in <code>box.error</code> module. The error + are defined in the <code>box.error</code> module. The error message can be arbitrary. - Throws client error. Lua procedure can emulate any + Lua procedures can use <code>box.raise()</code> to emulate request errors (for example: unique key exception). </para> + <para> + Parameters: <code>errcode-number</code> = number taken from the complete list of errors, + <code>errtext-string</code> = the message which will accompany the error. + </para> + <para> + Possible errors: whatever is specified in errcode-number. + </para> <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.raise(box.error.ER_WAL_IO, 'Wal I/O error')</userinput> +<programlisting>localhost> <userinput>lua box.raise(box.error.ER_WAL_IO, 'WAL I/O error')</userinput> --- -error: 'Wal I/O error' +error: 'WAL I/O error' ... </programlisting> </listitem> </varlistentry> + <varlistentry> <term> - <emphasis role="lua">box.auto_increment(space_no, ...)</emphasis> + <emphasis role="lua">box.auto_increment(<replaceable>space-number [, field-value ...]</replaceable>)</emphasis> </term> <listitem> <para> - Insert values into space designated by space_no, using - an auto-increment primary key. The space must have a + Insert a new tuple using an auto-increment primary key. + The space specified by space-number must have a NUM or NUM64 primary key index of type TREE. + The primary-key field will be incremented before the insert. + </para> + <para> + Parameters: <code>space-number</code>, <code>field-value(s)</code> = values for the tuple's fields, + other than the primary-key field. + </para> + <para> + Returns: (type = tuple) the inserted tuple. + </para> + <para> + Possible errors: index has wrong type or primary-key indexed field is not a number. </para> <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.auto_increment(0, "I am a duplicate")</userinput> +<programlisting>localhost> <userinput>lua box.auto_increment(0, 'Fld#1', 'Fld#2')</userinput> --- - - 1: {'I am a duplicate'} + - 1: {'Fld#1', 'Fld#2'} ... -localhost> <userinput>lua box.auto_increment(0, "I am a duplicate")</userinput> +localhost> <userinput>lua box.auto_increment(0, 'Fld#3')</userinput> --- - - 2: {'I am a duplicate'} + - 2: {'Fld#3'} ... </programlisting> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.counter.inc(space_no, key)</emphasis> + <emphasis role="lua">box.counter.inc(<replaceable>space-number, field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> <para> - Increments a counter identified by the key. The key can be - multipart, but there must be an index covering - all fields of the key. If there is no tuple - identified by the given key, creates a new one - with initial counter value set to 1. Returns the - new counter 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. + </para> + <para>Parameters: <code>space-number</code>, <code>field-value(s)</code> = values + which must match the primary key. + </para> + <para> + Returns: (type = number) the new counter value. </para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.counter.inc(0, 'top.mail.ru')</userinput> @@ -1104,13 +1198,21 @@ localhost> <userinput>lua box.counter.inc(0, 'top.mail.ru')</userinput> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.counter.dec(space_no, key)</emphasis> + <emphasis role="lua">box.counter.dec(<replaceable>space-number, field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> <para> - Decrements a counter identified by the given key. If - the key is not found, is a no-op. When counter value - drops to 0, the tuple is deleted. + 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. + </para> + <para> + Parameters: <code>space-number</code>, <code>field-value(s)</code> = values + which must match the primary key. + </para> + <para> + Returns: (type = number) the new counter value. </para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.counter.dec(0, 'top.mail.ru')</userinput> @@ -1124,35 +1226,96 @@ localhost> <userinput>lua box.counter.dec(0, 'top.mail.ru')</userinput> </listitem> </varlistentry> </variablelist> + +<bridgehead renderas="sect4">Example showing use of the box functions</bridgehead> +<para> +This example will work with the sandbox configuration described in the preface. That is, there is a space[0] with a numeric primary key. +The example function will: (1) select the tuple whose key value is 1000; +(2) return an error if the tuple already exists and already has 3 fields; +(3) Insert or replace the tuple with: field[0] = 1000, field[1] = a uuid, field[2] = number of seconds since 1970-01-01; +(4) Get field[2] from what was replaced; +(5) Format the value from field[2] as yyyy-mm-dd hh:mm:ss.ffff; +(6) Return the formatted value. +The function uses Tarantool box functions box.replace(), box.time(), box.select_limit(), box_uuid_hex(). +The function uses Lua functions +<link xlink:href="http://www.lua.org/pil/22.1.html">os.date()</link> +and <link xlink:href="http://www.lua.org/pil/20.html">string.sub()</link>. +</para> +<programlisting> +setopt delimiter='!' +lua function example() + local a, b, c, selected_tuple, replaced_tuple, time_field + selected_tuple = box.select_limit(0, 0, 0, 1, 1000) + if selected_tuple ~= nil then + if #selected_tuple == 3 then + box.raise(1,'This tuple already has 3 fields') + end + end + replaced_tuple = box.replace(0, 1000, box.uuid_hex(), tostring(box.time())) + time_field = tonumber(replaced_tuple[2]) + 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! +setopt delimiter=''! +</programlisting> +<para> +... And here is what happens when one invokes the function: +<programlisting> +<prompt>localhost></prompt> <userinput>lua box.delete(0, 1000)</userinput> +--- + - 1000: {'264ee2da03634f24972be76c43808254', '1391037015.6809'} +... +<prompt>localhost></prompt> <userinput>lua example(1001)</userinput> +--- + - 2014-01-29 16:11:51.1582 +... +<prompt>localhost></prompt> <userinput>lua example(1001)</userinput> +--- +error: 'This tuple already has 3 fields' +... +</programlisting> +</para> + </section> <section xml:id="sp-box-tuple"> <title>Package <code>box.tuple</code></title> <variablelist xml:id="box.tuple" xreflabel="box.tuple"> - <para>This package provides read-only access for the <code>box.tuple</code> userdata + <para>The <code>box.tuple</code> package provides read-only access for the <code>box.tuple</code> userdata type. It allows, for a single tuple: selective retrieval of the field contents, retrieval of information about size, iteration over all the fields, and conversion to a Lua table. </para> <varlistentry> <term> - <emphasis role="lua">box.tuple.new(...)</emphasis> + <emphasis role="lua">box.tuple.new(<replaceable>scalar-value | Lua-table-value</replaceable>)</emphasis> </term> <listitem> <para> - Construct a new tuple from a Lua table or a scalar. + Construct a new tuple from either a scalar or a Lua table. Alternatively, one can get new tuples from tarantool's SQL-like statements: SELECT, INSERT, UPDATE, REPLACE, which can be regarded as statements that do new() implicitly. - In the following example, x and t will be new tuple objects. - Saying <code>lua t</code> returns the entire tuple t. + </para> + <para> + Parameters: <code>scalar-value | Lua-table-value</code> = the value that will become the tuple contents. + </para> + <para> + Returns: (type = tuple) a new tuple. + </para> + <para> + In the following example, x and t will be new tuple objects. + Saying <code>lua t</code> returns the entire tuple t. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua x=box.insert(0,'a',tonumber('1'),tonumber64('2')):totable()</userinput> + <programlisting>localhost> <userinput>lua x = box.insert(0,'a',tonumber('1'),tonumber64('2')):totable()</userinput> --- ... -localhost> <userinput>lua t=box.tuple.new({'abc','def','ghi','abc'})</userinput> +localhost> <userinput>lua t = box.tuple.new({'abc', 'def', 'ghi', 'abc'})</userinput> --- ... localhost> <userinput>lua t</userinput> @@ -1163,19 +1326,24 @@ localhost> <userinput>lua t</userinput> </varlistentry> <varlistentry> <term> - <emphasis role="lua">#</emphasis> + <emphasis role="lua"># <replaceable>tuple-value</replaceable></emphasis> </term> <listitem> <para> - The # operand in Lua means "return count of components". + The # operator in Lua means "return count of components". So, if t is a tuple instance, <code>#t</code> will return the number of - fields. + fields. + </para> + <para> + Returns: (type = number) number of fields. + </para> + <para> In the following example, a tuple named t is created and then the number of fields in t is returned. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4'})</userinput> --- ... localhost> <userinput>lua #t</userinput> @@ -1186,24 +1354,29 @@ localhost> <userinput>lua #t</userinput> </varlistentry> <varlistentry> <term> - <emphasis role="lua">bsize()</emphasis> + <emphasis role="lua"> <replaceable>tuple-value</replaceable> : bsize()</emphasis> </term> <listitem> <para> - If t is a tuple instance, <code>t:bsize()</code> - will return the number of bytes in the tuple. - It is useful to check this number when making changes to data, - because there is a fixed maximum: one megabyte. - Every field has one or more "length" bytes preceding the - actual contents, so bsize() returns a value which is - slightly greater than the sum of the lengths of the contents. - In the following example, a tuple named t is created - which has three fields, and for each field it takes one byte - to store the length and three bytes to store the contents, - so bsize() returns 3*(1+3). + If t is a tuple instance, <code>t:bsize()</code> + will return the number of bytes in the tuple. + It is useful to check this number when making changes to data, + because there is a fixed maximum: one megabyte. + Every field has one or more "length" bytes preceding the + actual contents, so bsize() returns a value which is + slightly greater than the sum of the lengths of the contents. + </para> + <para> + Returns: (type = number) number of bytes. + </para> + <para> + In the following example, a tuple named t is created + which has three fields, and for each field it takes one byte + to store the length and three bytes to store the contents, + so bsize() returns 3*(1+3). </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'aaa','bbb','ccc'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'aaa','bbb','ccc'})</userinput> --- ... localhost> <userinput>lua t:bsize()</userinput> @@ -1214,38 +1387,49 @@ localhost> <userinput>lua t:bsize()</userinput> </varlistentry> <varlistentry> <term> - <emphasis role="lua">[ ]</emphasis> + <emphasis role="lua">[ <replaceable>field-number</replaceable> ]</emphasis> </term> <listitem> <para> - If t is a tuple instance, <code>t[<replaceable>n</replaceable>]</code> - will return the - <code>n</code>th field in the tuple. The first field is t[0]. - In the following example, a tuple named t is created - and then the second field in t is returned. + If t is a tuple instance, <code>t[<replaceable>field-number</replaceable>]</code> + will return the field numbered <code>field-number</code> in the tuple. + The first field is t[0]. + </para> + <para> + Returns: (type = scalar) field value. + </para> + <para> + In the following example, a tuple named t is created + and then the second field in t is returned. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4'})</userinput> --- ... localhost> <userinput>lua t[1]</userinput> --- - - Field#2 + - Fld#2 ...</programlisting> </listitem> </varlistentry> + <varlistentry> <term> - <emphasis role="lua">find() or findall()</emphasis> + <emphasis role="lua">find(<replaceable>[field-number,] field-value) or findall([field-number,] field-value</replaceable>)</emphasis> </term> <listitem> <para> - If t is a tuple instance, <code>t:find(<replaceable>search-value</replaceable>)</code> - will return the number of the first field in t that matches search-value, and - <code>t:findall(<replaceable>search-value</replaceable>)</code> - will return numbers of all fields in t that match search-value. Optionally - one can put a numeric argument n before the search-value to indicate - <quote>start searching at field number n.</quote> + If t is a tuple instance, <code>t:find(<replaceable>field-value</replaceable>)</code> + will return the number of the first field in t that matches the field value), and + <code>t:findall(<replaceable>field-value [, field-value ...]</replaceable>)</code> + will return numbers of all fields in t that match the field value. Optionally + one can put a numeric argument field-number before the search-value to indicate + <quote>start searching at field number <code>field-number</code>.</quote> + </para> + <para> + Returns: (type = number) the number of the field in the tuple. + </para> + <para> In the following example, a tuple named t is created and then: the number of the first field in t which matches 'a' is returned, then the numbers of all the fields in t which match 'a' are returned, @@ -1253,7 +1437,7 @@ localhost> <userinput>lua t[1]</userinput> are returned. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'a','b','c','a'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'a','b','c','a'})</userinput> --- ... localhost> <userinput>lua t:find('a')</userinput> @@ -1265,7 +1449,7 @@ localhost> <userinput>lua t:findall('a')</userinput> - 0 - 3 ... -localhost> <userinput>lua t:findall(1,'a')</userinput> +localhost> <userinput>lua t:findall(1, 'a')</userinput> --- - 3 ...</programlisting> @@ -1273,51 +1457,66 @@ localhost> <userinput>lua t:findall(1,'a')</userinput> </varlistentry> <varlistentry> <term> - <emphasis role="lua">transform()</emphasis> + <emphasis role="lua">transform(<replaceable>start-field-number, fields-to-remove [, field-value ...]</replaceable>)</emphasis> </term> <listitem> <para> - If t is a tuple instance, <code>t:transform(<replaceable>n1</replaceable>,<replaceable>n2</replaceable>)</code> - will return a tuple where, starting from field n1, a number of fields (n2) are removed. - Optionally one can add more arguments after n2 to indicate new values that will replace - what was removed. - In the following example, a tuple named t is created - and then, starting from the second field, two fields are removed - but one new one is added, then the result is returned. + If t is a tuple instance, <code>t:transform(<replaceable>start-field-number</replaceable>,<replaceable>fields-to-remove</replaceable>)</code> + will return a tuple where, starting from field start-field-number, a number of fields (fields-to-remove) are removed. + Optionally one can add more arguments after fields-to-remove to indicate new values that will replace + what was removed. + </para> + <para> + Parameters: <code>start-field-number</code> = base 0, may be negative, <code>fields-to-remove</code>, <code>field-values(s)</code>. + </para> + <para> + Returns: (type = tuple) a new tuple. + </para> + <para> + In the following example, a tuple named t is created + and then, starting from the second field, two fields are removed + but one new one is added, then the result is returned. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'}) + <programlisting>localhost> lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'}) --- ... localhost> <userinput>lua t:transform(1,2,'x')</userinput> --- - - 'Field#1': {'x', 'Field#4', 'Field#5'} + - 'Fld#1': {'x', 'Fld#4', 'Fld#5'} ...</programlisting> </listitem> </varlistentry> + <varlistentry> <term> - <emphasis role="lua">slice()</emphasis> + <emphasis role="lua">slice(<replaceable>start-field-number [, end-field-number]</replaceable>)</emphasis> </term> <listitem> <para> - If t is a tuple instance, <code>t:slice(<replaceable>n</replaceable>)</code> - will return all fields starting with field number n, and - <code>t:slice(<replaceable>n1</replaceable>,<replaceable>n2</replaceable>)</code> - will return a tuple containing fields starting with field number n1, but - stopping <emphasis>before</emphasis> field number n2. - In the following example, a tuple named t is created - and then, starting from the second field, fields before the fourth field are selected, - then the result is returned. + If t is a tuple instance, <code>t:slice(<replaceable>n</replaceable>)</code> + will return all fields starting with field number n, and + <code>t:slice(<replaceable>n1</replaceable>,<replaceable>n2</replaceable>)</code> + will return all fields starting with field number n1, but + stopping <emphasis>before</emphasis> field number n2. + In the following example, a tuple named t is created + and then, starting from the second field, fields before the fourth field are selected, + then the result is returned. + </para> + <para> + Parameters: <code>start-field-number</code> = base 0, may be negative, <code>end-field-number</code> = optional, base 0, negative treated as positive. + </para> + <para> + Returns: (type = scalar) one or more field values. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'})</userinput> --- ... -localhost> <userinput>lua t:slice(1,3)</userinput> +localhost> <userinput>lua t:slice(1, 3)</userinput> --- - - Field#2 - - Field#3 + - Fld#2 + - Fld#3 ...</programlisting> </listitem> </varlistentry> @@ -1327,23 +1526,28 @@ localhost> <userinput>lua t:slice(1,3)</userinput> </term> <listitem> <para> - If t is a tuple instance, <code>t:unpack(<replaceable>n</replaceable>)</code> - will return all fields. In effect, <code>unpack()</code> is the same as <code>slice(0,-1)</code>. - In the following example, a tuple named t is created - and then all its fields are selected, - then the result is returned. + If t is a tuple instance, <code>t:unpack(<replaceable>n</replaceable>)</code> + will return all fields. In effect, <code>unpack()</code> is the same as <code>slice(0, -1)</code>. + </para> + <para> + Returns: (type = scalar) field(s) from the tuple. + </para> + <para> + In the following example, a tuple named t is created + and then all its fields are selected, + then the result is returned. </para> <bridgehead renderas="sect4">Example</bridgehead> - <programlisting>localhost> <userinput>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})</userinput> + <programlisting>localhost> <userinput>lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'})</userinput> --- ... localhost> <userinput>lua t:unpack()</userinput> --- - - Field#1 - - Field#2 - - Field#3 - - Field#4 - - Field#5 + - Fld#1 + - Fld#2 + - Fld#3 + - Fld#4 + - Fld#5 ...</programlisting> </listitem> </varlistentry> @@ -1353,45 +1557,99 @@ localhost> <userinput>lua t:unpack()</userinput> </term> <listitem> <para> - In Lua, pairs() is a method which returns: function, value, nil. - It is useful for Lua iterators, because Lua iterators traverse - a value's components until an end marker is reached. - In the following example, a tuple named t is created - and then all its fields are selected using a Lua for-end loop. + In Lua, lua-table-value:pairs() is a method which returns: function, lua-table-value, nil. + Tarantool has extended this so that tuple-value:pairs() returns: function, tuple-value, nil. + It is useful for Lua iterators, because Lua iterators traverse + a value's components until an end marker is reached. + </para> + <para> + Returns: (type = function) function, (type = tuple) tuple-value, (type = nil) nil. + </para> + <para> + In the following example, a tuple named t is created + and then all its fields are selected using a Lua for-end loop. </para> <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'}) +<programlisting>localhost> <userinput>lua t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'})</userinput> --- ... -localhost> <userinput>lua for k,v in t:pairs() do print(v) end</userinput> +localhost> <userinput>lua for k, v in t:pairs() do print(v) end</userinput> --- -Field#1 -Field#2 -Field#3 -Field#4 -Field#5 +Fld#1 +Fld#2 +Fld#3 +Fld#4 +Fld#5 ...</programlisting> </listitem> </varlistentry> </variablelist> -</section> - -<section xml:id="sp-box-cjson"> - <title>Package <code>box.cjson</code></title> -<variablelist xml:id="box.cjson" xreflabel="box.cjson"> - <para> - This package provides JSON manipulation routines. - It's based on the <link xlink:href="http://www.kyne.com.au/~mark/software/lua-cjson.php"> - Lua-CJSON package by Mark Pulford</link>. +<bridgehead renderas="sect4">Example showing use of the box.tuple functions</bridgehead> +<para> +This function will illustrate how to convert tuples to/from +Lua tables and lists of scalars: +<programlisting> + scalars to tuple: tuple = box.tuple.new({scalar1, scalar2, ... scalar_n}) + tuple to Lua table: lua_table = {tuple:unpack()} + tuple to scalars: scalar1, scalar2, ... scalar_n = tuple:unpack() + Lua table to tuple: tuple = box.tuple.new(lua_table) +</programlisting> +Then it will find the field that contains 'b', +remove that field from the tuple, +and display how many bytes remain in the tuple. +The function uses Tarantool box.tuple functions new(), unpack(), find(), transform(), bsize(). +</para> +<programlisting> +setopt delimiter='!' +lua function example() + local tuple1, tuple2, lua_table_1, scalar1, scalar2, scalar3, field_number + tuple1 = box.tuple.new({'a', 'b', 'c'}) + luatable1 = {tuple1:unpack()} + scalar1, scalar2, scalar3 = tuple1:unpack() + tuple2 = box.tuple.new(luatable1) + field_number = tuple2:find('b') + tuple2 = tuple2:transform(field_number, 1) + print('tuple2 = ',tuple2, ' # of bytes = ', tuple2:bsize()) +end! +setopt delimiter=''! +</programlisting> +<para> +... And here is what happens when one invokes the function: +<programlisting> +<prompt>localhost></prompt> <userinput>lua example()</userinput> +--- +tuple2 = 'a': {'c'} # of bytes = 4 +... +</programlisting> +</para> + +</section> + +<section xml:id="sp-box-cjson"> + <title>Package <code>box.cjson</code></title> + +<variablelist xml:id="box.cjson" xreflabel="box.cjson"> + <para> + The <code>box.cjson</code> package provides JSON manipulation routines. + It is based on the <link xlink:href="http://www.kyne.com.au/~mark/software/lua-cjson.php"> + Lua-CJSON package by Mark Pulford</link>. For a complete manual on Lua-CJSON please read <link xlink:href="http://www.kyne.com.au/~mark/software/lua-cjson-manual.html">the official documentation</link>. </para> <varlistentry> - <term><emphasis role="lua">box.cjson.encode(object)</emphasis></term> + <term><emphasis role="lua">box.cjson.encode(<replaceable>scalar-value | Lua-table-value</replaceable>)</emphasis></term> <listitem> - <para>Convert a Lua object to a JSON string.</para> + <para> + Convert a Lua object to a JSON string. + </para> + <para> + Parameters: either a scalar value or a Lua table value. + </para> + <para> + Returns: (type = string) the original value reformatted as a JSON string. + </para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.cjson.encode(123)</userinput> --- @@ -1409,7 +1667,7 @@ localhost> <userinput>lua box.cjson.encode({abc = 234, cde = 345})</userinput --- - {"cde":345,"abc":234} ... -localhost> <userinput>lua box.cjson.encode({hello = { 'world' } })</userinput> +localhost> <userinput>lua box.cjson.encode({hello = {'world'}})</userinput> --- - {"hello":["world"]} ... @@ -1417,9 +1675,17 @@ localhost> <userinput>lua box.cjson.encode({hello = { 'world' } })</userinput </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.cjson.decode(string)</emphasis></term> + <term><emphasis role="lua">box.cjson.decode(<replaceable>string-value</replaceable>)</emphasis></term> <listitem> - <para>Convert a JSON string to a Lua object.</para> + <para> + Convert a JSON string to a Lua object. + </para> + <para> + Parameters: <code>string-value</code> = a string formatted as JSON. + </para> + <para> + Returns: (type = Lua table) the original contents formatted as a Lua table. + </para> <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.cjson.decode('123')</userinput> --- @@ -1442,82 +1708,95 @@ localhost> <userinput>lua box.cjson.decode('{"hello": "world"}').hello</useri <section xml:id="sp-box-space"> <title>Package <code>box.space</code></title> <variablelist xml:id="box.space" xreflabel="box.space"> - <para>This package is a container for all - configured spaces. A space object provides access to space - attributes, such as id, whether or not a space is - enabled, space cardinality, and estimated number of rows. It also - contains object-oriented versions of <code>box</code> - functions. For example, instead of <code>box.insert(0, ...)</code> - one can write <code>box.space[0]:insert(...)</code>. - Package source code is available in file <filename - xlink:href="https://github.com/tarantool/tarantool/blob/master/src/box/lua/box.lua">src/box/lua/box.lua</filename></para> - <para>A list of all <code>space</code> members follows.</para> + <para> + The <code>box.space</code> package provides access to space + attributes, such as id, whether or not a space is + enabled, space cardinality, and estimated number of rows. It also + contains object-oriented versions of <code>box</code> + functions. For example, instead of <code>box.insert(0, ...)</code> + one can write <code>box.space[0]:insert(...)</code>. + Package source code is available in file <filename + xlink:href="https://github.com/tarantool/tarantool/blob/master/src/box/lua/box.lua">src/box/lua/box.lua</filename>. + </para> + <para> + A list of all <code>space</code> members follows. + </para> <varlistentry> - <term><emphasis role="lua">space.n</emphasis></term> - <listitem><simpara>Ordinal space number, <code>box.space[i].n == i</code></simpara></listitem> + <term><emphasis role="lua">box.space[<replaceable>space-number</replaceable>].n</emphasis></term> + <listitem> + <para> + (type = number) Ordinal space number, <code>box.space[i].n == i</code>. + </para> + </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">space.enabled</emphasis></term> - <listitem><simpara> - Whether or not this space is enabled in the - configuration file. - </simpara></listitem> + <term><emphasis role="lua">box.space[<replaceable>space-number</replaceable>].enabled</emphasis></term> + <listitem> + <para> + (type = boolean) Whether or not this space is enabled in the + configuration file. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space.cardinality</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].cardinality</emphasis> </term> - <listitem><simpara> - A limit on tuple field count for tuples in this space. - This limit can be set in the configuration file. Value 0 - stands for <quote>unlimited</quote>. - </simpara></listitem> + <listitem> + <para> + (type = number) The required field count for all tuples in this space. + The cardinality can be set in <olink targetptr="space">the configuration file</olink>. + The default value is 0, which means there is no required field count. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space.index[]</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[]</emphasis> </term> - <listitem><para> - A container for all defined indexes. An index is a Lua object - of type <code xlink:href="#box.index">box.index</code> with - methods to search tuples and iterate over them in predefined order. + <listitem> + <para> + (type = table) A container for all defined indexes. An index is a Lua object + of type <code xlink:href="#box.index">box.index</code> with + methods to search tuples and iterate over them in predefined order. <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.space[0].n, box.space[0].enabled, box.space[0].cardinality, box.space[0].index[0].type</userinput> +<programlisting>localhost> <userinput>lua box.space[0].n,box.space[0].cardinality,box.space[0].index[0].type</userinput> --- - 0 - - true - 0 - HASH ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:select(index_no, ...)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:select(<replaceable>index-number, field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:select(<replaceable>index_number...</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.select">box.select(<replaceable>space_number</replaceable>,<replaceable>index_number...</replaceable>)</code>. - </simpara></listitem> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:select(<replaceable>index-number...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.select">box.select(<replaceable>space-number</replaceable>,<replaceable>index-number...</replaceable>)</code>. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.space.select_range" xreflabel="box.space[i].select_range()"> - space:select_range(index_no, limit, key) + box.space[<replaceable>space-number</replaceable>]:select_range(<replaceable>index-number, limit, field-value [, field-value ...]</replaceable>) </emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:select_range(<replaceable>index_number...</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.select_range">box.select_range(<replaceable>space_number</replaceable>,<replaceable>index_number...</replaceable>)</code>. - </simpara> + <para> + <code>lua box.space[<replaceable>space_number</replaceable>]:select_range(<replaceable>index-number...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.select_range">box.select_range(<replaceable>space_number</replaceable>,<replaceable>index-number...</replaceable>)</code>. + </para> </listitem> </varlistentry> @@ -1526,88 +1805,98 @@ localhost> <userinput>lua box.cjson.decode('{"hello": "world"}').hello</useri <emphasis role="lua" xml:id="box.space.select_reverse_range" xreflabel="box.space.select_reverse_range"> - space:select_reverse_range(limit, key)</emphasis> + box.space[<replaceable>space-number</replaceable>]:select_reverse_range(<replaceable>limit, field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:select_reverse_range(<replaceable>index_number...</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.select_range">box.select_reverse_range(<replaceable>space_number</replaceable>,<replaceable>index_number...</replaceable>)</code>. - </simpara> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:select_reverse_range(<replaceable>index-number...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.select_range">box.select_reverse_range(<replaceable>space-number</replaceable>,<replaceable>index-number...</replaceable>)</code>. + </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:insert(...)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:insert(<replaceable>field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:insert(<replaceable>key</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.insert">box.insert(<replaceable>space_number</replaceable>,<replaceable>key</replaceable>)</code>. - </simpara></listitem> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:insert(<replaceable>field-value ...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.insert">box.insert(<replaceable>space-number</replaceable>,<replaceable>field-value ...</replaceable>)</code>. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:replace(...)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:replace(<replaceable>field-value [, field-value ...]</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:replace(<replaceable>key</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.replace">box.replace(<replaceable>space_number</replaceable>,<replaceable>key</replaceable>)</code>. - </simpara></listitem> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:replace(<replaceable>key</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.replace">box.replace(<replaceable>space-number</replaceable>,<replaceable>field-value ...</replaceable>)</code>. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:delete(key)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:delete(<replaceable>field-value [, field-value ...</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:delete(<replaceable>key</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.delete">box.delete(<replaceable>space_number</replaceable>,<replaceable>key</replaceable>)</code>. - </simpara></listitem> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:delete(<replaceable>field-value ...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.delete">box.delete(<replaceable>space-number</replaceable>,<replaceable>field-value ...</replaceable>)</code>. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:update(key, format, ...)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:update(<replaceable>field-value [, field-value ...], format, {field_no, value}...</replaceable>)</emphasis> </term> <listitem> - <simpara> - <code>lua box.space[<replaceable>space_number</replaceable>]:update(<replaceable>key</replaceable>)</code> - is the object-oriented equivalent of - <code xlink:href="#box.update">box.update(<replaceable>space_number</replaceable>,<replaceable>key</replaceable>)</code>. - </simpara></listitem> + <para> + <code>lua box.space[<replaceable>space-number</replaceable>]:update(<replaceable>field-value ...</replaceable>)</code> + is the object-oriented equivalent of + <code xlink:href="#box.update">box.update(<replaceable>space-number</replaceable>,<replaceable>field-value ...</replaceable>)</code>. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:len()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:len()</emphasis> </term> <listitem> - <para> - Returns number of tuples in the space. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + Returns: (type = number) number of tuples in the space. + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.space[0]:len()</userinput> --- - 2 ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:truncate()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:truncate()</emphasis> </term> <listitem> - <para> - Deletes all tuples. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + Deletes all tuples. + </para> + <para> + Returns: nil. + </para> + <para> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.space[0]:truncate()</userinput> --- ... @@ -1616,21 +1905,25 @@ localhost> <userinput>lua box.space[0]:len()</userinput> - 0 ... </programlisting> - </para></listitem> + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">space:pairs()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>]:pairs()</emphasis> </term> <listitem> - <simpara> - A helper function to iterate over all space tuples, - Lua style. - </simpara> - <bridgehead renderas="sect4">Example</bridgehead> + <para> + A helper function to prepare for iterating over all tuples in a space. + </para> + <para> + Returns: (type = function) function which can be used in a for/end loop. + Within the loop, a value (type = tuple) is returned for each iteration. + </para> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting> -localhost> <userinput>lua for k,v in box.space[0]:pairs() do print(v) end</userinput> +localhost> <userinput>lua for k, v in box.space[0]:pairs() do print(v) end</userinput> --- 1: {'hello'} 2: {'my '} @@ -1641,70 +1934,146 @@ localhost> <userinput>lua for k,v in box.space[0]:pairs() do print(v) end</useri </listitem> </varlistentry> </variablelist> + +<bridgehead renderas="sect4">Example showing use of the box.space functions</bridgehead> +<para> +This function will illustrate how to look at all the spaces, +and for each display: whether the space is enabled, approximately +how many tuples it contains, and the contents of its first two tuples. +Warning: the way to traverse spaces will change in the next +version of Tarantool. +The function uses Tarantool box.space functions len() and pairs(). +The iteration through the spaces is coded as an infinite loop, +but no worries -- it will be halted with an error message when +the space number becomes greater than the maximum. +</para> +<programlisting> +setopt delimiter='!' +lua function example() + local space_number, tuple_number, tuple_count + space_number = 0 + while 0 == 0 do + tuple_count = box.space[space_number]:len() + print('space_number ',space_number) + print(' enabled = ', box.space[space_number].enabled) + print(' approximate number of tuples = ', tuple_count) + tuple_number = 1 + for k, v in box.space[space_number]:pairs() do + print(' tuple#',tuple_number,' = ',v) + tuple_number = tuple_number + 1 + if tuple_number > 2 then break end + end + space_number = space_number + 1 + end +end! +setopt delimiter=''! +</programlisting> +<para> +... And here is what happens when one invokes the function: +<programlisting> +<prompt>localhost></prompt> <userinput>lua example()</userinput> +--- +space_number 0 + enabled = true + approximate number of tuples = 4 + tuple#1 = 0: {0, 1390945890} + tuple#2 = 1: {1, 'b'} +space_number 1 + enabled = true + approximate number of tuples = 4 + tuple#1 = 0: {0, 1390945890} + tuple#2 = 1: {1, 'b'} +error: '[string "function example() local space_number, tupl..."]:1: + attempt to index a nil value' +... +</programlisting> +</para> + </section> <section xml:id="sp-box-index"> <title>Package <code >box.index</code></title> <variablelist xml:id="box.index" xreflabel="box.index"> - <para> - This package implements methods of type <code>box.index</code>. - Indexes are contained in <code - xlink:href="#box.space">box.space[i].index[]</code> 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 in the - storage engine. - </para> + <para> + The <code>box.index</code> package provides read-only access for index definitions and index keys. + Indexes are contained in <code + xlink:href="#box.space">box.space[i].index[]</code> 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 <code>box.index</code> in the + storage engine. + </para> <varlistentry> - <term><emphasis role="lua">index.unique</emphasis></term> - <listitem><simpara> - Boolean, true if the index is unique. - </simpara></listitem> + <term><emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>].unique</emphasis></term> + <listitem> + <para> + (type = boolean) true if the index is unique. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">index.type</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>].type</emphasis> </term> - <listitem><simpara> - A string for index type, either 'TREE', 'HASH', or 'BITSET'. - </simpara></listitem> + <listitem> + <para> + (type = string) index type, 'TREE' or 'HASH' or 'BITSET'. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">index.key_field[]</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>].key_field[]</emphasis> </term> - <listitem><simpara> - An array describing index key fields. - </simpara></listitem> + <listitem> + <para> + An array describing index key fields. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">index.idx</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>].idx</emphasis> </term> - <listitem><para> - The underlying userdata which does all the magic. - <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.space[0].index[0].unique,box.space[0].index[0].type,box.space[0].index[0].key_field[0],box.space[0].index[0].idx</userinput> + <listitem> + <para> + A group of methods including max(), min(), count(), random(), next(), + next_equal(), prev(), prev_equal(). For each case there is an alternative syntax, + and use of the alternative syntax is recommended. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term></term> + <listitem><bridgehead renderas="sect4">Example</bridgehead> +<programlisting>localhost> <userinput>lua box.space[0].index[0].unique, box.space[0].index[0].type</userinput> --- - true - HASH +... +localhost> <userinput>lua box.space[0].index[0].key_field[0], box.space[0].index[0].idx</userinput> +--- - table: 0x41d03518 - index 0 in space 0 ... -</programlisting> - </para></listitem> + +localhost> <userinput>lua box.space[0].index[0].idx:max()</userinput> +--- + - 999: {2, 'c'} +...</programlisting></listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.index.iterator" xreflabel="box.index.iterator(type, ...)"> - index:iterator(type, ...)</emphasis> + box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>]:iterator(<replaceable>iterator-type, bitset-value | field-value...</replaceable>)</emphasis> </term> <listitem> - <simpara> + <para> This method provides iteration support within an index. Parameter <code>type</code> is used to identify the semantics of iteration. Different @@ -1715,7 +2084,7 @@ localhost> <userinput>lua for k,v in box.space[0]:pairs() do print(v) end</useri can return all tuples in ascending or descending order, starting from the specified key. Other index types, however, do not support ordering. - </simpara> + </para> <para xml:id="iterator-consistency"> To understand consistency of tuples returned by an iterator, it's essential to know @@ -1734,18 +2103,18 @@ localhost> <userinput>lua for k,v in box.space[0]:pairs() do print(v) end</useri content of the database. </para> <para> - <bridgehead renderas="sect4">Parameters</bridgehead> - <simplelist> - <member><code>type</code> — iteration strategy as defined in tables below.</member> - </simplelist> - - <bridgehead renderas="sect4">Returns</bridgehead> - This method returns an iterator closure, i.e. + Parameters: + <code>type</code> — iteration strategy as defined in tables below. + </para> + <para> + Returns: this method returns an iterator closure, i.e. a <code>function</code> which can be used to get the next value on each invocation. - <bridgehead renderas="sect4">Errors</bridgehead> + </para> + <para> + Possible Errors: Selected iteration type is not supported in - the subject index type or supplied parameters + the subject index type, or supplied parameters do not match iteration type. </para> @@ -1781,19 +2150,31 @@ localhost> <userinput>INSERT INTO t0 VALUES (5, 2, 1)</userinput> Insert OK, 1 rows affected localhost> <userinput>INSERT INTO t0 VALUES (6, 2, 2)</userinput> Insert OK, 1 rows affected -localhost> <userinput>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1); print(it(), " ", it(), " ", it());</userinput> +localhost> <userinput>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1);</userinput> +--- +... +localhost> <userinput>lua print(it(), ' ', it(), ' ', it());</userinput> --- 1: {1, 0} 2: {1, 1} 3: {1, 2} ... -localhost> <userinput>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1, 2); print(it(), " ", it(), " ", it());</userinput> +localhost> <userinput>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1, 2);</userinput> +--- +... +localhost> <userinput>lua print(it(), ' ', it(), ' ', it());</userinput> --- 3: {1, 2} nil nil ... -localhost> <userinput>lua i = box.space[0].index[1]:iterator(box.index.GE, 2, 1); print(it(), " ", it(), " ", it());</userinput> +localhost> <userinput>lua it = box.space[0].index[1]:iterator(box.index.GE, 2, 1);</userinput> +--- +... +localhost> <userinput>lua print(it(), ' ', it(), ' ', it());</userinput> --- 5: {2, 1} 6: {2, 2} nil ... -localhost> <userinput>lua for v in box.space[0].index[1]:iterator(box.index.ALL) do print(v) end</userinput> +localhost> <userinput>lua v = box.space[0].index[1]:iterator(box.index.ALL)</userinput> +--- +... +localhost> <userinput>lua for v in v do print(v) end</userinput> --- 1: {1, 0} 2: {1, 1} @@ -1813,16 +2194,23 @@ error: 'Iterator type is not supported' <varlistentry> <term> - <emphasis role="lua">index:min()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>]:min()</emphasis> </term> <listitem> - <para> - The smallest value in the index. Available only for - indexes of type 'TREE'. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + Find the minimum value in the specified index. + </para> + <para> + Returns: (type = tuple) the tuple for the first key in the index. + </para> + <para> + Possible errors: index is not of type 'TREE'. + </para> + <para> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.space[0].index[0]:min()</userinput> --- - - 'Alpha': {55, 'This is the first tuple!'} + - 'Alpha!': {55, 'This is the first tuple!'} ... </programlisting> </para> @@ -1831,38 +2219,51 @@ error: 'Iterator type is not supported' <varlistentry> <term> - <emphasis role="lua">index:max()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>]:max()</emphasis> </term> <listitem> - <para> - The biggest value in the index. Available only for - indexes of type 'TREE'. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + Find the maximum value in the specified index. + </para> + <para> + Returns: (type = tuple) the tuple for the last key in the index. + </para> + <para> + Possible errors: index is not of type 'TREE'. + </para> + <para> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.space[0].index[0]:max()</userinput> --- - - 'Gamma': {55, 'This is the third tuple!'} + - 'Gamma!': {55, 'This is the third tuple!'} ... </programlisting> - </para> + </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">index:random(randint)</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>]:random(<replaceable>random-value</replaceable>)</emphasis> </term> <listitem> - <para> - Return a random value from an index. A random - non-negative integer must be supplied as input, and - a value is selected accordingly in index-specific fashion. - 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. - <bridgehead renderas="sect4">Example</bridgehead> + <para> + 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. + </para> + <para> + Parameters: <code>random-number</code> = an arbitrary non-negative integer. + </para> + <para> + Returns: (type = tuple) the tuple for the random key in the index. + </para> + <para> + <bridgehead renderas="sect4">Example</bridgehead> <programlisting>localhost> <userinput>lua box.space[0].index[0]:random(1)</userinput> --- - - 1635018050: {66, 'This is the second tuple!'} + - 'Beta!': {66, 'This is the second tuple!'} ... </programlisting> </para> @@ -1871,16 +2272,24 @@ error: 'Iterator type is not supported' <varlistentry> <term> - <emphasis role="lua">index:count()</emphasis> + <emphasis role="lua">box.space[<replaceable>space-number</replaceable>].index[<replaceable>index-number</replaceable>]:count(<replaceable>key-value</replaceable>)</emphasis> </term> <listitem> - <para> - Iterate over an index, counting the number of tuples which equal the - provided search criteria. The argument can either point to a - tuple, a key, or one or more key parts. Returns the number of matched - tuples. - <bridgehead renderas="sect4">Example</bridgehead> -<programlisting>localhost> <userinput>lua box.space[0].index[0]:count("Alpha")</userinput> + <para> + Iterate over an index, counting the number of tuples which equal the + provided search criteria. + </para> + <para> + Parameters: <code>key-value</code> = the value which must match the key(s) + in the specified index. The type may be a list of field-values, or a tuple containg + only the field-values. + </para> + <para> + Returns: (type = number) the number of matching index keys. + </para> + <para> + <bridgehead renderas="sect4">Example</bridgehead> +<programlisting>localhost> <userinput>lua box.space[0].index[0]:count('Alpha!')</userinput> --- - 1 ... @@ -1894,49 +2303,49 @@ error: 'Iterator type is not supported' <section xml:id="sp-box-fiber"> <title>Package <code>box.fiber</code></title> - <para>Functions in this package allow for creating, running and - managing <emphasis>fibers</emphasis>. + <para> + The <code>box.fiber</code> package allows for creating, running and managing <emphasis>fibers</emphasis>. </para> <para> -A fiber is a set of instructions which are executed -with cooperative multitasking. Fibers managed by the -box.fiber library are associated with a user-supplied function -called the <emphasis>fiber function</emphasis>. - -A fiber has three possible states: running, suspended or dead. -When a fiber is created with <code>box.fiber.create()</code>, it is suspended. -When a fiber is started with <code>box.fiber.resume()</code>, it is running. -When a fiber yields control with <code>box.fiber.yield()</code>, it is suspended. -When a fiber ends (because the fiber function ends), it is dead. + A fiber is a set of instructions which are executed + with cooperative multitasking. Fibers managed by the + box.fiber package are associated with a user-supplied function + called the <emphasis>fiber function</emphasis>. + + A fiber has three possible states: running, suspended or dead. + When a fiber is created with <code>box.fiber.create()</code>, it is suspended. + When a fiber is started with <code>box.fiber.resume()</code>, it is running. + When a fiber yields control with <code>box.fiber.yield()</code>, it is suspended. + When a fiber ends (because the fiber function ends), it is dead. </para> <para> -A fiber can also be attached or detached. -An attached fiber is a child of the creator, -and is running only if the creator has called -<code>box.fiber.resume()</code>. A detached fiber is a child of -the Tarantool internal <quote>sched</quote> fiber, and gets -scheduled only if there is a libev event associated -with it. -To detach, a running fiber must invoke <code>box.fiber.detach()</code>. -A detached fiber loses connection with its parent forever. + A fiber can also be attached or detached. + An attached fiber is a child of the creator, + and is running only if the creator has called + <code>box.fiber.resume()</code>. A detached fiber is a child of + the Tarantool internal <quote>sched</quote> fiber, and gets + scheduled only if there is a libev event associated + with it. + To detach, a running fiber should invoke <code>box.fiber.wrap()</code>. + A detached fiber loses connection with its parent forever. </para> <para> -All fibers are part of the fiber registry, <code>box.fiber</code>. -This registry can be searched (<code>box.fiber.find()</code>) -either by fiber id (fid), which is numeric, or by fiber name, -which is a string. If there is more than one fiber with the given -name, the first fiber that matches is returned. + All fibers are part of the fiber registry, <code>box.fiber</code>. + This registry can be searched (<code>box.fiber.find()</code>) + either by fiber id (fid), which is numeric, or by fiber name, + which is a string. If there is more than one fiber with the given + name, the first fiber that matches is returned. </para> <para> -A runaway fiber can be stopped with <code>box.fiber.cancel()</code>. -However, <code>box.fiber.cancel()</code> is advisory — it works -only if the runaway fiber calls <code>box.fiber.testcancel()</code> -once in a while. Most <code>box.*</code> hooks, such as <code>box.delete()</code> -or <code>box.update()</code>, do call <code>box.fiber.testcancel()</code>. -<code>box.select()</code> does not. -In practice, a runaway fiber can only become unresponsive -if it does many computations and does not check -whether it's been canceled. + A runaway fiber can be stopped with <code>box.fiber.cancel()</code>. + However, <code>box.fiber.cancel()</code> is advisory — it works + only if the runaway fiber calls <code>box.fiber.testcancel()</code> + once in a while. Most <code>box.*</code> hooks, such as <code>box.delete()</code> + or <code>box.update()</code>, do call <code>box.fiber.testcancel()</code>. + <code>box.select()</code> does not. + In practice, a runaway fiber can only become unresponsive + if it does many computations and does not check + whether it's been canceled. <!-- In addition to the advisory cancellation, configuration parameter <code>lua_timeout</code> can be used to cancel runaway Lua @@ -1944,154 +2353,217 @@ procedures. --> </para> <para> -The other potential problem comes from detached -fibers which never get scheduled, because they are not subscribed -to any events, or because no relevant events occur. Such morphing fibers -can be killed with <code>box.fiber.cancel()</code> at any time, -since <code>box.fiber.cancel()</code> -sends an asynchronous wakeup event to the fiber, -and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs. + The other potential problem comes from detached + fibers which never get scheduled, because they are not subscribed + to any events, or because no relevant events occur. Such morphing fibers + can be killed with <code>box.fiber.cancel()</code> at any time, + since <code>box.fiber.cancel()</code> + sends an asynchronous wakeup event to the fiber, + and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs. </para> - <para>Like all Lua objects, dead fibers are - garbage collected. The garbage collector frees pool allocator - memory owned by the fiber, resets all fiber data, and returns - the fiber (now called a fiber carcass) to the fiber pool. - The carcass can be reused when another fiber is created. + <para> + Like all Lua objects, dead fibers are + garbage collected. The garbage collector frees pool allocator + memory owned by the fiber, resets all fiber data, and returns + the fiber (now called a fiber carcass) to the fiber pool. + The carcass can be reused when another fiber is created. </para> <variablelist xml:id="box.fiber"> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.id">box.fiber.id(fiber) </emphasis> + <emphasis role="lua" xml:id="box.fiber.id">box.fiber.id(<replaceable>fiber</replaceable>) </emphasis> </term> - <listitem><simpara>Return a numeric id of the fiber.</simpara></listitem> + <listitem> + <para> + Returns: (type = number) id of the fiber. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.fiber.self">box.fiber.self() </emphasis> </term> - <listitem><simpara>Return <code>box.fiber</code> userdata - object for the currently scheduled fiber.</simpara></listitem> + <listitem> + <para> + Returns: (type = userdata) <code>box.fiber</code> + object for the currently scheduled fiber. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.find">box.fiber.find(id) </emphasis> + <emphasis role="lua" xml:id="box.fiber.find">box.fiber.find(<replaceable>id</replaceable>) </emphasis> </term> - <listitem><simpara>Locate a fiber userdata object by id.</simpara></listitem> + <listitem> + <para> + Locate a fiber userdata object by id. + </para> + <para> + Returns: (type = userdata) box.fiber object for the specified fiber. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.create">box.fiber.create(function) </emphasis> + <emphasis role="lua" xml:id="box.fiber.create">box.fiber.create(<replaceable>function-name</replaceable>) </emphasis> </term> - <listitem><simpara> - Create a fiber for a <code>function</code>. There will be an error if the function does not exist or if a recursion limit is hit. - </simpara> + <listitem> + <para> + Create a fiber. + </para> + <para> + Parameters: <code>function-name</code> = the function that the fiber is associated with. + </para> + <para> + Returns: (type = userdata) the box.fiber object of the new fiber. + </para> + <para> + Possible errors: the function does not exist or if a recursion limit is hit. + </para> </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.resume">box.fiber.resume(fiber, ...) </emphasis> + <emphasis role="lua" xml:id="box.fiber.resume">box.fiber.resume(<replaceable>fiber, ...</replaceable>) </emphasis> </term> - <listitem><simpara>Resume a created - or suspended fiber.</simpara></listitem> + <listitem> + <para> + Resume a created + or suspended fiber. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.yield" xreflabel="box.fiber.yield([arguments])">box.fiber.yield(...) </emphasis> + <emphasis role="lua" xml:id="box.fiber.yield" xreflabel="box.fiber.yield">box.fiber.yield(<replaceable>yield-arguments</replaceable>) </emphasis> </term> - <listitem><simpara> - If the fiber is attached, yield control to the calling fiber if the fiber - is attached; otherwise, yield to sched. - If the fiber is attached, arguments passed - to box.fiber.yield are passed on to the calling fiber. - If the fiber is detached, <code>box.fiber.yield()</code> - arguments passed to box.fiber.yield are returned after temporarily - yielding control back to the scheduler. - </simpara></listitem> + <listitem> + <para> + If the fiber is attached, yield control to the calling fiber if the fiber + is attached; otherwise, yield to sched. + </para> + <para> + Parameters: <code>yield-arguments</code>: + If the fiber is attached, arguments passed + to box.fiber.yield are passed on to the calling fiber. + If the fiber is detached, <code>box.fiber.yield()</code> + arguments passed to box.fiber.yield are returned after temporarily + yielding control back to the scheduler. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.fiber.detach">box.fiber.detach()</emphasis> </term> - <listitem><simpara> - Detach the current fiber. This is a cancellation point. This is a yield point. - </simpara></listitem> + <listitem> + <para> + Detach the current fiber. This is a cancellation point. This is a yield point. + It is usually more convenient to use <code>box.fiber.wrap()</code> for detaching. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.wrap">box.fiber.wrap(function, ...)</emphasis> + <emphasis role="lua" xml:id="box.fiber.wrap">box.fiber.wrap(<replaceable>function, function-arguments</replaceable>)</emphasis> </term> - <listitem><simpara> - This is a quick way to create and start a detached - fiber. The fiber function is passed in the first - argument, the function arguments follow. The fiber is - created, detached, and resumed immediately. - </simpara></listitem> + <listitem> + <para> + This is a quick way to create and start a detached + fiber. The fiber is + created, detached, and resumed immediately. + </para> + <para> + Parameters: <code>function</code> = the function to be associated with the fiber, + <code>function-arguments</code> = what will be passed to the function. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.sleep">box.fiber.sleep(time)</emphasis> + <emphasis role="lua" xml:id="box.fiber.sleep">box.fiber.sleep(<replaceable>time</replaceable>)</emphasis> </term> - <listitem><simpara> - Yield to the sched fiber and sleep <code>time</code> seconds. - Only the current fiber can be made to sleep. - </simpara></listitem> + <listitem> + <para> + Yield to the sched fiber and sleep for the specified number of seconds. + Only the current fiber can be made to sleep. + </para> + <para> + Parameters: <code>time</code> = number of seconds to sleep. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.status">box.fiber.status(fiber)</emphasis> + <emphasis role="lua" xml:id="box.fiber.status">box.fiber.status(<replaceable>[fiber]</replaceable>)</emphasis> </term> - <listitem><simpara> - Returns the status of <code>fiber</code>. If no argument is - provided, the current fiber's status is returned. The - status can be one of: <quote>dead</quote>, - <quote>suspended</quote>, <quote>attached</quote> - or <quote>running</quote>. - </simpara></listitem> + <listitem> + <para> + Return the status of the specified fiber. + </para> + <para> + Parameters: <code>fiber></code> = the fiber to be checked -- if this is not + supplied, then the current fiber is to be checked. + </para> + <para> + Returns: (type = string) the status of <code>fiber</code>. + One of: <quote>dead</quote>, + <quote>suspended</quote>, <quote>attached</quote> + or <quote>running</quote>. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua" xml:id="box.fiber.cancel">box.fiber.cancel(fiber)</emphasis> + <emphasis role="lua" xml:id="box.fiber.cancel">box.fiber.cancel(<replaceable>fiber</replaceable>)</emphasis> </term> - <listitem><simpara> - Cancel a <code>fiber</code>. - Running and suspended fibers can be canceled. - Returns an error if the subject fiber does not permit cancel. - </simpara></listitem> + <listitem> + <para> + Cancel a <code>fiber</code>. + Running and suspended fibers can be canceled. + </para> + <para> + Parameters: <code>fiber</code> = the fiber to be canceled. + </para> + <para> + Possible errors: the specified fiber does not permit cancel. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua" xml:id="box.fiber.testcancel">box.fiber.testcancel()</emphasis> </term> - <listitem><simpara> - Check if the current fiber has been canceled and - throw an exception if this is the case. - </simpara></listitem> + <listitem> + <para> + Check if the current fiber has been canceled and + throw an exception if this is the case. + </para> + </listitem> </varlistentry> </variablelist> <para> <bridgehead renderas="sect4">Example</bridgehead> Make the function which will be associated with the fiber. -When this function gets invoked, it will immediately "detach" -so it will be running independently of the caller, and then -will enter an infinite loop ("while 0 == 0" is an infinite -loop). Each iteration of the loop adds 1 to a global variable -named gvar, then goes to sleep for 2 seconds, -then yields. The sleep causes an implicit box.fiber.yield().<programlisting> +This function contains an infinite loop +("while 0 == 0" is always true). +Each iteration of the loop adds 1 to a global variable +named gvar, then goes to sleep for 2 seconds. +The sleep causes an implicit box.fiber.yield().<programlisting> <prompt>localhost></prompt><userinput> setopt delimiter = '!'</userinput> -<prompt>localhost></prompt><userinput> lua function function_x ()</userinput> -<prompt> -></prompt><userinput> box.fiber.detach()</userinput> +<prompt>localhost></prompt><userinput> lua function function_x()</userinput> <prompt> -></prompt><userinput> gvar = 0</userinput> <prompt> -></prompt><userinput> while 0 == 0 do</userinput> <prompt> -></prompt><userinput> gvar = gvar + 1</userinput> @@ -2101,35 +2573,36 @@ then yields. The sleep causes an implicit box.fiber.yield().<programlisting> --- ... <prompt>localhost></prompt><userinput> setopt delimiter = ''!</userinput></programlisting> -Make the fiber and associate the function with it. - Get the id of the fiber (fid), to be used in later displays.<programlisting> -<prompt>localhost></prompt><userinput> lua fiber_of_x = box.fiber.create(function_x)</userinput> ---- -... -<prompt>localhost></prompt><userinput> lua fid = box.fiber.id(fiber_of_x)</userinput> +Make a fiber, associate function_x with the fiber, +and start function_x. It will immediately "detach" +so it will be running independently of the caller. +<programlisting> +<prompt>localhost></prompt><userinput> lua fiber_of_x = box.fiber.wrap(function_x)</userinput> --- ...</programlisting> -"Resume" the fiber. This causes invocation of the function.<programlisting> -<prompt>localhost></prompt><userinput> lua box.fiber.resume(fiber_of_x)</userinput> + +Get the id of the fiber (fid), to be used in later displays.<programlisting> +<prompt>localhost></prompt><userinput> lua fid = box.fiber.wrap(function_x)</userinput> --- -...</programlisting> +... +</programlisting> Pause for a while, while the detached function runs. Then ... Display the fiber id, the fiber status, and gvar (gvar will have gone up a bit depending how long the pause lasted). The status is suspended because the fiber spends almost all its time sleeping or yielding.<programlisting> -<prompt>localhost></prompt><userinput> lua print("fiber=",fid,". ",box.fiber.status(fiber_of_x),". gvar=",gvar)</userinput> +<prompt>localhost></prompt><userinput> lua print('#',fid,'. ',box.fiber.status(fiber_of_x),'. gvar=',gvar)</userinput> --- fiber=104. suspended. gvar=9 ...</programlisting> Pause for a while, while the detached function runs. Then ... Cancel the fiber. Then, once again ... Display the fiber id, the fiber status, and gvar (gvar will have -gone up a bit more depending how long pause lasted). This time +gone up a bit more depending how long the pause lasted). This time the status is dead because the cancel worked.<programlisting> <prompt>localhost></prompt><userinput> lua box.fiber.cancel(fiber_of_x)</userinput> --- ... -<prompt>localhost></prompt><userinput> lua print("fiber=",fid,". ",box.fiber.status(fiber_of_x),". gvar=",gvar)</userinput> +<prompt>localhost></prompt><userinput> lua print('#',fid,'. ',box.fiber.status(fiber_of_x),'. gvar=',gvar)</userinput> --- fiber=104. dead. gvar=22 ...</programlisting> @@ -2142,49 +2615,67 @@ fiber=104. dead. gvar=22 <section xml:id="sp-box-session"> <title>Package <code>box.session</code></title> <para> - Query session state, write to a session-specific temporary - record, or set up triggers which will fire when a session - starts or ends. A <emphasis>session</emphasis> is an object associated with each client connection. + The <code>box.session</code> package allows querying the session state, + writing to a session-specific temporary record, or setting up triggers + which will fire when a session starts or ends. + A <emphasis>session</emphasis> is an object associated with each client connection. </para> <variablelist> <varlistentry> <term> <emphasis role="lua">box.session.id() </emphasis> </term> - <listitem><simpara>Return the unique numeric identifier - (ID) for the current session. The result can be 0 meaning - there is no session (for example because a function is - running in a detached fiber). - </simpara></listitem> + <listitem> + <para> + Returns: (type = number) the unique identifier + (ID) for the current session. The result can be 0 meaning + there is no session (for example because a function is + running in a detached fiber). + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.session.exists(id) </emphasis> + <emphasis role="lua">box.session.exists(<replaceable>id</replaceable>) </emphasis> </term> - <listitem><simpara>If the specified session exists, return true (1). - otherwise return false (0).</simpara></listitem> + <listitem> + <para> + Returns: (type = number) 1 if the session exists, + 0 if the session does not exist. + </para> + </listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.session.peer(id) </emphasis> + <emphasis role="lua">box.session.peer(<replaceable>id</replaceable>) </emphasis> </term> - <listitem><simpara>If the specified session exists, return the host - address and port of the session peer, for example "127.0.0.1:55457". - Otherwise return "0.0.0.0:0". The command is executed on the server, - so the "local name" is the server's host and administrative port, - and the "peer name" is the client's host and port.</simpara></listitem> + <listitem> + <para> + Parameters: <code>id</code> = the unique identifier of the session. + </para> + <para> + Returns: (type = string) If the specified session exists, the host + address and port of the session peer, for example "127.0.0.1:55457". + If the specified session does not exist, "0.0.0.0:0". The command is executed on the server, + so the "local name" is the server's host and administrative port, + and the "peer name" is the client's host and port. + </para> + </listitem> </varlistentry> <varlistentry> <term> <emphasis role="lua">box.session.storage</emphasis> </term> - <listitem><simpara>A Lua table that can hold arbitrary - unordered session-specific names and values, which will last until - the session ends. - </simpara></listitem> + <listitem> + <para> + A Lua table that can hold arbitrary + unordered session-specific names and values, which will last until + the session ends. + </para> + </listitem> </varlistentry> </variablelist> @@ -2194,21 +2685,21 @@ fiber=104. dead. gvar=22 --- - 127.0.0.1:45129 ... -<prompt>localhost></prompt><userinput> lua box.session.storage.random_memorandum = "Don't forget to buy eggs."</userinput> +<prompt>localhost></prompt><userinput> lua box.session.storage.random_memorandum = "Don't forget the eggs."</userinput> --- ... <prompt>localhost></prompt><userinput> lua box.session.storage.radius_of_mars = 3396</userinput> --- ... -<prompt>localhost></prompt><userinput> lua for k,v in pairs(box.session.storage) do print(k,' ',v) end</userinput> +<prompt>localhost></prompt><userinput> lua for k, v in pairs(box.session.storage) do print(k, ' ', v) end</userinput> --- radius_of_mars 3396 -random_memorandum Don't forget to buy eggs. +random_memorandum Don't forget the eggs. ...</programlisting> </para> <para> -See <olink targetptr="sp-box-session-triggers">the triggers chapter</olink> +See <olink targetptr="sp-box-session-triggers">the section "Triggers on connect and disconnect"</olink> for instructions about defining triggers for connect and disconnect events with <code>box.session.on_connect()</code> and <code>box.session.on_disconnect()</code>. </para> @@ -2219,108 +2710,147 @@ for instructions about defining triggers for connect and disconnect events <section xml:id="sp-box-ipc"> <title>Package <code>box.ipc</code> — inter procedure communication</title> <para> - Send and receive messages between different procedures of a session. + The <code>box.ipc</code> package allows sending and receiving messages between different procedures of a session. </para> <para> - Call <code>box.ipc.channel()</code> to allocate space and get a channel object, which will be - called <code>channel</code> for examples in this section. - Call the other box.ipc() routines, passing <code>channel</code>, to send messages, receive messages, or check ipc status. - Message exchange is synchronous. - The channel is garbage collected when no one is using it, as with any - other Lua object. - Object-oriented and functional APIs are equivalent, so <code>channel:put(message)</code> - is the same as <code>box.ipc.channel.put(channel, message)</code>. + Call <code>box.ipc.channel()</code> to allocate space and get a channel object, which will be + called <code>channel</code> for examples in this section. + Call the other box.ipc() routines, passing <code>channel</code>, to send messages, receive messages, or check ipc status. + Message exchange is synchronous. + The channel is garbage collected when no one is using it, as with any + other Lua object. + Object-oriented and functional APIs are equivalent, so <code>channel:put(message)</code> + is the same as <code>box.ipc.channel.put(channel, message)</code>. </para> <variablelist xml:id="box.ipc"> - <simpara> - </simpara> + <para> + </para> <varlistentry> - <term><emphasis role="lua">box.ipc.channel(capacity)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel(<replaceable>capacity-number</replaceable>)</emphasis></term> <listitem> - <simpara> - Create a new communication channel. The capacity should be - a positive integer as great as the maximum number of slots - (spaces for get or put or broadcast messages) - that might be pending at any given time. - </simpara> + <para> + Create a new communication channel. + </para> + <para> + Parameters: <code>capacity-number</code> = + a positive integer as great as the maximum number of slots + (spaces for get or put or broadcast messages) + that might be pending at any given time. + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.put(channel, message, timeout)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.put(<replaceable>channel, message[, timeout]</replaceable>)</emphasis></term> <listitem> - <simpara> + <para> Send a message using a channel. If the channel is full, <code>box.ipc.channel.put()</code> blocks until there is a free slot in the channel. - If <code>timeout</code> is provided, + </para> + <para> + Parameters: <code>channel</code>, <code>message</code>, <code>timeout</code>. + </para> + <para> + Returns: If <code>timeout</code> is provided, and the channel doesn't become empty for the duration of the timeout, <code>box.ipc.channel.put()</code> returns false. Otherwise it returns true. - </simpara> + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.get(channel, timeout)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.get(<replaceable>channel[, timeout]</replaceable>)</emphasis></term> <listitem> - <simpara> + <para> Fetch a message from a channel. If the channel is empty, <code>box.ipc.channel.get()</code> blocks until there is a message. - If <code>timeout</code> is provided, + </para> + <para> + Parameters: <code>channel</code>, <code>timeout</code>. + </para> + <para> + Possible errors: If <code>timeout</code> is provided, and there are no new messages for the duration of the timeout, <code>box.ipc.channel.get()</code> returns error. - </simpara> + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.broadcast(channel, message, timeout)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.broadcast(<replaceable>channel, message, timeout</replaceable>)</emphasis></term> <listitem> - <simpara> + <para> If the channel is empty, <code>box.ipc.channel.broadcast()</code> is equivalent to <code>box.ipc.channel.put()</code>. Otherwise, <code>box.ipc.channel.broadcast()</code> sends the message to all readers of the channel. - </simpara> + </para> + <para> + Parameters: <code>channel</code>, <code>message</code>, <code>timeout</code>. + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.is_empty(channel)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.is_empty(<replaceable>channel</replaceable>)</emphasis></term> <listitem> - <simpara> - Return true if the specified channel is empty (has no messages). - </simpara> + <para> + Check whether the specified channel is empty (has no messages). + </para> + <para> + Parameters: <code>channel</code>. + </para> + <para> + Returns: (type = boolean) true if the specified channel is empty. + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.is_full(channel)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.is_full(<replaceable>channel</replaceable>)</emphasis></term> <listitem> - <simpara> - Return true if the specified channel is full (has no room for a new message). - </simpara> + <para> + Check whether the specified channel is full. + </para> + <para> + Parameters: <code>channel</code>. + </para> + <para> + Returns: (type = boolean) true if the specified channel is full (has no room for a new message). + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.has_readers(channel)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.has_readers(<replaceable>channel</replaceable>)</emphasis></term> <listitem> - <simpara> - Return true if the specified channel is empty and has readers waiting - for a message (because they have issued <code>box.ipc.channel.get()</code> and then - blocked). - Otherwise return false. - </simpara> + <para> + Check whether the specified channel is empty and has readers waiting + for a message (because they have issued <code>box.ipc.channel.get()</code> and then + blocked). + </para> + <para> + Parameters: <code>channel</code>. + </para> + <para> + Returns: (type = boolean) true if blocked users are waiting. Otherwise false. + </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">box.ipc.channel.has_writers(channel)</emphasis></term> + <term><emphasis role="lua">box.ipc.channel.has_writers(<replaceable>channel</replaceable>)</emphasis></term> <listitem> - <simpara> - Return true if the specified channel is full and has writers waiting + <para> + Check whether the specified channel is full and has writers waiting (because they have issued <code>box.ipc.channel.put()</code> and then blocked - due to lack of room). Otherwise return false. - </simpara> + due to lack of room). + </para> + <para> + Parameters: <code>channel</code>. + </para> + <para> + Returns: (type = boolean) true if blocked users are waiting. Otherwise false. + </para> </listitem> </varlistentry> </variablelist> @@ -2359,7 +2889,7 @@ function producer_fiber() ... if channel:has_readers() then - # there are some fibers that wait are waiting for data + # there are some fibers that are waiting for data end ... @@ -2390,16 +2920,15 @@ end <section xml:id="sp-box-socket"> <title>Package <code>box.socket</code> — TCP and UDP sockets</title> <variablelist xml:id="box.socket"> - <simpara> - BSD sockets is a mechanism to exchange data with a local or - remote host in connection-oriented (TCP) or datagram-oriented - (UDP) mode. + <para> + The <code>box.socket</code> package allows exchanging data via BSD sockets + with a local or remote host in connection-oriented (TCP) or datagram-oriented (UDP) mode. Semantics of the calls in the <code>box.socket</code> API closely follow semantics of the corresponding POSIX calls. Function names and signatures are mostly compatible with <link xlink:href="http://w3.impa.br/~diego/software/luasocket/">luasocket</link>. - </simpara> - <simpara> + </para> + <para> Similarly to luasocket, <code>box.socket</code> doesn't throw exceptions on errors. On success, most calls return a socket object. On error, a multiple return of <code>nil, status, errno, errstr</code> @@ -2415,46 +2944,47 @@ end success returns the number of bytes sent, and the <code>status</code> is, again, <code>nil</code>. On error or timeout <code>0</code> is returned, followed by status, error number and message. - </simpara> - <simpara> + </para> + <para> The last error can be retrieved from the socket using <code>socket:error()</code>. Any call except <code>error()</code> clears the last error first (but may set a new one). - </simpara> - <simpara> + </para> + <para> Calls which require a socket address and in POSIX expect <code>struct sockaddr_in</code>, in <code>box.socket</code> simply accept host name and port as additional arguments. Name resolution is done automatically. If it fails, status is set to <code>"error"</code>, errno is set to <code>-1</code> and error string is set to <code>"Host name resolution failed"</code>. - </simpara> - <simpara> + </para> + <para> All calls that can take time block the calling fiber and can get it preempted. The implementation, however, uses non-blocking cooperative I/O, so Tarantool continues processing queries while a call is blocked. A timeout can be provided for any socket call which can take a long time. - </simpara> - <simpara> - As with all other <code>box</code> libraries, the API can be used + </para> + <para> + As with all other <code>box</code> functions, the API can be used in procedural style (e.g. <code>box.socket.close(socket)</code>) as well as in object-oriented style (<code>socket:close()</code>). - </simpara> - <simpara> + </para> + <para> A closed socket should not be used any more. Alternatively, the socket will be closed when its userdata is garbage collected by Lua. - </simpara> + </para> <varlistentry> <term><emphasis role="lua">box.socket.tcp()</emphasis></term> <listitem> <para> Create a new TCP socket. - <bridgehead renderas="sect4">Returns</bridgehead> - A new socket or <code>nil</code>. + </para> + <para> + Returns: (type = userdata) a new socket, or <code>nil</code>. </para> </listitem> </varlistentry> @@ -2464,34 +2994,42 @@ end <listitem> <para> Create a new UDP socket. - <bridgehead renderas="sect4">Returns</bridgehead> - A new socket or <code>nil</code>. + </para> + <para> + Returns: (type = userdata) a new socket, or <code>nil</code>. </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:connect(host, port, [timeout])</emphasis></term> + <term><emphasis role="lua">socket:connect(<replaceable>host, port, [timeout</replaceable>])</emphasis></term> <listitem> <para> Connect a socket to a remote host. Can be used with IPv6 and IPv4 addresses, as well as domain names. If multiple addresses - correspond to a domain, tries them all until successfully - connected. - <bridgehead renderas="sect4">Returns</bridgehead> - Returns a connected socket on success, + correspond to a domain, tries them all until connection succeeds. + </para> + <para> + Parameters: <code>host</code>, <code>port</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = userdata) a connected socket on success, <code>nil, status, errno, errstr</code> on error or timeout. </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:send(data, [timeout])</emphasis></term> + <term><emphasis role="lua">socket:send(<replaceable>data, [timeout]</replaceable>)</emphasis></term> <listitem> <para> Send data over a connected socket. - <bridgehead renderas="sect4">Returns</bridgehead> - The number of bytes sent. On success, this is exactly + </para> + <para> + Parameters: <code>data</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = number) the number of bytes sent. On success, this is exactly the length of <code>data</code>. In case of error or timeout, returns the number of bytes sent before error, followed by <code>status, errno, errstr</code>. @@ -2500,14 +3038,18 @@ end </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:recv(size, [timeout])</emphasis></term> + <term><emphasis role="lua">socket:recv(<replaceable>size, [timeout]</replaceable>)</emphasis></term> <listitem> <para> Read <code>size</code> bytes from a connected socket. An internal read-ahead buffer is used to reduce the cost of this call. - <bridgehead renderas="sect4">Returns</bridgehead> - A string of the requested length on success. + </para> + <para> + Parameters: <code>size</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = string) a string of the requested length on success. On error or timeout, returns an empty string, followed by <code>status, errno, errstr</code>. If there was some data read before a timeout occurred, it @@ -2520,26 +3062,27 @@ end </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:readline([limit], [separator list], [timeout])</emphasis></term> + <term><emphasis role="lua">socket:readline(<replaceable>[limit] [, separator list] [, timeout]</replaceable>)</emphasis></term> <listitem> <para> Read a line from a connected socket. </para> <para> <code>socket:readline()</code> with no arguments reads data from a socket - until '\n' or eof. - If a limit is set, the call reads data until a separator is found, - or the limit is reached. By default, there is no limit. - Instead of the default separator, a Lua table can be used - with one or multiple separators. Then the data is read - until the first matching separator is found. - </para> - <para> - <bridgehead renderas="sect4">Returns</bridgehead> - A Lua string with data in case of success - or an empty string in case of error. When - multiple separators were provided in a separator - table, the matched separator is returned as the third argument. + until '\n' (line feed) or eof (end of transmission). + </para> + <para> + Parameters: <code>limit</code> — maximum number of bytes to read. The function reads + until a separator is seen, or until (limit) bytes have been read. The default is "no limit". + <code>separator list</code> — a Lua table containing one or more separators. + The function reads until one of the separators is seen. The default is a Lua table containing '\n'. + <code>timeout</code> — number of seconds to wait before returning an error. + </para> + <para> + Returns: + (type = string) A Lua string with data if success, + an empty string if error. If multiple separators were passed in <code>separator list</code>, + the separator which matched is also shown, as the third part of the return. <table> <title><code>readline()</code> returns</title> <tgroup cols="2" align="left" colsep="1" rowsep="1"> @@ -2567,7 +3110,7 @@ end </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:bind(host, port[, timeout])</emphasis></term> + <term><emphasis role="lua">socket:bind(<replaceable>host, port[, timeout]</replaceable>)</emphasis></term> <listitem> <para> Bind a socket to the given host/port. @@ -2576,10 +3119,14 @@ end can be used to accept new connections, after it's been put in listen mode. The timeout is used for name resolution only. If host - name is an IP address, the call never yields and + name is an IP address, <code>socket:bind</code> never yields and the timeout is unused. - <bridgehead renderas="sect4">Returns</bridgehead> - Socket object on success, <code>nil, status, errno, errstr</code> on error. + </para> + <para> + Parameters: <code>host</code>, <code>port</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = userdata) a socket object on success, <code>nil, status, errno, errstr</code> on error. </para> </listitem> </varlistentry> @@ -2591,44 +3138,57 @@ end Start listening for incoming connections. The listen backlog, on Linux, is taken from <filename>/proc/sys/net/core/somaxconn</filename>, whereas on BSD it is set to <constant>SOMAXCONN</constant>. - <bridgehead renderas="sect4">Returns</bridgehead> - Socket on success, <code>nil, "error", errno, errstr</code> on error. + </para> + <para> + Returns: (type = userdata) a socket object on success, <code>nil, "error", errno, errstr</code> on error. </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:accept([timeout])</emphasis></term> + <term><emphasis role="lua">socket:accept(<replaceable>[timeout]</replaceable>)</emphasis></term> <listitem> <para> Wait for a new client connection and create a connected socket. - <bridgehead renderas="sect4">Returns</bridgehead> - <code>peer_socket, nil, peer_host, peer_port</code> on success. - <code>nil, status, errno, errstr</code> on error. + </para> + <para> + Parameters: <code>timeout</code>. + </para> + <para> + Returns: (type = userdata) <code>peer_socket, nil, peer_host, peer_port</code> on success. + <code>nil, status, errno, errstr</code> on error. </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:sendto(data, host, port, [timeout])</emphasis></term> + <term><emphasis role="lua">socket:sendto(<replaceable>data, host, port, [timeout]</replaceable>)</emphasis></term> <listitem> <para> Send a message on a UDP socket to a specified host. - <bridgehead renderas="sect4">Returns</bridgehead> - The number of bytes sent on success, <code>0, status, errno, errstr</code> + </para> + <para> + Parameters: <code>data</code>, <code>host</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = number) the number of bytes sent on success, <code>0, status, errno, errstr</code> on error or timeout. </para> </listitem> </varlistentry> <varlistentry> - <term><emphasis role="lua">socket:recvfrom(limit[, timeout])</emphasis></term> + <term><emphasis role="lua">socket:recvfrom(<replaceable>limit[, timeout]</replaceable>)</emphasis></term> <listitem> <para> Receive a message on a UDP socket. - <bridgehead renderas="sect4">Returns</bridgehead> - Message, <code>nil</code>, client address, client port on success, + </para> + <para> + Parameters: <code>limit</code>, <code>timeout</code>. + </para> + <para> + Returns: (type = string) Message, <code>nil</code>, client address, client port on success, <code>"", status, errno, errstr</code> on error or timeout. </para> </listitem> @@ -2636,14 +3196,17 @@ end <varlistentry> - <term><emphasis role="lua">socket:shutdown(how)</emphasis></term> + <term><emphasis role="lua">socket:shutdown(<replaceable>how</replaceable>)</emphasis></term> <listitem> <para> Shutdown a reading, writing or both ends of a socket. - Accepts box.socket.SHUT_RD, box.socket.SHUT_WR - and box.socket.SHUT_RDWR. - <bridgehead renderas="sect4">Returns</bridgehead> - Socket on success, <code>nil, "error", errno, errstr</code> on error. + </para> + <para> + Parameters: <code>how</code> = box.socket.SHUT_RD, box.socket.SHUT_WR, + or box.socket.SHUT_RDWR. + </para> + <para> + Returns: (type = userdata) Socket on success, <code>nil, "error", errno, errstr</code> on error. </para> </listitem> </varlistentry> @@ -2663,27 +3226,68 @@ end <listitem> <para> Retrieve the last error that occurred on a socket. - <bridgehead renderas="sect4">Returns</bridgehead> - <code>errno, errstr</code>. <code>0, "Success"</code> + </para> + <para> + Returns:(type = number) <code>errno, errstr</code>. <code>0, "Success"</code> if there is no error. </para> </listitem> </varlistentry> </variablelist> + + + <bridgehead renderas="sect4">Example showing use of box.socket over the Internet</bridgehead> + <para> + In this example a connection is made over the internet between the Tarantool server + and <link xlink:href="http://mail.ru">mail.ru</link>, + then an HTTP "get" message is sent, and a response is received: "HTTP/1.0 200 OK". + This is not a useful way to communicate with this particular site, + but shows that the system works. +<programlisting> +<prompt>localhost></prompt> <userinput>lua sock = box.socket.tcp()</userinput> +--- +... +<prompt>localhost></prompt> <userinput>lua type(s)</userinput> +--- + - userdata +... +<prompt>localhost></prompt> <userinput>lua sock:connect('mail.ru', 80)</userinput> +--- + - fd 22, aka 192.168.1.72:49488, peer of 94.100.180.199:80 +... +<prompt>localhost></prompt> <userinput>lua sock:error()</userinput> +--- + - 0 + - Success +... +<prompt>localhost></prompt> <userinput>lua sock:send('GET / HTTP/1.0\n\n')</userinput> +--- + - 16 +... +<prompt>localhost></prompt> <userinput>lua sock:recv(17)</userinput> +--- + - HTTP/1.0 200 OK +... + <prompt>localhost></prompt> <userinput>lua sock:close()</userinput> +--- +... +</programlisting> +</para> + </section> <section xml:id="sp-box-net-box"> <title>Package <code>box.net.box</code> — working with networked Tarantool peers</title> - <simpara> - The <code>box.net</code> library contains connectors to remote database systems. + <para> + The <code>box.net</code> package contains connectors to remote database systems. One variant, <code>box.net.sql</code>, is for connecting to MySQL or MariaDB or PostgreSQL — that variant is the subject of the <quote>SQL DBMS plugins</quote> appendix. In this section the subject is the built-in variant, <code>box.net.box</code>. This is for connecting to tarantool_box servers via a network. - </simpara> + </para> <variablelist xml:id="box.net.box"> - <simpara> + <para> Call <code>box.net.box.new()</code> to connect and get a connection object, which will be called <code>conn</code> for examples in this section. Call the other <code>box.net.box()</code> routines, passing <code>conn</code>, @@ -2691,9 +3295,9 @@ end Call <code>box.net.box.close(conn)</code> to disconnect. Object-oriented and functional APIs are equivalent, so <code>conn:close()</code> is the same as <code>box.net.box.close(conn)</code>. - </simpara> + </para> - <simpara> + <para> All <code>box.net.box</code> methods are fiber-safe, that is, it is safe to share and use the same connection object across multiple concurrent fibers. In fact, it's perhaps the @@ -2705,7 +3309,7 @@ end overall server performance. There are, however, cases when a single connection is not enough — for example when it's necessary to prioritize requests or to use different authentication ids. - </simpara> + </para> <varlistentry> <term> @@ -2718,12 +3322,11 @@ end established on demand, at the time of the first request. It is re-established automatically after a disconnect. The argument - <code>reconnect_interval</code> (in seconds) is - responsible for the amount of time the server + <code>reconnect_interval</code> (in seconds) + specifies the amount of time the server sleeps between failing attempts to reconnect. The returned <code>conn</code> object supports methods for making remote requests, such as select, update or delete. - Example: <code>conn = box.net.box.new('localhost', 33013)</code>. </para> <para> For the local tarantool_box server there is a pre-created always-established @@ -2738,6 +3341,15 @@ end a remote connection, any request can yield, and local database state may have changed by the time it returns. </para> + <para> + Parameters: <code>host</code>, <code>port</code>, <code>reconnect_interval</code>. + </para> + <para> + Returns: (type = userdata) conn object). + </para> + <para> + Example: <code>conn = box.net.box.new('localhost', 33013)</code>. + </para> </listitem> </varlistentry> @@ -2747,7 +3359,9 @@ end <listitem> <para> Execute a PING command. - Returns <code>true</code> on success, + </para> + <para> + Returns: (type = boolean) <code>true</code> on success, <code>false</code> on error. Example: <code>self:ping()</code>. </para> </listitem> @@ -2758,104 +3372,110 @@ end conn:close()</emphasis></term> <listitem> <para> - Close a connection. Example: <code>conn:close()</code>. + Close a connection. </para> <para> - Connection objects are garbage collected just like any other objects - in Lua, so an explicit destruction is not mandatory. - However, since <code>close()</code> is a system call, it - is good programming practice to close a connection - explicitly when it is no longer needed, to avoid lengthy - stalls of the garbage collector. + Connection objects are garbage collected just like any other objects + in Lua, so an explicit destruction is not mandatory. + However, since <code>close()</code> is a system call, it + is good programming practice to close a connection + explicitly when it is no longer needed, to avoid lengthy + stalls of the garbage collector. + </para> + <para> + Example: <code>conn:close()</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.select"> - conn:select(<replaceable>space_no</replaceable>, <replaceable>index_no</replaceable>, ...)</emphasis></term> + conn:select(<replaceable>space-number</replaceable>, <replaceable>index-number</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:select(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select">box.select(...)</code>. - Please note this difference: a local <code>box.select()</code> does not yield, - but a remote <code>conn:select()</code> call does yield, - so local data may change while a remote <code>conn:select()</code> is running. + <code>conn:select(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select">box.select(...)</code>. + Please note this difference: a local <code>box.select()</code> does not yield, + but a remote <code>conn:select()</code> call does yield, + so local data may change while a remote <code>conn:select()</code> is running. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.select_limit"> - conn:select_limit(<replaceable>space_no</replaceable>, <replaceable>index_no</replaceable>, <replaceable>offset</replaceable>, <replaceable>limit</replaceable>, ...)</emphasis></term> + conn:select_limit(<replaceable>space-number</replaceable>, <replaceable>index-number</replaceable>, <replaceable>offset</replaceable>, <replaceable>limit</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:select_limit(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select_limit">box.select_limit(...)</code>. + <code>conn:select_limit(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select_limit">box.select_limit(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.select_range"> - conn:select_range(<replaceable>space_no</replaceable>, <replaceable>index_no</replaceable>, <replaceable>limit</replaceable>, <replaceable>key</replaceable>, ...)</emphasis></term> + conn:select_range(<replaceable>space-number</replaceable>, <replaceable>index-number</replaceable>, <replaceable>limit</replaceable>, <replaceable>key</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:select_range(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select_range">box.select_range(...)</code>. + <code>conn:select_range(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.select_range">box.select_range(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.insert"> - conn:insert(<replaceable>space_no</replaceable>, ...)</emphasis></term> + conn:insert(<replaceable>space-number</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:insert(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.insert">box.insert(...)</code>. + <code>conn:insert(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.insert">box.insert(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.replace"> - conn:replace(<replaceable>space_no</replaceable>, ...)</emphasis></term> + conn:replace(<replaceable>space-number</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:replace(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.replace">box.replace(...)</code>. + <code>conn:replace(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.replace">box.replace(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.update"> - conn:update(<replaceable>space_no</replaceable>, <replaceable>key</replaceable>, <replaceable>format</replaceable>, ...)</emphasis></term> + conn:update(<replaceable>space-number</replaceable>, <replaceable>key</replaceable>, <replaceable>format</replaceable>, ...)</emphasis></term> <listitem> <para> - <code>conn:update(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.update">box.update(...)</code>. + <code>conn:update(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.update">box.update(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.delete"> - conn:delete(<replaceable>space_no</replaceable>, <replaceable>key</replaceable>)</emphasis></term> + conn:delete(<replaceable>space-number</replaceable>, <replaceable>key</replaceable>)</emphasis></term> <listitem> <para> - <code>conn:delete(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.delete">box.delete(...)</code>. + <code>conn:delete(...)</code> is the remote-call equivalent of the local call <code xlink:href="#box.delete">box.delete(...)</code>. </para> </listitem> </varlistentry> <varlistentry> <term><emphasis role="lua" xml:id="box.net.box.call"> - conn:call(<replaceable>proc_name</replaceable> [, <replaceable>arguments</replaceable>])</emphasis></term> + conn:call(<replaceable>function-name</replaceable> [, <replaceable>arguments</replaceable>])</emphasis></term> <listitem> <para> - <code>conn:call('proc','1','2','3')</code> is the remote-call equivalent of <code>CALL proc('1','2','3')</code>. - That is, box.net.box.call is a remote stored-procedure call. - Please keep in mind that the call is using - the binary protocol to pack procedure arguments, - and the binary protocol is type agnostic, so it's recommended - to pass all arguments of remote stored procedure calls as - strings. Example: <code>conn:call("box.select_reverse_range", "1", "4", "10", "Smith")</code>. + <code>conn:call('proc', '1', '2', '3')</code> is the remote-call equivalent of <code>CALL proc('1', '2', '3')</code>. + That is, box.net.box.call is a remote stored-procedure call. + Please keep in mind that the call is using + the binary protocol to pack procedure arguments, + and the binary protocol is type agnostic, so it's recommended + to pass all arguments of remote stored procedure calls as + strings. + </para> + <para> + Example: <code>conn:call("box.select_reverse_range", "1", "4", "10", "Smith")</code>. </para> </listitem> </varlistentry> @@ -2865,10 +3485,10 @@ end conn:timeout(<replaceable>timeout</replaceable>)</emphasis></term> <listitem> <para> - <code>timeout(...)</code> is a wrapper which sets a timeout for the request that follows it. + <code>timeout(...)</code> is a wrapper which sets a timeout for the request that follows it. Example: <code>conn:timeout(0):update('1', 'arg1', 15)</code>. - </para> - <para> + </para> + <para> All remote calls support execution timeouts. Using a wrapper object makes the remote connection API compatible with the local one, removing @@ -2877,7 +3497,7 @@ end it cannot be revoked from the remote server even if a timeout expires: the timeout expiration only aborts the wait for the remote server response, not the request itself. - </para> + </para> </listitem> </varlistentry> </variablelist> @@ -2890,27 +3510,32 @@ end Assume that the tarantool_box server is running on localhost 127.0.0.1:33013. <programlisting><prompt>localhost></prompt> <userinput> setopt delimiter = '!'</userinput> <prompt>localhost></prompt> <userinput> lua function example()</userinput> -<prompt> -></prompt> <userinput> if self:ping() then</userinput> -<prompt> -></prompt> <userinput> print("self:ping() succeeded (not surprising since self is a pre-established connection).")</userinput> +<prompt> -></prompt> <userinput> if box.net.self:ping() then</userinput> +<prompt> -></prompt> <userinput> print('self:ping() succeeded')</userinput> +<prompt> -></prompt> <userinput> print(' (no surprise -- self connection is pre-established)')</userinput> <prompt> -></prompt> <userinput> end</userinput> <prompt> -></prompt> <userinput> if box.cfg.primary_port == 33013 then</userinput> -<prompt> -></prompt> <userinput> print('The local server primary port number is 33013 (the default)')</userinput> +<prompt> -></prompt> <userinput> print('The local server primary port number = 33013 = default')</userinput> <prompt> -></prompt> <userinput> else</userinput> -<prompt> -></prompt> <userinput> print('The local server primary port number is not 33013, so connect will fail')</userinput> +<prompt> -></prompt> <userinput> print('The local server primary port number is not 33013')</userinput> +<prompt> -></prompt> <userinput> print(' (so connect will fail)')</userinput> <prompt> -></prompt> <userinput> end</userinput> <prompt> -></prompt> <userinput> conn = box.net.box.new('127.0.0.1', 33013)</userinput> <prompt> -></prompt> <userinput> conn:delete(0,800)</userinput> <prompt> -></prompt> <userinput> print('conn:delete done on space[0].')</userinput> -<prompt> -></prompt> <userinput> conn:insert(0,800,'data')</userinput> -<prompt> -></prompt> <userinput> print('conn:insert done on space[0], index 0. primary key value = 800.')</userinput> -<prompt> -></prompt> <userinput> wtuple = conn:select(0,0,800)</userinput> -<prompt> -></prompt> <userinput> print('conn:select done on space[0], index 0. number of fields = ', #wtuple)</userinput> -<prompt> -></prompt> <userinput> conn:delete(0,800)</userinput> +<prompt> -></prompt> <userinput> conn:insert(0, 800, 'data')</userinput> +<prompt> -></prompt> <userinput> print('conn:insert done on space[0], index 0')</userinput> +<prompt> -></prompt> <userinput> print(' primary key value = 800.')</userinput> +<prompt> -></prompt> <userinput> wtuple = conn:select(0, 0, 800)</userinput> +<prompt> -></prompt> <userinput> print('conn:select done on space[0], index 0.')</userinput> +<prompt> -></prompt> <userinput> print(' number of fields = ', #wtuple)</userinput> +<prompt> -></prompt> <userinput> conn:delete(0, 800)</userinput> <prompt> -></prompt> <userinput> print('conn:delete done on space[0].')</userinput> -<prompt> -></prompt> <userinput> conn:replace(0,800,'New data','Extra data')</userinput> +<prompt> -></prompt> <userinput> conn:replace(0, 800, 'New data', 'Extra data')</userinput> <prompt> -></prompt> <userinput> print('conn:replace done on space[0].')</userinput> -<prompt> -></prompt> <userinput> conn:timeout(0):update(0,800,'=p=p=p',1, 'Field#1', 2,'Field#2', 3,'Field#3')</userinput> -<prompt> -></prompt> <userinput> print('conn:update done on space[0], timed out after waiting 0 seconds for a return')</userinput> +<prompt> -></prompt> <userinput> conn:timeout(0):update(0, 800, '=p=p', 1, 'Fld#1', 2, 'Fld#2')</userinput> +<prompt> -></prompt> <userinput> print('conn:update done on space[0]')</userinput> +<prompt> -></prompt> <userinput> print(' timed out after waiting 0 seconds for a return')</userinput> <prompt> -></prompt> <userinput> conn:close()</userinput> <prompt> -></prompt> <userinput> print('conn:close done')</userinput> <prompt> -></prompt> <userinput> end!</userinput> @@ -2919,19 +3544,23 @@ end <prompt>localhost></prompt> <userinput> setopt delimiter = ''!</userinput> <prompt>localhost></prompt> <userinput> lua example()</userinput> --- -self:ping() succeeded (not surprising since self is a pre-established connection). -The local server primary port number is 33013 (the default) +self:ping() succeeded + (no surprise -- self connection is pre-established) +The local server primary port number = 33013 = default conn:delete done on space[0]. -conn:insert done on space[0], index 0. primary key value = 800. -conn:select done on space[0], index 0. number of fields = 2 +conn:insert done on space[0], index 0. + primary key value = 800. +conn:select done on space[0], index 0. + number of fields = 2 conn:delete done on space[0]. conn:replace done on space[0]. -conn:update done on space[0], timed out after waiting 0 seconds for a return +conn:update done on space[0], + timed out after waiting 0 seconds for a return conn:close done ... <prompt>localhost></prompt> <userinput> select * from t0 where k0 = 800 # Prove that the update succeeded.</userinput> Select OK, 1 rows affected -[800, 'Field#1', 'Field#2', 'Field#3'] +[800, 'Fld#1', 'Fld#2'] </programlisting> </para> </section> @@ -2943,12 +3572,14 @@ Select OK, 1 rows affected <variablelist> <title>Package <code xml:id="box.cfg">box.cfg</code></title> - <para>This package provides read-only access to - all server configuration parameters.</para> + <para> + The box.cfg package provides read-only access to + all server configuration parameters. + </para> <varlistentry> <term><emphasis role="lua">box.cfg</emphasis></term> <listitem><bridgehead renderas="sect4">Example</bridgehead><programlisting> -localhost> <userinput>lua for k, v in pairs(box.cfg) do print(k, " = ", v) end</userinput> +localhost> <userinput>lua for k, v in pairs(box.cfg) do print(k, ' = ', v) end</userinput> --- io_collect_interval = 0 pid_file = box.pid @@ -2965,7 +3596,7 @@ logger = cat - >> tarantool.log <variablelist> <title>Package <code>box.info</code></title> <para> - This package provides access to information about + The box.info package provides access to information about server variables: pid, uptime, version and such. Its contents are identical to the output from <olink targetptr="show-info"/>. @@ -2975,24 +3606,26 @@ logger = cat - >> tarantool.log <emphasis role="lua">box.info()</emphasis> </term> <listitem> - <simpara> - Since box.info contents are dynamic, it's not - possible to iterate over keys with the Lua - <emphasis>pairs()</emphasis> function. For this - purpose, <emphasis>box.info()</emphasis> builds and - returns a Lua table with all keys and values provided - in the package. - </simpara> - - <bridgehead renderas="sect4">Example</bridgehead><programlisting> -localhost> <userinput>lua for k,v in pairs(box.info()) do print(k, ": ", v) end</userinput> + <para> + Since box.info contents are dynamic, it's not + possible to iterate over keys with the Lua + <emphasis>pairs()</emphasis> function. For this + purpose, <emphasis>box.info()</emphasis> builds and + returns a Lua table with all keys and values provided + in the package. + </para> + <para> + Returns: (type = Lua table) keys and values in the package. + </para> + <bridgehead renderas="sect4">Example</bridgehead><programlisting> +localhost> <userinput>lua for k, v in pairs(box.info()) do print(k, ': ', v) end</userinput> --- -version: 1.4.7-92-g4ba95ca +version: 1.5.2-17-g99249b5 status: primary pid: 1747 lsn: 1712 recovery_last_update: 1306964594.980 -recovery_lag: 0.000 +recovery_lag: 0 uptime: 39 build: table: 0x419cb880 logger_pid: 1748 @@ -3006,7 +3639,7 @@ config: /home/unera/work/tarantool/test/box/tarantool_good.cfg <emphasis role="lua">box.info.status, box.info.pid, box.info.lsn, ...</emphasis> </term> <listitem> - <bridgehead renderas="sect4">Example</bridgehead><programlisting> + <bridgehead renderas="sect4">Example</bridgehead><programlisting> localhost> <userinput>lua box.info.pid</userinput> --- - 1747 @@ -3049,10 +3682,15 @@ localhost> <userinput>lua box.info.snapshot_pid</userinput> ... localhost> <userinput>lua for k, v in pairs(box.info.build) do print(k .. ': ', v) end</userinput> --- -flags: -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCORO_ASM -fno-omit-frame-pointer -fno-stack-protector -fexceptions -funwind-tables -fgnu89-inline -pthread -Wno-sign-compare -Wno-strict-aliasing -std=gnu99 -Wall -Wextra -Werror +flags: -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCORO_ASM -fno-omit-frame-pointer + -fno-stack-protector -fexceptions -funwind-tables -fgnu89-inline + -pthread -Wno-sign-compare -Wno-strict-aliasing -std=gnu99 + -Wall -Wextra -Werror target: Linux-x86_64-Debug compiler: /usr/bin/gcc -options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_STATIC=OFF -DENABLE_GCOV=OFF -DENABLE_TRACE=ON -DENABLE_BACKTRACE=ON -DENABLE_CLIENT=OFF +options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_STATIC=OFF + -DENABLE_GCOV=OFF -DENABLE_TRACE=ON -DENABLE_BACKTRACE=ON + -DENABLE_CLIENT=OFF ... </programlisting> </listitem> @@ -3060,7 +3698,9 @@ options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_STATIC=OFF -DENABLE_ </variablelist> <variablelist> <title>Package <code>box.slab</code></title> - <para>This package provides access to slab allocator statistics.</para> + <para> + The box.slab package provides access to slab allocator statistics. + </para> <varlistentry> <term><emphasis role="lua">box.slab</emphasis></term> <listitem><bridgehead renderas="sect4">Example</bridgehead><programlisting> @@ -3091,8 +3731,10 @@ bytes_free:4194144 <variablelist> <title>Package <code xml:id="box.stat">box.stat</code></title> - <para>This package provides access to request - statistics.</para> + <para> + The box.stat package provides access to request + statistics. + </para> <varlistentry> <term><emphasis role="lua">box.stat</emphasis></term> <listitem><bridgehead renderas="sect4">Example</bridgehead><programlisting> @@ -3118,7 +3760,7 @@ localhost> <userinput>lua for k, v in pairs(box.stat().DELETE) do print(k, ': ', total: 23210 rps: 22 ... -localhost> <userinput>lua for k, v in pairs(box.stat.DELETE) do print(k, ': ', v) end -- the same</userinput> +localhost> <userinput>lua for k, v in pairs(box.stat.DELETE) do print(k, ': ', v) end</userinput> --- total: 23210 rps: 22 diff --git a/doc/user/tnt.css b/doc/user/tnt.css index 70562743e2fdfb335ba1d5e5f303e45aa7e1ce82..5911992c4716741630e782bc44a77a31eafd783a 100644 --- a/doc/user/tnt.css +++ b/doc/user/tnt.css @@ -1,23 +1,28 @@ .tntadmin { - font-family: monospace; - font-weight: bold; - text-transform: uppercase; - color: green; + font-family: monospace; + font-weight: bold; + text-transform: uppercase; + color: green; } .lua { - font-family: monospace; - font-weight: bold; + font-family: monospace; + font-weight: bold; } -.hl-keyword { - color: green; - font-weight: bold; +strong.hl-keyword { + color: green !important; + font-weight: bold; } -.hl-comment{ - color: blue; - font-weight: bold; +em.hl-comment { + color: #3366FF !important; + font-weight: normal; +} + +strong.hl-string em { + color: red !important; + font-weight: bold; } body { @@ -28,7 +33,6 @@ body { .book { margin: 2pt auto; - /*font-family: Verdana, Arial, Helvetica, sans-serif; */ font-size: 100% } diff --git a/doc/user/tutorial.xml b/doc/user/tutorial.xml index c83d876504a768510e015aab04da847c6900aa9e..ab9d785b80b04519497287a261c164f21203db9a 100644 --- a/doc/user/tutorial.xml +++ b/doc/user/tutorial.xml @@ -56,7 +56,7 @@ An automatic build system creates, tests and publishes packages for every push i Therefore if you looked at <link xlink:href="http://tarantool.org/dist" xlink:title="tarantool.org/dist">tarantool.org/dist</link> you would see many files. Names of binary packages have the format <computeroutput><filename>tarantool-<replaceable><version></replaceable>-<replaceable><OS></replaceable>-<replaceable><machine></replaceable>.tar.gz</filename></computeroutput>. Here is one example: -<programlisting>tarantool-1.5.1-97-g8e8cd06-linux-x86_64.tar.gz 26-Sep-2013 15:55 3664777</programlisting> +<programlisting>tarantool-1.5.1-97-g8e8cd06-linux-x86_64.tar.gz 26-Sep-2013 15:55 3664777</programlisting> which means <quote>Tarantool package, major version = 1, minor version number = 5, patch number 1, git revision id g8e8cd06, is a Linux x86 64-bit compressed tarball, pushed on September 26 2013, which contains 3.6 MB.</quote> @@ -70,16 +70,21 @@ start a shell (terminal) and enter one of the following sets of command-line ins <para> <programlisting> # DEBIAN commands for Tarantool stable binary download: -# There is always an up-to-date Debian repository at <link xlink:href="http://tarantool.org/dist/debian">http://tarantool.org/dist/debian</link> -# The repository contains builds for Debian unstable "Sid", stable "Wheezy", forthcoming "Jessie", ... +# There is always an up-to-date Debian repository at +# <link xlink:href="http://tarantool.org/dist/debian">http://tarantool.org/dist/debian</link> +# The repository contains builds for Debian unstable "Sid", stable "Wheezy", +# forthcoming "Jessie", ... # add the tarantool.org repository to your apt sources list: -# ($release is an environment variable which will contain the Debian version code e.g. "Wheezy") +# ($release is an environment variable which will contain the Debian version +# code e.g. "Wheezy") <command>wget</command> http://tarantool.org/dist/public.key <command>sudo apt-key add</command> <filename>./public.key</filename> release=`lsb_release -c -s` # append two lines to a list of source repositories -<command>echo</command> "deb http://tarantool.org/dist/debian/ $release main" | <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> -<command>echo</command> "deb-src http://tarantool.org/dist/debian/ $release main" | <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> +<command>echo</command> "deb http://tarantool.org/dist/debian/ $release main" | \ +<command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> +<command>echo</command> "deb-src http://tarantool.org/dist/debian/ $release main" | \ +<command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> # install <command>sudo apt-get update</command> <command>sudo apt-get install</command> tarantool tarantool-client @@ -89,18 +94,24 @@ release=`lsb_release -c -s` <para> <programlisting> # UBUNTU commands for Tarantool stable binary download: -# There is always an up-to-date Ubuntu repository at <link xlink:href="http://tarantool.org/dist/ubuntu">http://tarantool.org/dist/ubuntu</link> -# The repository contains builds for Ubuntu 12.04 "precise", 12.10 "quantal", 13.04 "raring", 13.10 "saucy", ... +# There is always an up-to-date Ubuntu repository at +# <link xlink:href="http://tarantool.org/dist/ubuntu">http://tarantool.org/dist/ubuntu</link> +# The repository contains builds for Ubuntu 12.04 "precise", 12.10 "quantal", +# 13.04 "raring", 13.10 "saucy", ... # add the tarantool.org repository to your apt sources list: -# ($release is an environment variable which will contain the Ubuntu version code e.g. "precise") -# (if you want the version that comes with Ubuntu, start with the lines that follow the '# install' comment) +# ($release is an environment variable which will contain the Ubuntu version +# code e.g. "precise") +# (if you want the version that comes with Ubuntu, start with the lines that +# follow the '# install' comment) <command>cd</command> ~ <command>wget</command> http://tarantool.org/dist/public.key <command>sudo apt-key add</command> <filename>./public.key</filename> release=`lsb_release -c -s` # append two lines to a list of source repositories -<command>echo</command> "deb http://tarantool.org/dist/ubuntu/ $release main" | <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> -<command>echo</command> "deb-src http://tarantool.org/dist/ubuntu/ $release main" | <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> +<command>echo</command> "deb http://tarantool.org/dist/ubuntu/ $release main" | \ +<command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> +<command>echo</command> "deb-src http://tarantool.org/dist/ubuntu/ $release main" | \ +<command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename> # install <command>sudo apt-get update</command> <command>sudo apt-get install</command> tarantool tarantool-client @@ -110,15 +121,20 @@ release=`lsb_release -c -s` <para> <programlisting> # CENTOS commands for Tarantool stable binary download: -# These instructions are applicable for CentOS version 5 or 6, and RHEL version 5 or 6 -# Pick the CentOS repository which fits your CentOS/RHEL version and your x86 platform: +# These instructions are applicable for CentOS version 5 or 6, +# and RHEL version 5 or 6 +# Pick the CentOS repository which fits your CentOS/RHEL version +# and your x86 platform: # <link xlink:href="http://tarantool.org/dist/centos/5/os/i386">http://tarantool.org/dist/centos/5/os/i386 for version 5, x86-32</link> # <link xlink:href="http://tarantool.org/dist/centos/5/os/x86_64">http://tarantool.org/dist/centos/5/os/x86_64 for version 5, x86-64</link> # <link xlink:href="http://tarantool.org/dist/centos/6/os/i386">http://tarantool.org/dist/centos/6/os/i386 for version 6, x86-32</link> # <link xlink:href="http://tarantool.org/dist/centos/6/os/x86_64">http://tarantool.org/dist/centos/6/os/x86_64 for version 6, x86-64</link> -# Add the following section to your yum repository list (<filename>/etc/yum.repos.d/tarantool.repo</filename>): -# (in the following instructions, $releasever i.e. CentOS release version must be either 5 or 6) -# (in the following instructions, $basearch i.e. base architecture must be either i386 or x86_64) +# Add the following section to your yum repository list +# (<filename>/etc/yum.repos.d/tarantool.repo</filename>): +# (in the following instructions, +# $releasever i.e. CentOS release version must be either 5 or 6) +# (in the following instructions, +# $basearch i.e. base architecture must be either i386 or x86_64) # [tarantool] # name=CentOS-<replaceable>$releasever</replaceable> - Tarantool # baseurl=http://tarantool.org/dist/centos/<replaceable>$releasever</replaceable>/os/<replaceable>$basearch</replaceable>/ @@ -126,9 +142,11 @@ release=`lsb_release -c -s` # gpgcheck=0 # For example, if you have CentOS version 6 and x86-64, you can # add the new section thus: -<command>echo</command> "[tarantool]" | <command>sudo tee</command> <filename>/etc/yum.repos.d/tarantool.repo</filename> +<command>echo</command> "[tarantool]" | \ +<command>sudo tee</command> <filename>/etc/yum.repos.d/tarantool.repo</filename> <command>echo</command> "name=CentOS-6 - Tarantool"| <command>sudo tee</command> <option>-a</option> <filename>/etc/yum.repos.d/tarantool.repo</filename> -<command>echo</command> "baseurl=http://tarantool.org/dist/centos/6/os/x86_64/" | <command>sudo tee</command> <option>-a</option> <filename>/etc/yum.repos.d/tarantool.repo</filename> +<command>echo</command> "baseurl=http://tarantool.org/dist/centos/6/os/x86_64/" | \ +<command>sudo tee</command> <option>-a</option> <filename>/etc/yum.repos.d/tarantool.repo</filename> <command>echo</command> "enabled=1" | <command>sudo tee</command> <option>-a</option> <filename>/etc/yum.repos.d/tarantool.repo</filename> <command>echo</command> "gpgcheck=0" | <command>sudo tee</command> <option>-a</option> <filename>/etc/yum.repos.d/tarantool.repo</filename> </programlisting> @@ -137,7 +155,8 @@ release=`lsb_release -c -s` <para> <programlisting> # GENTOO commands for Tarantool stable binary download: -# Tarantool is available from tarantool portage overlay. Use layman to add the overlay to your system: +# Tarantool is available from tarantool portage overlay. +# Use layman to add the overlay to your system: <command>layman</command> <option>-S</option> <command>layman</command> <option>-a</option> tarantool <command>emerge</command> <filename>dev-db/tarantool</filename> <option>-av</option> @@ -148,11 +167,11 @@ release=`lsb_release -c -s` <programlisting> # ANY-LINUX commands for Tarantool stable binary download: # If you have a GNU/Linux distribution which is not one of the above, -# or if you want to install on your own subdirectory without affecting /usr /etc etc., -# start your browser and go to +# or if you want to install on your own subdirectory without affecting +# /usr /etc etc., start your browser and go to # <link xlink:href="http://tarantool.org/download.html">http://tarantool.org/download.html</link> download page. # Look for words similar to "Other Linux distributions". You will want the -# binary tarball (<filename>.tar.gz</filename>) file for your release architecture (32-bit or 64-bit). +# right binary tarball (<filename>.tar.gz</filename>) file for your release architecture. # Click on either "32-bit" or "64-bit" depending on your release architecture. # This will cause a download of the latest stable tarball. # Suppose it is <filename>tarantool-1.5.1-133-g11edda1-linux-x86_64.tar.gz</filename>. Say: @@ -182,7 +201,8 @@ release=`lsb_release -c -s` # This is actually a <quote><productname>homebrew</productname></quote> recipe # so it's not a true binary download, some source code is involved. # First upgrade Clang (the C compiler) to version 3.2 or later using -# Command Line Tools for Xcode disk image version 4.6+ from Apple Developer web-site. +# Command Line Tools for Xcode disk image version 4.6+ from +# Apple Developer web-site. <command>brew install</command> --use-clang http://tarantool.org/dist/tarantool.rb # or <command>brew install</command> http://tarantool.org/dist/tarantool.rb @@ -240,36 +260,96 @@ Here are names of tools and libraries which may have to be installed in advance, or the equivalent on other platforms. Different platforms may use slightly different names. Do not worry about the <quote>optional, for build with -DENABLE_DOC</quote> ones unless you intend to work on the documentation. -<programlisting> binutils-dev or binutils-devel # contains GNU bfd for printing stack traces - gcc or clang # see above - git # see above - uuid-dev # optional, for box_uuid_* functions - cmake # see above - libreadline-dev # optional, for build with -DENABLE_CLIENT - libncurses5-dev or ncurses-devel # optional, for build with -DENABLE_CLIENT - xsltproc # optional, for build with -DENABLE_DOC - lynx # optional, for build with -DENABLE_DOC - jing # optional, for build with -DENABLE_DOC - libxml2-utils # optional, for build with -DENABLE_DOC - docbook5-xml # optional, for build with -DENABLE_DOC - docbook-xsl-ns # optional, for build with -DENABLE_DOC - w3c-sgml-lib # optional, for build with -DENABLE_DOC - libsaxon-java # optional, for build with -DENABLE_DOC - libxml-commons-resolver1.1-java # optional, for build with -DENABLE_DOC - libxerces2-java # optional, for build with -DENABLE_DOC - libxslthl-java # optional, for build with -DENABLE_DOC - autoconf # optional, appears only in Mac OS scripts - zlib1g or zlib # optional, appears only in Mac OS scripts</programlisting> -</para> - -<para> -2. Pick a default directory. +<programlisting> binutils-dev or binutils-devel # contains GNU bfd for printing stack traces + gcc or clang # see above + git # see above + uuid-dev # optional, for box_uuid_* functions + cmake # see above + libreadline-dev # optional, for build with -DENABLE_CLIENT + libncurses5-dev or ncurses-devel # optional, for build with -DENABLE_CLIENT + xsltproc # optional, for build with -DENABLE_DOC + lynx # optional, for build with -DENABLE_DOC + jing # optional, for build with -DENABLE_DOC + libxml2-utils # optional, for build with -DENABLE_DOC + docbook5-xml # optional, for build with -DENABLE_DOC + docbook-xsl-ns # optional, for build with -DENABLE_DOC + w3c-sgml-lib # optional, for build with -DENABLE_DOC + libsaxon-java # optional, for build with -DENABLE_DOC + libxml-commons-resolver1.1-java # optional, for build with -DENABLE_DOC + libxerces2-java # optional, for build with -DENABLE_DOC + libxslthl-java # optional, for build with -DENABLE_DOC + autoconf # optional, appears only in Mac OS scripts + zlib1g or zlib # optional, appears only in Mac OS scripts</programlisting> +</para> + +<para> +2. Set up python modules for running the test suite or creating documentation. + This step is optional. Python modules are not necessary for building Tarantool + itself, unless one intends to use the -DENABLE_DOC option in step 6 or the + "Run the test suite" option in step 8.</para> +<para>Say:<programlisting><command>python --version</command></programlisting> +... You should see that the python version is between 2.6 and 3.</para> +<para>On Ubuntu you can get modules from the repository: +<programlisting> +# For test suite +<command>sudo apt-get install</command> python-daemon python-yaml python-argparse python-pexpect +# For documentation +<command>sudo apt-get install</command> python-jinja2 python-markdown +</programlisting></para> +<para>On CentOS too you can get modules from the repository:<programlisting><command>sudo yum install</command> python26 python26-PyYAML python26-argparse</programlisting></para> +<para>But in general it is best to set up the modules by getting + a tarball and doing the setup with <computeroutput>python setup.py</computeroutput>, thus: +<programlisting># python module for parsing YAML (pyYAML): For test suite: +# (If wget fails, check the <citetitle xlink:href="http://pyyaml.org/wiki/PyYAML" xlink:title="Python YAML parser">PyYAML</citetitle> web site +# to see what the current version is.) +<command>cd</command> ~ +<command>wget</command> http://pyyaml.org/download/pyyaml/PyYAML-3.10.tar.gz +<command>tar</command> <option>-xzf</option> PyYAML-3.10.tar.gz +<command>cd</command> PyYAML-3.10 +<command>sudo python</command> setup.py install +# python module for spawning child applications (pexpect): For test suite: +# (If wget fails, check the <citetitle xlink:href="http://pypi.python.org/pypi/pexpect/" xlink:title="pure-Python pexpect implementation">python-pexpect</citetitle> web site +# to see what the current version is.) +<command>cd</command> ~ +<command>wget</command> http://pypi.python.org/packages/source/p/pexpect-u/pexpect-u-2.5.1.tar.gz +<command>tar</command> <option>-xzvf</option> pexpect-u-2.5.1.tar. +<command>cd</command> pexpect-u-2.5.1 +<command>sudo python</command> setup.py install +# python module for helping programs become daemons (daemon): For test suite: +# (if wget fails, check the <citetitle xlink:href="http://pypi.python.org/pypi/python-daemon" xlink:title="Python daemon">python-daemon</citetitle> web site +# to see what the current version is.) +<command>cd</command> ~ +<command>wget</command> http://pypi.python.org/packages/source/d/daemon/daemon-1.0.tar.gz +<command>tar</command> <option>-xzvf</option> daemon-1.0.tar.gz +<command>cd</command> daemon-1.0 +<command>sudo python</command> setup.py install +# python module for template engine (jinja2): For documentation: +# (If wget fails, check the <citetitle xlink:href="https://pypi.python.org/pypi/Jinja2" xlink:title="Python Jinja2">python-jinja2</citetitle> web site +# to see what the current version is.) +<command>cd</command> ~ +<command>wget</command> https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.7.2.tar.gz +<command>tar</command> <option>-xzvf</option> Jinja2-2.7.2.tar.gz +<command>cd</command> Jinja2-2.7.2 +<command>sudo python</command> setup.py install +# python module for text-to-html conversion (markdown): For documentation: +# (If wget fails, check the <citetitle xlink:href="http://pypi.python.org/pypi/Markdown/" xlink:title="Python implementation of Markdown">python-markdown</citetitle> web site +# to see what the current version is.) +<command>cd</command> ~ +<command>wget</command> https://pypi.python.org/packages/source/M/Markdown/Markdown-2.3.1.tar.gz +<command>tar</command> <option>-xzvf</option> Markdown-2.3.1.tar.gz +<command>cd</command> Markdown-2.3.1 +<command>sudo python</command> setup.py install +</programlisting> +</para> + +<para> +3. Pick a default directory. This can be anywhere. We'll assume that your default directory is <quote>~</quote>, and therefore the tarantool download will go inside it, as <computeroutput><filename>~/tarantool</filename></computeroutput>. </para> <para> -3. Use <productname>git</productname> to download from github.com.<programlisting> +4. Use <productname>git</productname> to download from github.com.<programlisting> <command>cd</command> ~ <command>git clone</command> <option>-b stable</option> https://github.com/tarantool/tarantool.git <option>tarantool</option></programlisting> The optional argument <quote>-b stable</quote> causes download from the stable branch instead of the master branch, @@ -277,7 +357,7 @@ and the optional last word on the line, <quote>tarantool</quote>, means download </para> <para> -4. Use <productname>git</productname> again so that third-party contributions will be seen as well. +5. Use <productname>git</productname> again so that third-party contributions will be seen as well. This step is only necessary once, the first time you do a download. There is an alternative -- say <quote><computeroutput><command>git clone</command> --recursive</computeroutput></quote> in step 3 -- but we prefer this method because it works with older versions of <productname>git</productname>.<programlisting><command>cd</command> ~/tarantool @@ -287,10 +367,10 @@ but we prefer this method because it works with older versions of <productname>g </para> <para> -5. Use CMake to initiate the build.<programlisting><command>cd</command> ~/tarantool +6. Use CMake to initiate the build.<programlisting><command>cd</command> ~/tarantool <command>make clean</command> # unnecessary, added for good luck <command>rm CMakeCache.txt</command> # unnecessary, added for good luck -<command>cmake .</command> # The command to initiate with build type=Debug, no client, no doc</programlisting> +<command>cmake .</command> # Start build with build type=Debug, no client, no doc</programlisting> The option for specifying build type is <option>-DCMAKE_BUILD_TYPE=</option><replaceable>type</replaceable> where type = {None | Debug | Release | RelWithDebInfo | MinSizeRel} and a reasonable @@ -307,43 +387,10 @@ and a reasonable choice is <computeroutput><option>-DENABLE_CLIENT=true</option> </para> <para> - 6. Use make to complete the build.<programlisting><command>make</command></programlisting> + 7. Use make to complete the build.<programlisting><command>make</command></programlisting> It's possible to say <quote><computeroutput><command>make install</command></computeroutput></quote> too, but that's not generally done. </para> -<para> -7. Set up python modules for running the test suite. - This step is optional.</para> -<para>Say:<programlisting><command>python --version</command></programlisting> -... You should see that the python version is between 2.6 and 3.</para> -<para>On Ubuntu you can get modules from the repository:<programlisting><command>sudo apt-get install</command> python-daemon python-yaml python-argparse python-pexpect</programlisting></para> -<para>On CentOS too you can get modules from the repository:<programlisting><command>sudo yum install</command> python26 python26-PyYAML python26-argparse</programlisting></para> -<para>But in general it is best to set up the modules by getting - a tarball and doing the setup with <computeroutput>python setup.py</computeroutput>, thus: -<programlisting># python module for parsing YAML (pyYAML): -# (If wget fails, check the <citetitle xlink:href="http://pyyaml.org/wiki/PyYAML" xlink:title="Python YAML parser">PyYAML</citetitle> web site to see what the current version is.) -<command>cd</command> ~ -<command>wget</command> http://pyyaml.org/download/pyyaml/PyYAML-3.10.tar.gz -<command>tar</command> <option>-xzf</option> PyYAML-3.10.tar.gz -<command>cd</command> PyYAML-3.10 -<command>sudo python</command> setup.py install -# python module for spawning child applications (pexpect): -# (If wget fails, check the <citetitle xlink:href="http://pypi.python.org/pypi/pexpect/" xlink:title="pure-Python pexpect implementation">python-pexpect</citetitle> web site to see what the current version is.) -<command>cd</command> ~ -<command>wget</command> http://pypi.python.org/packages/source/p/pexpect-u/pexpect-u-2.5.1.tar.gz -<command>tar</command> <option>-xzvf</option> pexpect-u-2.5.1.tar. -<command>cd</command> pexpect-u-2.5.1 -<command>sudo python</command> setup.py install -# python module for assisting programs to turn themselves into daemons (daemon): -# (if wget fails, check the <citetitle xlink:href="http://pypi.python.org/pypi/python-daemon" xlink:title="Python daemon">python-daemon</citetitle> web site to see what the current version is.) -<command>cd</command> ~ -<command>wget</command> http://pypi.python.org/packages/source/d/daemon/daemon-1.0.tar.gz -<command>tar</command> <option>-xzvf</option> daemon-1.0.tar.gz -<command>cd</command> daemon-1.0 -<command>sudo python</command> setup.py install</programlisting> -</para> - - <para> 8. Run the test suite. This step is optional. </para> @@ -441,54 +488,58 @@ which make a minimal configuration file that will be suitable for day one. <command>echo</command> "space[0].index[0].key_field[0].type = \"NUM\"" | <command>tee</command> <option>-a</option> tarantool.cfg <command>echo</command> "logger = \"tee --append tarantool.log\"" | <command>tee</command> <option>-a</option> tarantool.cfg <command>echo</command> "work_dir = \"work_dir\"" | <command>tee</command> <option>-a</option> tarantool.cfg -(With some downloads a tarantool.cfg file like this is already available in a test subdirectory.) +# (With some downloads a tarantool.cfg file like this is already available +# in a test subdirectory.) </programlisting> </para> <para> Initialize the storage area. You only have to do this once.<programlisting> -<command>/usr/bin/tarantool_box --init-storage</command> #if you downloaded a binary with apt-get or yum -<command>~/tarantool/bin/tarantool_box --init-storage</command> #if you downloaded and untarred a binary tarball to ~/tarantool -<command>~/tarantool/src/box/tarantool_box --init-storage</command> #f you built from a source download </programlisting> +#if you downloaded a binary with apt-get or yum, say this: + <command>/usr/bin/tarantool_box --init-storage</command> +#if you downloaded and untarred a binary tarball to ~/tarantool, say this: + <command>~/tarantool/bin/tarantool_box --init-storage</command> +#if you built from a source download, say this: + <command>~/tarantool/src/box/tarantool_box --init-storage</command></programlisting> </para> <para> Start the server. The server name is <computeroutput><filename>tarantool_box</filename></computeroutput>.<programlisting> -<command>/usr/bin/tarantool_box</command> #if you downloaded a binary with apt-get or yum -<command>~/tarantool/bin/tarantool_box</command> #if you downloaded and untarred a binary tarball to ~/tarantool -<command>~/tarantool/src/box/tarantool_box</command> #f you built from a source download</programlisting> +#if you downloaded a binary with apt-get or yum, say this: + <command>/usr/bin/tarantool_box</command> +#if you downloaded and untarred a binary tarball to ~/tarantool, say this: + <command>~/tarantool/bin/tarantool_box</command> +#if you built from a source download, say this: + <command>~/tarantool/src/box/tarantool_box</command> </programlisting> </para> <para> If all goes well, you will see the server displaying progress as it initializes, something like this:<programlisting> 2013-10-18 20:20:36.806 [16560] 1/sched C> version 1.5.1-141-ga794d35 -2013-10-18 20:20:36.830 [16560] 1/sched I> Loading plugin: /usr/lib/tarantool/plugins/libmysql.so -2013-10-18 20:20:37.016 [16560] 1/sched I> Plugin 'mysql' was loaded, version: 1 -2013-10-18 20:20:37.016 [16560] 1/sched I> Loading plugin: /usr/lib/tarantool/plugins/libpg.so -2013-10-18 20:20:37.044 [16560] 1/sched I> Plugin 'postgresql' was loaded, version: 1 -2013-10-18 20:20:37.044 [16560] 1/sched I> space 0 successfully configured -2013-10-18 20:20:37.044 [16560] 1/sched I> recovery start -2013-10-18 20:20:37.060 [16560] 1/sched I> recover from `./00000000000000000001.snap' -2013-10-18 20:20:37.060 [16560] 1/sched I> snapshot recovered, confirmed lsn: 1 -2013-10-18 20:20:37.070 [16560] 1/sched I> done `./00000000000000000002.xlog' confirmed_lsn: 2 -2013-10-18 20:20:37.070 [16560] 1/sched I> WALs recovered, confirmed lsn: 2 -2013-10-18 20:20:37.070 [16560] 1/sched I> building secondary indexes -2013-10-18 20:20:37.070 [16560] 1/sched I> bound to primary port 33013 -2013-10-18 20:20:37.070 [16560] 1/sched I> I am primary -2013-10-18 20:20:37.070 [16560] 1/sched I> bound to secondary port 33014 -2013-10-18 20:20:37.070 [16560] 1/sched I> bound to admin port 33015 -2013-10-18 20:20:37.071 [16560] 1/sched C> log level 4 -2013-10-18 20:20:37.071 [16560] 1/sched C> entering event loop</programlisting> +2013-10-18 20:20:37.044 ... I> space 0 successfully configured +2013-10-18 20:20:37.044 ... I> recovery start +2013-10-18 20:20:37.060 ... I> recover from `./00000000000000000001.snap' +2013-10-18 20:20:37.060 ... I> snapshot recovered, confirmed lsn: 1 +2013-10-18 20:20:37.070 ... I> building secondary indexes +2013-10-18 20:20:37.070 ... I> bound to primary port 33013 +2013-10-18 20:20:37.070 ... I> I am primary +2013-10-18 20:20:37.070 ... I> bound to secondary port 33014 +2013-10-18 20:20:37.070 ... I> bound to admin port 33015 +2013-10-18 20:20:37.071 ... C> log level 4 +2013-10-18 20:20:37.071 ... C> entering event loop</programlisting> Now take the server down, with <programlisting><keycombo><keysym>Ctrl</keysym><keysym>C</keysym></keycombo></programlisting> </para> <para> Now start the server again. This time start it in the background.<programlisting> -<command>/usr/bin/tarantool_box --background</command> #if you downloaded a binary with apt-get or yum -<command>~/tarantool/bin/tarantool_box --background</command> #if you downloaded and untarred a binary tarball to ~/tarantool -<command>~/tarantool/src/box/tarantool_box --background</command> #f you built from a source download</programlisting> +#if you downloaded a binary with apt-get or yum, say this: + <command>/usr/bin/tarantool_box --background</command> +#if you downloaded and untarred a binary tarball to ~/tarantool, say this: + <command>~/tarantool/bin/tarantool_box --background</command> +#if you built from a source download, say this: + <command>~/tarantool/src/box/tarantool_box --background</command></programlisting> </para> <para> @@ -517,9 +568,12 @@ Tarantool instances on your operating system. <para> Now that the server is up, you can start the client. The client name is tarantool.<programlisting> -<command>/usr/bin/tarantool</command> #If you downloaded a binary with apt-get or yum: -<command>~/tarantool/bin/tarantool</command> #If you downloaded and untarred a binary tarball to ~/tarantool: -<command>~/tarantool/client/tarantool/tarantool</command> #If you built from a source download on ~tarantool</programlisting> +#if you downloaded a binary with apt-get or yum, say this: + <command>/usr/bin/tarantool</command> +#if you downloaded and untarred a binary tarball to ~/tarantool, say this: + <command>~/tarantool/bin/tarantool</command> +#if you built from a source download on ~tarantool, say this: + <command>~/tarantool/client/tarantool/tarantool</command></programlisting> If all goes well, a prompt will appear:<programlisting><prompt>localhost></prompt></programlisting> The client is waiting for the user to type instructions. diff --git a/extra/rpm.spec.in b/extra/rpm.spec.in index 4b0efdb7c2a890fc606b07816751fdf74951cd32..5a4b2174ed5c673d4bf95f1471bab96149aa2c81 100644 --- a/extra/rpm.spec.in +++ b/extra/rpm.spec.in @@ -30,7 +30,6 @@ Vendor: tarantool.org Group: Applications/Databases Provides: tarantool-client Obsoletes: tarantool-client -Requires: tarantool-debug = @RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@ %description -n tarantool-client Tarantool is a high performance in-memory NoSQL database. It supports replication, online backup, stored procedures in Lua. @@ -45,9 +44,9 @@ Summary: Tarantool C connector and header files Vendor: tarantool.org Group: Applications/Databases #Requires: /sbin/ldconfig +#Requires: tarantool-debug = @RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@ Provides: tarantool-dev Obsoletes: tarantool-dev -Requires: tarantool-debug = @RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@ %description -n tarantool-dev Tarantool is a high performance in-memory NoSQL database. It supports replication, online backup, stored procedures in Lua. diff --git a/include/say.h b/include/say.h index 460ca9c3280c59a4a06528d621316c11cd99d34d..6273adafb2d86275a5f8cd3b70ea6df503565142 100644 --- a/include/say.h +++ b/include/say.h @@ -40,6 +40,7 @@ extern "C" { enum say_level { S_FATAL, /* do not this value use directly */ + S_SYSERROR, S_ERROR, S_CRIT, S_WARN, @@ -66,7 +67,7 @@ extern sayfunc_t _say __attribute__ ((format(FORMAT_PRINTF, 5, 6))); #define panic_status(status, ...) ({ say(S_FATAL, NULL, __VA_ARGS__); exit(status); }) #define panic(...) panic_status(EXIT_FAILURE, __VA_ARGS__) #define panic_syserror(...) ({ say(S_FATAL, strerror(errno), __VA_ARGS__); exit(EXIT_FAILURE); }) -#define say_syserror(...) say(S_ERROR, strerror(errno), __VA_ARGS__) +#define say_syserror(...) say(S_SYSERROR, strerror(errno), __VA_ARGS__) #define say_error(...) say(S_ERROR, NULL, __VA_ARGS__) #define say_crit(...) say(S_CRIT, NULL, __VA_ARGS__) #define say_warn(...) say(S_WARN, NULL, __VA_ARGS__) diff --git a/src/exception.cc b/src/exception.cc index 55b04f8bad38c027dce93feff22ca3c49dca0361..de73973914907a2f0ee438d7034e25f4ffebf11a 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -70,7 +70,7 @@ SystemError::init(const char *format, va_list ap) void SystemError::log() const { - _say(S_ERROR, m_file, m_line, strerror(m_errnum), "SystemError %s", + _say(S_SYSERROR, m_file, m_line, strerror(m_errnum), "SystemError %s", m_errmsg); } diff --git a/src/salloc.cc b/src/salloc.cc index 8f48002e1ae94cd25f8b373149103d1d269f27b6..2d560962907a18de35b074c7f0f17e437d6e4d52 100644 --- a/src/salloc.cc +++ b/src/salloc.cc @@ -163,7 +163,10 @@ arena_init(struct arena *arena, size_t size) "(https://bugzilla.openvz.org/show_bug.cgi?id=2805)"); flags = MAP_PRIVATE | MAP_ANONYMOUS; private_arena = true; - } + } else { + say_info("Mapping %zu bytes for a shared arena", + arena->mmap_size); + } arena->mmap_base = mmap(NULL, arena->mmap_size, PROT_READ | PROT_WRITE, flags, -1, 0); diff --git a/src/say.cc b/src/say.cc index bfaea8f521ec53f6bd62e66b31f0b09e693adb19..17d4d64d69654e2e65eec322983cc5481cda9f98 100644 --- a/src/say.cc +++ b/src/say.cc @@ -62,6 +62,8 @@ level_to_char(int level) switch (level) { case S_FATAL: return 'F'; + case S_SYSERROR: + return '!'; case S_ERROR: return 'E'; case S_CRIT: diff --git a/test/big/suite.ini b/test/big/suite.ini index 4c9d65bcc5f5f528c7e51e57a5d3bdf7de8fc46b..4b6a9ae7b4b8595cf533e9f9ae47ee4afb9caacf 100644 --- a/test/big/suite.ini +++ b/test/big/suite.ini @@ -5,4 +5,4 @@ config = tarantool.cfg # disabled = lua.test # put disabled in valgrind test here #valgrind_disabled = ... -release_disabled = hash_errinj.test +release_disabled = hash_errinj.test tree_alloc_fail.test diff --git a/test/box/admin.result b/test/box/admin.result index ee4390105a19c74ea33c142962006d72b87fcc61..b8fd3e0e1a4f3752909bf977520a6e12d8479103 100644 --- a/test/box/admin.result +++ b/test/box/admin.result @@ -38,7 +38,7 @@ configuration: coredump: "false" admin_port: "33015" replication_port: "0" - log_level: "4" + log_level: "5" slab_alloc_arena: "0.1" slab_alloc_minimal: "64" slab_alloc_factor: "2" diff --git a/test/box/args.test b/test/box/args.test index 8e7d546b51a0314fe035c373c7503097832575f8..8e73abc7bdd9bc8f780fb79553483ee6d384e804 100644 --- a/test/box/args.test +++ b/test/box/args.test @@ -61,6 +61,7 @@ os.symlink(os.path.abspath("box/bug726778.cfg"), cfg) sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool") sys.stdout.push_filter(".*(P|p)lugin.*", "") +sys.stdout.push_filter(".*shared.*", "") server.test_option("--config=bug726778.cfg --init-storage") sys.stdout.pop_filter() diff --git a/test/box/configuration.result b/test/box/configuration.result index 37452c939768e7ffb8236b55ce29c8d23e6a6e1a..2709bc0c635ead9f41e1a2e14e9155028af0f3ce 100644 --- a/test/box/configuration.result +++ b/test/box/configuration.result @@ -13,7 +13,7 @@ configuration: coredump: "false" admin_port: "33015" replication_port: "0" - log_level: "4" + log_level: "5" slab_alloc_arena: "0.1" slab_alloc_minimal: "64" slab_alloc_factor: "2" @@ -77,7 +77,7 @@ configuration: coredump: "false" admin_port: "33015" replication_port: "0" - log_level: "4" + log_level: "5" slab_alloc_arena: "0.1" slab_alloc_minimal: "64" slab_alloc_factor: "2" @@ -231,7 +231,7 @@ io_collect_interval = 0 pid_file = box.pid slab_alloc_minimal = 64 primary_port = 33013 -log_level = 4 +log_level = 5 logger_nonblock = true memcached_expire_per_loop = 1024 snap_dir = . diff --git a/test/box/lua.result b/test/box/lua.result index ce9cb50805da889c4592c6f3412af5aea986db55..67f9e68bc5c7191d02c53109bff6fd99e8a60b35 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -423,7 +423,7 @@ lua for k,v in pairs(box.cfg) do print(' - ', k, ': ', v) end - pid_file: box.pid - slab_alloc_minimal: 64 - slab_alloc_arena: 0.1 - - log_level: 4 + - log_level: 5 - logger_nonblock: true - memcached_expire_per_loop: 1024 - snap_dir: . @@ -471,7 +471,7 @@ lua for k,v in pairs(box.cfg) do print(' - ', k, ': ', v) end - pid_file: box.pid - slab_alloc_minimal: 64 - slab_alloc_arena: 0.1 - - log_level: 4 + - log_level: 5 - logger_nonblock: true - memcached_expire_per_loop: 1024 - snap_dir: . @@ -1345,7 +1345,7 @@ io_collect_interval = 0 pid_file = box.pid slab_alloc_minimal = 64 primary_port = 33013 -log_level = 4 +log_level = 5 logger_nonblock = true memcached_expire_per_loop = 1024 snap_dir = . diff --git a/test/memcached/off.result b/test/memcached/off.result index a2ccef2bccdce380ac4447406b1333b56854ea33..b1cc5dfb868e6988903f4ae89f8327319d5ae150 100644 --- a/test/memcached/off.result +++ b/test/memcached/off.result @@ -12,7 +12,7 @@ configuration: coredump: "false" admin_port: "33015" replication_port: "0" - log_level: "4" + log_level: "5" slab_alloc_arena: "0.1" slab_alloc_minimal: "64" slab_alloc_factor: "2"