/src/tarantool/src/box/sql/prepare.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or |
5 | | * without modification, are permitted provided that the following |
6 | | * conditions are met: |
7 | | * |
8 | | * 1. Redistributions of source code must retain the above |
9 | | * copyright notice, this list of conditions and the |
10 | | * following disclaimer. |
11 | | * |
12 | | * 2. Redistributions in binary form must reproduce the above |
13 | | * copyright notice, this list of conditions and the following |
14 | | * disclaimer in the documentation and/or other materials |
15 | | * provided with the distribution. |
16 | | * |
17 | | * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND |
18 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
19 | | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
21 | | * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
22 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
24 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
25 | | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
26 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
28 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | | * SUCH DAMAGE. |
30 | | */ |
31 | | |
32 | | /* |
33 | | * This file contains the implementation of the sql_prepare() |
34 | | * interface, and routines that contribute to loading the database schema |
35 | | * from disk. |
36 | | */ |
37 | | #include "sqlInt.h" |
38 | | #include "tarantoolInt.h" |
39 | | #include "box/space.h" |
40 | | #include "box/session.h" |
41 | | |
42 | | int |
43 | | sql_stmt_compile(const char *zSql, int nBytes, struct Vdbe *pReprepare, |
44 | | struct Vdbe **ppStmt, const char **pzTail) |
45 | 7.28k | { |
46 | 7.28k | int rc = 0; /* Result code */ |
47 | 7.28k | Parse sParse; /* Parsing context */ |
48 | 7.28k | sql_parser_create(&sParse, current_session()->sql_flags); |
49 | 7.28k | sParse.pReprepare = pReprepare; |
50 | 7.28k | *ppStmt = NULL; |
51 | | |
52 | | /* Check to verify that it is possible to get a read lock on all |
53 | | * database schemas. The inability to get a read lock indicates that |
54 | | * some other database connection is holding a write-lock, which in |
55 | | * turn means that the other connection has made uncommitted changes |
56 | | * to the schema. |
57 | | * |
58 | | * Were we to proceed and prepare the statement against the uncommitted |
59 | | * schema changes and if those schema changes are subsequently rolled |
60 | | * back and different changes are made in their place, then when this |
61 | | * prepared statement goes to run the schema cookie would fail to detect |
62 | | * the schema change. Disaster would follow. |
63 | | * |
64 | | * Note that setting READ_UNCOMMITTED overrides most lock detection, |
65 | | * but it does *not* override schema lock detection, so this all still |
66 | | * works even if READ_UNCOMMITTED is set. |
67 | | */ |
68 | 7.28k | if (nBytes >= 0 && (nBytes == 0 || zSql[nBytes - 1] != 0)) { |
69 | 7.28k | char *zSqlCopy; |
70 | 7.28k | int mxLen = SQL_MAX_SQL_LENGTH; |
71 | 7.28k | if (nBytes > mxLen) { |
72 | 0 | diag_set(ClientError, ER_SQL_PARSER_LIMIT, |
73 | 0 | "SQL command length", nBytes, mxLen); |
74 | 0 | rc = -1; |
75 | 0 | goto end_prepare; |
76 | 0 | } |
77 | 7.28k | zSqlCopy = sql_xstrndup(zSql, nBytes); |
78 | 7.28k | if (zSqlCopy) { |
79 | 7.28k | sqlRunParser(&sParse, zSqlCopy); |
80 | 7.28k | sParse.zTail = &zSql[sParse.zTail - zSqlCopy]; |
81 | 7.28k | sql_xfree(zSqlCopy); |
82 | 7.28k | } else { |
83 | 0 | sParse.zTail = &zSql[nBytes]; |
84 | 0 | } |
85 | 7.28k | } else { |
86 | 0 | sqlRunParser(&sParse, zSql); |
87 | 0 | } |
88 | 7.28k | assert(0 == sParse.nQueryLoop || sParse.is_aborted); |
89 | | |
90 | 7.28k | if (pzTail) { |
91 | 0 | *pzTail = sParse.zTail; |
92 | 0 | } |
93 | 7.28k | if (sParse.is_aborted) |
94 | 2.57k | rc = -1; |
95 | | |
96 | 7.28k | if (rc == 0 && sParse.pVdbe != NULL && sParse.explain) { |
97 | 0 | static const char *const azColName[] = { |
98 | 0 | /* 0 */ "addr", |
99 | 0 | /* 1 */ "integer", |
100 | 0 | /* 2 */ "opcode", |
101 | 0 | /* 3 */ "text", |
102 | 0 | /* 4 */ "p1", |
103 | 0 | /* 5 */ "integer", |
104 | 0 | /* 6 */ "p2", |
105 | 0 | /* 7 */ "integer", |
106 | 0 | /* 8 */ "p3", |
107 | 0 | /* 9 */ "integer", |
108 | 0 | /* 10 */ "p4", |
109 | 0 | /* 11 */ "text", |
110 | 0 | /* 12 */ "p5", |
111 | 0 | /* 13 */ "text", |
112 | 0 | /* 14 */ "comment", |
113 | 0 | /* 15 */ "text", |
114 | 0 | /* 16 */ "selectid", |
115 | 0 | /* 17 */ "integer", |
116 | 0 | /* 18 */ "order", |
117 | 0 | /* 19 */ "integer", |
118 | 0 | /* 20 */ "from", |
119 | 0 | /* 21 */ "integer", |
120 | 0 | /* 22 */ "detail", |
121 | 0 | /* 23 */ "text", |
122 | 0 | }; |
123 | |
|
124 | 0 | int name_first, name_count; |
125 | 0 | if (sParse.explain == 2) { |
126 | 0 | name_first = 16; |
127 | 0 | name_count = 4; |
128 | 0 | } else { |
129 | 0 | name_first = 0; |
130 | 0 | name_count = 8; |
131 | 0 | } |
132 | 0 | sqlVdbeSetNumCols(sParse.pVdbe, name_count); |
133 | 0 | for (int i = 0; i < name_count; i++) { |
134 | 0 | int name_index = 2 * i + name_first; |
135 | 0 | vdbe_metadata_set_col_name(sParse.pVdbe, i, |
136 | 0 | azColName[name_index]); |
137 | 0 | vdbe_metadata_set_col_type(sParse.pVdbe, i, |
138 | 0 | azColName[name_index + 1]); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 7.28k | if (sql_get()->init.busy == 0) { |
143 | 7.28k | Vdbe *pVdbe = sParse.pVdbe; |
144 | 7.28k | sqlVdbeSetSql(pVdbe, zSql, (int)(sParse.zTail - zSql)); |
145 | 7.28k | } |
146 | 7.28k | if (sParse.pVdbe != NULL && rc != 0) { |
147 | 0 | sqlVdbeFinalize(sParse.pVdbe); |
148 | 0 | assert(!(*ppStmt)); |
149 | 7.28k | } else { |
150 | 7.28k | *ppStmt = sParse.pVdbe; |
151 | 7.28k | } |
152 | | |
153 | | /* Delete any TriggerPrg structures allocated while parsing this statement. */ |
154 | 7.28k | while (sParse.pTriggerPrg) { |
155 | 0 | TriggerPrg *pT = sParse.pTriggerPrg; |
156 | 0 | sParse.pTriggerPrg = pT->pNext; |
157 | 0 | sql_xfree(pT); |
158 | 0 | } |
159 | | |
160 | 7.28k | end_prepare: |
161 | | |
162 | 7.28k | sql_parser_destroy(&sParse); |
163 | 7.28k | return rc; |
164 | 7.28k | } |
165 | | |
166 | | /* |
167 | | * Rerun the compilation of a statement after a schema change. |
168 | | */ |
169 | | int |
170 | | sqlReprepare(Vdbe * p) |
171 | 0 | { |
172 | 0 | struct Vdbe *pNew; |
173 | 0 | const char *zSql; |
174 | |
|
175 | 0 | zSql = sql_sql(p); |
176 | 0 | assert(zSql != 0); |
177 | 0 | if (sql_stmt_compile(zSql, -1, p, &pNew, 0) != 0) { |
178 | 0 | assert(pNew == 0); |
179 | 0 | return -1; |
180 | 0 | } |
181 | 0 | assert(pNew != 0); |
182 | 0 | sqlVdbeSwap(pNew, p); |
183 | 0 | sqlTransferBindings(pNew, p); |
184 | 0 | sqlVdbeResetStepResult(pNew); |
185 | 0 | sqlVdbeFinalize(pNew); |
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | | void |
190 | | sql_parser_create(struct Parse *parser, uint32_t sql_flags) |
191 | 7.28k | { |
192 | 7.28k | memset(parser, 0, sizeof(struct Parse)); |
193 | 7.28k | parser->sql_flags = sql_flags; |
194 | 7.28k | parser->line_count = 1; |
195 | 7.28k | parser->line_pos = 1; |
196 | 7.28k | parser->has_autoinc = false; |
197 | 7.28k | region_create(&parser->region, &cord()->slabc); |
198 | 7.28k | } |
199 | | |
200 | | void |
201 | | sql_parser_destroy(Parse *parser) |
202 | 7.28k | { |
203 | 7.28k | assert(parser != NULL); |
204 | 0 | assert(!parser->parse_only || parser->pVdbe == NULL); |
205 | 0 | sql_xfree(parser->default_funcs); |
206 | 7.28k | sql_xfree(parser->aLabel); |
207 | 7.28k | sql_expr_list_delete(parser->pConstExpr); |
208 | 7.28k | struct create_fk_constraint_parse_def *create_fk_constraint_parse_def = |
209 | 7.28k | &parser->create_fk_constraint_parse_def; |
210 | 7.28k | create_fk_constraint_parse_def_destroy(create_fk_constraint_parse_def); |
211 | 7.28k | assert(sql_get()->lookaside.bDisable >= parser->disableLookaside); |
212 | 0 | sql_get()->lookaside.bDisable -= parser->disableLookaside; |
213 | 7.28k | parser->disableLookaside = 0; |
214 | 7.28k | switch (parser->parsed_ast_type) { |
215 | 0 | case AST_TYPE_SELECT: |
216 | 0 | sql_select_delete(parser->parsed_ast.select); |
217 | 0 | break; |
218 | 0 | case AST_TYPE_EXPR: |
219 | 0 | sql_expr_delete(parser->parsed_ast.expr); |
220 | 0 | break; |
221 | 0 | case AST_TYPE_TRIGGER: |
222 | 0 | sql_trigger_delete(parser->parsed_ast.trigger); |
223 | 0 | break; |
224 | 7.28k | default: |
225 | 7.28k | assert(parser->parsed_ast_type == AST_TYPE_UNDEFINED); |
226 | 7.28k | } |
227 | 7.28k | region_destroy(&parser->region); |
228 | 7.28k | } |