From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 011446ECE3; Wed, 24 Nov 2021 10:29:40 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 011446ECE3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1637738981; bh=wpYEUUz54wFU589QlsEQcayXVZgYBzWdSzH8/NHjqjI=; h=Date:To:Cc:References:In-Reply-To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=a4P8dZFRG7mXmKfxmsTDiJzjpgsxZZATY6VnzuAuonPmQT8LvPh2hXcvuX5IRaRAm sge/Enluy+fXMKR4/YqyV++W5LKIyFSrEfnyUDVHkIJn3tVU5ekxU9kXPH3iAw6Ffc N1DQ7P+SQt6HD7oQF9dQUCox99b4prNUQYThVp5M= Received: from smtpng1.i.mail.ru (smtpng1.i.mail.ru [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id CF3D46ECE3 for ; Wed, 24 Nov 2021 10:29:39 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org CF3D46ECE3 Received: by smtpng1.m.smailru.net with esmtpa (envelope-from ) id 1mpmip-0000Ou-2q; Wed, 24 Nov 2021 10:29:39 +0300 Date: Wed, 24 Nov 2021 10:29:37 +0300 To: Vladislav Shpilevoy Cc: tarantool-patches@dev.tarantool.org Message-ID: <20211124072937.GB41102@tarantool.org> References: <0304eeda12ed2a459370656121cb70ededdbb977.1637159909.git.imeevma@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: X-4EC0790: 10 X-7564579A: B8F34718100C35BD X-77F55803: 4F1203BC0FB41BD9FE0487E502468146DE9D5261128A1063E979179EAE55CF1E182A05F53808504095A38037B5E7A15A0DBAC735CE69B1A41A46D341E6856D424C6FD73E6CFCA3DB X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7227E4400968B082FEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637AA32F0A5ADCF96E68638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D883BF04C70DAD9B27864BA3F283D10AC8117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB231DD303D21008E298D5E8D9A59859A8B6B372FE9A2E580EFC725E5C173C3A84C3335407143AA9223635872C767BF85DA2F004C90652538430E4A6367B16DE6309 X-C1DE0DAB: 0D63561A33F958A5B1BF65B74F3507A342EB4406B6C760907B1229216B9CE257D59269BC5F550898D99A6476B3ADF6B47008B74DF8BB9EF7333BD3B22AA88B938A852937E12ACA759F66ED85EB5F25FD410CA545F18667F91A7EA1CDA0B5A7A0 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34AC6E62257D6CD1C9E0CEDB37E0235DD0216EC7D17F12EBF39C6F729994F0E77EAA229D040165A5DF1D7E09C32AA3244C6B4AC2E152FBE57B4B3BA551DFBB2CD5435BF7150578642F729B2BEF169E0186 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojFw1tprMwPO1d5XZjRNrpEw== X-Mailru-Sender: 689FA8AB762F7393C37E3C1AEC41BA5D8E82B9B2660AB304470D601E486DEB6683D72C36FC87018B9F80AB2734326CD2FB559BB5D741EB96352A0ABBE4FDA4210A04DAD6CC59E33667EA787935ED9F1B X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH v1 3/3] sql: introduce syntax for ARRAY values X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Mergen Imeev via Tarantool-patches Reply-To: Mergen Imeev Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Thank you for the review! My answer, diff and new patch below. Also I replaced sql_expr_new_dequoted() by sql_expr_new_anon(). On Sat, Nov 20, 2021 at 12:36:19AM +0100, Vladislav Shpilevoy wrote: > Thanks for the patch! Cool feature! > > Could you also please add a test for an empty array creation? As []. Added. Also I added a test about creation of ARRAY value with more than 1000 elements. This is possible because creation of ARRAY is an operation and not a built-in function. Diff: diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 92fb37dd4..ee319d5ad 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1129,7 +1129,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { } expr(A) ::= LB(X) exprlist(Y) RB(E). { - struct Expr *expr = sql_expr_new_dequoted(pParse->db, TK_ARRAY, NULL); + struct Expr *expr = sql_expr_new_anon(pParse->db, TK_ARRAY); if (expr == NULL) { sql_expr_list_delete(pParse->db, Y); pParse->is_aborted = true; diff --git a/test/sql-tap/array.test.lua b/test/sql-tap/array.test.lua index 1f2ba6d5d..79a1c831d 100755 --- a/test/sql-tap/array.test.lua +++ b/test/sql-tap/array.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool local test = require("sqltester") -test:plan(113) +test:plan(115) box.schema.func.create('A1', { language = 'Lua', @@ -999,6 +999,25 @@ test:do_execsql_test( test:do_execsql_test( "array-13.3", + [[ + SELECT []; + ]], { + {} + }) + +local arr = {0} +local arr_str = '0' +for i = 1, 1000 do table.insert(arr, i) arr_str = arr_str .. ', ' .. i end +test:do_execsql_test( + "array-13.4", + [[ + SELECT []] .. arr_str .. [[]; + ]], { + arr + }) + +test:do_execsql_test( + "array-13.5", [[ SELECT typeof([1]); ]], { New patch: commit 9036493dd4085cc5ce73fc0e8160cfdcd3412228 Author: Mergen Imeev Date: Tue Nov 16 10:16:25 2021 +0300 sql: introduce syntax for ARRAY values This patch introduces a new syntax that allows to create ARRAY values in an SQL query. Part of #4762 @TarantoolBot document Title: Syntax for ARRAY in SQL The syntax for creating ARRAY values is available in SQL. You can use `[` and `]` to create an ARRAY value - all values in those brackets will be part of ARRAY. The position of the values will be translated to the same positions in ARRAY. Examples: ``` tarantool> box.execute("SELECT [1, 'a', 1.5];") --- - metadata: - name: COLUMN_1 type: array rows: - [[1, 'a', 1.5]] ... ``` ``` tarantool> box.execute("SELECT [1, 'a', ['abc', 321], 1.5];") --- - metadata: - name: COLUMN_1 type: array rows: - [[1, 'a', ['abc', 321], 1.5]] ... ``` diff --git a/changelogs/unreleased/gh-4762-introduce-array-to-sql.md b/changelogs/unreleased/gh-4762-introduce-array-to-sql.md index 1446ab1cb..fcfa53c73 100644 --- a/changelogs/unreleased/gh-4762-introduce-array-to-sql.md +++ b/changelogs/unreleased/gh-4762-introduce-array-to-sql.md @@ -1,3 +1,4 @@ ## feature/core - * Field type ARRAY is now available in SQL (gh-4762). + * Field type ARRAY is now available in SQL. The syntax has also been + implemented to allow the creation of ARRAY values (gh-4762). diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 2c8021060..eb169aeb8 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -3370,6 +3370,22 @@ int_overflow: is_neg ? P4_INT64 : P4_UINT64); } +static void +expr_code_array(struct Parse *parser, struct Expr *expr, int reg) +{ + struct Vdbe *vdbe = parser->pVdbe; + struct ExprList *list = expr->x.pList; + if (list == NULL) { + sqlVdbeAddOp3(vdbe, OP_Array, 0, reg, 0); + return; + } + int count = list->nExpr; + int values_reg = parser->nMem + 1; + parser->nMem += count; + sqlExprCodeExprList(parser, list, values_reg, 0, SQL_ECEL_FACTOR); + sqlVdbeAddOp3(vdbe, OP_Array, count, reg, values_reg); +} + /* * Erase column-cache entry number i */ @@ -3821,6 +3837,10 @@ sqlExprCodeTarget(Parse * pParse, Expr * pExpr, int target) return inReg; } + case TK_ARRAY: + expr_code_array(pParse, pExpr, target); + break; + case TK_LT: case TK_LE: case TK_GT: diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y index 548004252..ee319d5ad 100644 --- a/src/box/sql/parse.y +++ b/src/box/sql/parse.y @@ -1128,6 +1128,20 @@ expr(A) ::= CAST(X) LP expr(E) AS typedef(T) RP(Y). { sqlExprAttachSubtrees(pParse->db, A.pExpr, E.pExpr, 0); } +expr(A) ::= LB(X) exprlist(Y) RB(E). { + struct Expr *expr = sql_expr_new_anon(pParse->db, TK_ARRAY); + if (expr == NULL) { + sql_expr_list_delete(pParse->db, Y); + pParse->is_aborted = true; + return; + } + expr->x.pList = Y; + expr->type = FIELD_TYPE_ARRAY; + sqlExprSetHeightAndFlags(pParse, expr); + A.pExpr = expr; + spanSet(&A, &X, &E); +} + expr(A) ::= TRIM(X) LP trim_operands(Y) RP(E). { A.pExpr = sqlExprFunction(pParse, Y, &X); spanSet(&A, &X, &E); diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c index b3cf8f6e6..f2d5a2df5 100644 --- a/src/box/sql/tokenize.c +++ b/src/box/sql/tokenize.c @@ -83,6 +83,8 @@ #define CC_DOT 26 /* '.' */ #define CC_ILLEGAL 27 /* Illegal character */ #define CC_LINEFEED 28 /* '\n' */ +#define CC_LB 29 /* '[' */ +#define CC_RB 30 /* ']' */ static const char sql_ascii_class[] = { /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */ @@ -91,7 +93,7 @@ static const char sql_ascii_class[] = { /* 2x */ 7, 15, 9, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16, /* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6, /* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 1, +/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 29, 27, 30, 27, 1, /* 6x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27, /* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -220,6 +222,12 @@ sql_token(const char *z, int *type, bool *is_reserved) case CC_RP: *type = TK_RP; return 1; + case CC_LB: + *type = TK_LB; + return 1; + case CC_RB: + *type = TK_RB; + return 1; case CC_SEMI: *type = TK_SEMI; return 1; diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 2e6893f1a..55e494332 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -1418,6 +1418,26 @@ case OP_Cast: { /* in1 */ goto abort_due_to_error; } +/* Opcode: Array P1 P2 P3 * * + * Synopsis: r[P2]=array(P3@P1) + * + * Construct an ARRAY value from P1 registers starting at reg(P3). + */ +case OP_Array: { + pOut = &aMem[pOp->p2]; + + uint32_t size; + struct region *region = &fiber()->gc; + size_t svp = region_used(region); + char *val = mem_encode_array(&aMem[pOp->p3], pOp->p1, &size, region); + if (val == NULL || mem_copy_array(pOut, val, size) != 0) { + region_truncate(region, svp); + goto abort_due_to_error; + } + region_truncate(region, svp); + break; +} + /* Opcode: Eq P1 P2 P3 P4 P5 * Synopsis: IF r[P3]==r[P1] * diff --git a/test/sql-tap/array.test.lua b/test/sql-tap/array.test.lua index 752cb24f2..79a1c831d 100755 --- a/test/sql-tap/array.test.lua +++ b/test/sql-tap/array.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool local test = require("sqltester") -test:plan(110) +test:plan(115) box.schema.func.create('A1', { language = 'Lua', @@ -979,6 +979,51 @@ test:do_catchsql_test( 1, "Failed to execute SQL statement: wrong arguments for function ZEROBLOB()" }) +-- Make sure syntax for ARRAY values works as intended. +test:do_execsql_test( + "array-13.1", + [[ + SELECT [a, g, t, n, f, i, b, v, s, d, u] FROM t1 WHERE id = 1; + ]], { + {{1}, 1, '1', 1, 1, 1, true, '1', 1, require('decimal').new(1), + require('uuid').fromstr('11111111-1111-1111-1111-111111111111')} + }) + +test:do_execsql_test( + "array-13.2", + [[ + SELECT [1, true, 1.5e0, ['asd', x'32'], 1234.0]; + ]], { + {1, true, 1.5, {'asd', '2'}, require('decimal').new(1234)} + }) + +test:do_execsql_test( + "array-13.3", + [[ + SELECT []; + ]], { + {} + }) + +local arr = {0} +local arr_str = '0' +for i = 1, 1000 do table.insert(arr, i) arr_str = arr_str .. ', ' .. i end +test:do_execsql_test( + "array-13.4", + [[ + SELECT []] .. arr_str .. [[]; + ]], { + arr + }) + +test:do_execsql_test( + "array-13.5", + [[ + SELECT typeof([1]); + ]], { + "array" + }) + box.execute([[DROP TABLE t1;]]) box.execute([[DROP TABLE t;]]) diff --git a/test/sql-tap/colname.test.lua b/test/sql-tap/colname.test.lua index ff7585c7a..698a446e1 100755 --- a/test/sql-tap/colname.test.lua +++ b/test/sql-tap/colname.test.lua @@ -1,6 +1,6 @@ #!/usr/bin/env tarantool local test = require("sqltester") -test:plan(79) +test:plan(76) --!./tcltestrunner.lua -- 2008 July 15 @@ -546,7 +546,6 @@ test:do_test( local data = { [[`a`]], - "[a]", } for i, val in ipairs(data) do test:do_catchsql_test( @@ -559,7 +558,6 @@ end local data2 = { {[['a']],{1, "/Syntax error/"}}, -- because ' is delimiter for strings {[[`a`]],{1, "/unrecognized token/"}}, -- because ` is undefined symbol - {"[a]",{1, "/unrecognized token/"}} -- because [ is undefined symbol } for i, val in ipairs(data2) do test:do_catchsql_test(