1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.expression; |
7 | |
8 | import static org.h2.util.ToChar.toChar; |
9 | |
10 | import java.io.IOException; |
11 | import java.io.InputStream; |
12 | import java.io.InputStreamReader; |
13 | import java.io.Reader; |
14 | import java.sql.Connection; |
15 | import java.sql.ResultSet; |
16 | import java.sql.SQLException; |
17 | import java.sql.Timestamp; |
18 | import java.text.SimpleDateFormat; |
19 | import java.util.ArrayList; |
20 | import java.util.Calendar; |
21 | import java.util.HashMap; |
22 | import java.util.Locale; |
23 | import java.util.TimeZone; |
24 | import java.util.regex.PatternSyntaxException; |
25 | |
26 | import org.h2.api.ErrorCode; |
27 | import org.h2.command.Command; |
28 | import org.h2.command.Parser; |
29 | import org.h2.engine.Constants; |
30 | import org.h2.engine.Database; |
31 | import org.h2.engine.Mode; |
32 | import org.h2.engine.Session; |
33 | import org.h2.message.DbException; |
34 | import org.h2.mvstore.DataUtils; |
35 | import org.h2.schema.Schema; |
36 | import org.h2.schema.Sequence; |
37 | import org.h2.security.BlockCipher; |
38 | import org.h2.security.CipherFactory; |
39 | import org.h2.security.SHA256; |
40 | import org.h2.store.fs.FileUtils; |
41 | import org.h2.table.Column; |
42 | import org.h2.table.ColumnResolver; |
43 | import org.h2.table.LinkSchema; |
44 | import org.h2.table.Table; |
45 | import org.h2.table.TableFilter; |
46 | import org.h2.tools.CompressTool; |
47 | import org.h2.tools.Csv; |
48 | import org.h2.util.AutoCloseInputStream; |
49 | import org.h2.util.DateTimeUtils; |
50 | import org.h2.util.JdbcUtils; |
51 | import org.h2.util.MathUtils; |
52 | import org.h2.util.New; |
53 | import org.h2.util.StatementBuilder; |
54 | import org.h2.util.StringUtils; |
55 | import org.h2.util.Utils; |
56 | import org.h2.value.DataType; |
57 | import org.h2.value.Value; |
58 | import org.h2.value.ValueArray; |
59 | import org.h2.value.ValueBoolean; |
60 | import org.h2.value.ValueBytes; |
61 | import org.h2.value.ValueDate; |
62 | import org.h2.value.ValueDouble; |
63 | import org.h2.value.ValueInt; |
64 | import org.h2.value.ValueLong; |
65 | import org.h2.value.ValueNull; |
66 | import org.h2.value.ValueResultSet; |
67 | import org.h2.value.ValueString; |
68 | import org.h2.value.ValueTime; |
69 | import org.h2.value.ValueTimestamp; |
70 | import org.h2.value.ValueUuid; |
71 | |
72 | /** |
73 | * This class implements most built-in functions of this database. |
74 | */ |
75 | public class Function extends Expression implements FunctionCall { |
76 | public static final int ABS = 0, ACOS = 1, ASIN = 2, ATAN = 3, ATAN2 = 4, |
77 | BITAND = 5, BITOR = 6, BITXOR = 7, CEILING = 8, COS = 9, COT = 10, |
78 | DEGREES = 11, EXP = 12, FLOOR = 13, LOG = 14, LOG10 = 15, MOD = 16, |
79 | PI = 17, POWER = 18, RADIANS = 19, RAND = 20, ROUND = 21, |
80 | ROUNDMAGIC = 22, SIGN = 23, SIN = 24, SQRT = 25, TAN = 26, |
81 | TRUNCATE = 27, SECURE_RAND = 28, HASH = 29, ENCRYPT = 30, |
82 | DECRYPT = 31, COMPRESS = 32, EXPAND = 33, ZERO = 34, |
83 | RANDOM_UUID = 35, COSH = 36, SINH = 37, TANH = 38, LN = 39; |
84 | |
85 | public static final int ASCII = 50, BIT_LENGTH = 51, CHAR = 52, |
86 | CHAR_LENGTH = 53, CONCAT = 54, DIFFERENCE = 55, HEXTORAW = 56, |
87 | INSERT = 57, INSTR = 58, LCASE = 59, LEFT = 60, LENGTH = 61, |
88 | LOCATE = 62, LTRIM = 63, OCTET_LENGTH = 64, RAWTOHEX = 65, |
89 | REPEAT = 66, REPLACE = 67, RIGHT = 68, RTRIM = 69, SOUNDEX = 70, |
90 | SPACE = 71, SUBSTR = 72, SUBSTRING = 73, UCASE = 74, LOWER = 75, |
91 | UPPER = 76, POSITION = 77, TRIM = 78, STRINGENCODE = 79, |
92 | STRINGDECODE = 80, STRINGTOUTF8 = 81, UTF8TOSTRING = 82, |
93 | XMLATTR = 83, XMLNODE = 84, XMLCOMMENT = 85, XMLCDATA = 86, |
94 | XMLSTARTDOC = 87, XMLTEXT = 88, REGEXP_REPLACE = 89, RPAD = 90, |
95 | LPAD = 91, CONCAT_WS = 92, TO_CHAR = 93, TRANSLATE = 94; |
96 | |
97 | public static final int CURDATE = 100, CURTIME = 101, DATE_ADD = 102, |
98 | DATE_DIFF = 103, DAY_NAME = 104, DAY_OF_MONTH = 105, |
99 | DAY_OF_WEEK = 106, DAY_OF_YEAR = 107, HOUR = 108, MINUTE = 109, |
100 | MONTH = 110, MONTH_NAME = 111, NOW = 112, QUARTER = 113, |
101 | SECOND = 114, WEEK = 115, YEAR = 116, CURRENT_DATE = 117, |
102 | CURRENT_TIME = 118, CURRENT_TIMESTAMP = 119, EXTRACT = 120, |
103 | FORMATDATETIME = 121, PARSEDATETIME = 122, ISO_YEAR = 123, |
104 | ISO_WEEK = 124, ISO_DAY_OF_WEEK = 125; |
105 | |
106 | public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152, |
107 | IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155, |
108 | READONLY = 156, DATABASE_PATH = 157, LOCK_TIMEOUT = 158, |
109 | DISK_SPACE_USED = 159; |
110 | |
111 | public static final int IFNULL = 200, CASEWHEN = 201, CONVERT = 202, |
112 | CAST = 203, COALESCE = 204, NULLIF = 205, CASE = 206, |
113 | NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, |
114 | CSVWRITE = 211, MEMORY_FREE = 212, MEMORY_USED = 213, |
115 | LOCK_MODE = 214, SCHEMA = 215, SESSION_ID = 216, |
116 | ARRAY_LENGTH = 217, LINK_SCHEMA = 218, GREATEST = 219, LEAST = 220, |
117 | CANCEL_SESSION = 221, SET = 222, TABLE = 223, TABLE_DISTINCT = 224, |
118 | FILE_READ = 225, TRANSACTION_ID = 226, TRUNCATE_VALUE = 227, |
119 | NVL2 = 228, DECODE = 229, ARRAY_CONTAINS = 230; |
120 | |
121 | /** |
122 | * Used in MySQL-style INSERT ... ON DUPLICATE KEY UPDATE ... VALUES |
123 | */ |
124 | public static final int VALUES = 250; |
125 | |
126 | /** |
127 | * This is called H2VERSION() and not VERSION(), because we return a fake |
128 | * value for VERSION() when running under the PostgreSQL ODBC driver. |
129 | */ |
130 | public static final int H2VERSION = 231; |
131 | |
132 | public static final int ROW_NUMBER = 300; |
133 | |
134 | private static final int VAR_ARGS = -1; |
135 | private static final long PRECISION_UNKNOWN = -1; |
136 | |
137 | private static final HashMap<String, FunctionInfo> FUNCTIONS = New.hashMap(); |
138 | private static final HashMap<String, Integer> DATE_PART = New.hashMap(); |
139 | private static final char[] SOUNDEX_INDEX = new char[128]; |
140 | |
141 | protected Expression[] args; |
142 | |
143 | private final FunctionInfo info; |
144 | private ArrayList<Expression> varArgs; |
145 | private int dataType, scale; |
146 | private long precision = PRECISION_UNKNOWN; |
147 | private int displaySize; |
148 | private final Database database; |
149 | |
150 | static { |
151 | // DATE_PART |
152 | DATE_PART.put("SQL_TSI_YEAR", Calendar.YEAR); |
153 | DATE_PART.put("YEAR", Calendar.YEAR); |
154 | DATE_PART.put("YYYY", Calendar.YEAR); |
155 | DATE_PART.put("YY", Calendar.YEAR); |
156 | DATE_PART.put("SQL_TSI_MONTH", Calendar.MONTH); |
157 | DATE_PART.put("MONTH", Calendar.MONTH); |
158 | DATE_PART.put("MM", Calendar.MONTH); |
159 | DATE_PART.put("M", Calendar.MONTH); |
160 | DATE_PART.put("SQL_TSI_WEEK", Calendar.WEEK_OF_YEAR); |
161 | DATE_PART.put("WW", Calendar.WEEK_OF_YEAR); |
162 | DATE_PART.put("WK", Calendar.WEEK_OF_YEAR); |
163 | DATE_PART.put("WEEK", Calendar.WEEK_OF_YEAR); |
164 | DATE_PART.put("DAY", Calendar.DAY_OF_MONTH); |
165 | DATE_PART.put("DD", Calendar.DAY_OF_MONTH); |
166 | DATE_PART.put("D", Calendar.DAY_OF_MONTH); |
167 | DATE_PART.put("SQL_TSI_DAY", Calendar.DAY_OF_MONTH); |
168 | DATE_PART.put("DAYOFYEAR", Calendar.DAY_OF_YEAR); |
169 | DATE_PART.put("DAY_OF_YEAR", Calendar.DAY_OF_YEAR); |
170 | DATE_PART.put("DY", Calendar.DAY_OF_YEAR); |
171 | DATE_PART.put("DOY", Calendar.DAY_OF_YEAR); |
172 | DATE_PART.put("SQL_TSI_HOUR", Calendar.HOUR_OF_DAY); |
173 | DATE_PART.put("HOUR", Calendar.HOUR_OF_DAY); |
174 | DATE_PART.put("HH", Calendar.HOUR_OF_DAY); |
175 | DATE_PART.put("SQL_TSI_MINUTE", Calendar.MINUTE); |
176 | DATE_PART.put("MINUTE", Calendar.MINUTE); |
177 | DATE_PART.put("MI", Calendar.MINUTE); |
178 | DATE_PART.put("N", Calendar.MINUTE); |
179 | DATE_PART.put("SQL_TSI_SECOND", Calendar.SECOND); |
180 | DATE_PART.put("SECOND", Calendar.SECOND); |
181 | DATE_PART.put("SS", Calendar.SECOND); |
182 | DATE_PART.put("S", Calendar.SECOND); |
183 | DATE_PART.put("MILLISECOND", Calendar.MILLISECOND); |
184 | DATE_PART.put("MS", Calendar.MILLISECOND); |
185 | |
186 | // SOUNDEX_INDEX |
187 | String index = "7AEIOUY8HW1BFPV2CGJKQSXZ3DT4L5MN6R"; |
188 | char number = 0; |
189 | for (int i = 0, length = index.length(); i < length; i++) { |
190 | char c = index.charAt(i); |
191 | if (c < '9') { |
192 | number = c; |
193 | } else { |
194 | SOUNDEX_INDEX[c] = number; |
195 | SOUNDEX_INDEX[Character.toLowerCase(c)] = number; |
196 | } |
197 | } |
198 | |
199 | // FUNCTIONS |
200 | addFunction("ABS", ABS, 1, Value.NULL); |
201 | addFunction("ACOS", ACOS, 1, Value.DOUBLE); |
202 | addFunction("ASIN", ASIN, 1, Value.DOUBLE); |
203 | addFunction("ATAN", ATAN, 1, Value.DOUBLE); |
204 | addFunction("ATAN2", ATAN2, 2, Value.DOUBLE); |
205 | addFunction("BITAND", BITAND, 2, Value.LONG); |
206 | addFunction("BITOR", BITOR, 2, Value.LONG); |
207 | addFunction("BITXOR", BITXOR, 2, Value.LONG); |
208 | addFunction("CEILING", CEILING, 1, Value.DOUBLE); |
209 | addFunction("CEIL", CEILING, 1, Value.DOUBLE); |
210 | addFunction("COS", COS, 1, Value.DOUBLE); |
211 | addFunction("COSH", COSH, 1, Value.DOUBLE); |
212 | addFunction("COT", COT, 1, Value.DOUBLE); |
213 | addFunction("DEGREES", DEGREES, 1, Value.DOUBLE); |
214 | addFunction("EXP", EXP, 1, Value.DOUBLE); |
215 | addFunction("FLOOR", FLOOR, 1, Value.DOUBLE); |
216 | addFunction("LOG", LOG, 1, Value.DOUBLE); |
217 | addFunction("LN", LN, 1, Value.DOUBLE); |
218 | addFunction("LOG10", LOG10, 1, Value.DOUBLE); |
219 | addFunction("MOD", MOD, 2, Value.LONG); |
220 | addFunction("PI", PI, 0, Value.DOUBLE); |
221 | addFunction("POWER", POWER, 2, Value.DOUBLE); |
222 | addFunction("RADIANS", RADIANS, 1, Value.DOUBLE); |
223 | // RAND without argument: get the next value |
224 | // RAND with one argument: seed the random generator |
225 | addFunctionNotDeterministic("RAND", RAND, VAR_ARGS, Value.DOUBLE); |
226 | addFunctionNotDeterministic("RANDOM", RAND, VAR_ARGS, Value.DOUBLE); |
227 | addFunction("ROUND", ROUND, VAR_ARGS, Value.DOUBLE); |
228 | addFunction("ROUNDMAGIC", ROUNDMAGIC, 1, Value.DOUBLE); |
229 | addFunction("SIGN", SIGN, 1, Value.INT); |
230 | addFunction("SIN", SIN, 1, Value.DOUBLE); |
231 | addFunction("SINH", SINH, 1, Value.DOUBLE); |
232 | addFunction("SQRT", SQRT, 1, Value.DOUBLE); |
233 | addFunction("TAN", TAN, 1, Value.DOUBLE); |
234 | addFunction("TANH", TANH, 1, Value.DOUBLE); |
235 | addFunction("TRUNCATE", TRUNCATE, VAR_ARGS, Value.NULL); |
236 | // same as TRUNCATE |
237 | addFunction("TRUNC", TRUNCATE, VAR_ARGS, Value.NULL); |
238 | addFunction("HASH", HASH, 3, Value.BYTES); |
239 | addFunction("ENCRYPT", ENCRYPT, 3, Value.BYTES); |
240 | addFunction("DECRYPT", DECRYPT, 3, Value.BYTES); |
241 | addFunctionNotDeterministic("SECURE_RAND", SECURE_RAND, 1, Value.BYTES); |
242 | addFunction("COMPRESS", COMPRESS, VAR_ARGS, Value.BYTES); |
243 | addFunction("EXPAND", EXPAND, 1, Value.BYTES); |
244 | addFunction("ZERO", ZERO, 0, Value.INT); |
245 | addFunctionNotDeterministic("RANDOM_UUID", RANDOM_UUID, 0, Value.UUID); |
246 | addFunctionNotDeterministic("SYS_GUID", RANDOM_UUID, 0, Value.UUID); |
247 | // string |
248 | addFunction("ASCII", ASCII, 1, Value.INT); |
249 | addFunction("BIT_LENGTH", BIT_LENGTH, 1, Value.LONG); |
250 | addFunction("CHAR", CHAR, 1, Value.STRING); |
251 | addFunction("CHR", CHAR, 1, Value.STRING); |
252 | addFunction("CHAR_LENGTH", CHAR_LENGTH, 1, Value.INT); |
253 | // same as CHAR_LENGTH |
254 | addFunction("CHARACTER_LENGTH", CHAR_LENGTH, 1, Value.INT); |
255 | addFunctionWithNull("CONCAT", CONCAT, VAR_ARGS, Value.STRING); |
256 | addFunctionWithNull("CONCAT_WS", CONCAT_WS, VAR_ARGS, Value.STRING); |
257 | addFunction("DIFFERENCE", DIFFERENCE, 2, Value.INT); |
258 | addFunction("HEXTORAW", HEXTORAW, 1, Value.STRING); |
259 | addFunctionWithNull("INSERT", INSERT, 4, Value.STRING); |
260 | addFunction("LCASE", LCASE, 1, Value.STRING); |
261 | addFunction("LEFT", LEFT, 2, Value.STRING); |
262 | addFunction("LENGTH", LENGTH, 1, Value.LONG); |
263 | // 2 or 3 arguments |
264 | addFunction("LOCATE", LOCATE, VAR_ARGS, Value.INT); |
265 | // alias for MSSQLServer |
266 | addFunction("CHARINDEX", LOCATE, VAR_ARGS, Value.INT); |
267 | // same as LOCATE with 2 arguments |
268 | addFunction("POSITION", LOCATE, 2, Value.INT); |
269 | addFunction("INSTR", INSTR, VAR_ARGS, Value.INT); |
270 | addFunction("LTRIM", LTRIM, VAR_ARGS, Value.STRING); |
271 | addFunction("OCTET_LENGTH", OCTET_LENGTH, 1, Value.LONG); |
272 | addFunction("RAWTOHEX", RAWTOHEX, 1, Value.STRING); |
273 | addFunction("REPEAT", REPEAT, 2, Value.STRING); |
274 | addFunction("REPLACE", REPLACE, VAR_ARGS, Value.STRING); |
275 | addFunction("RIGHT", RIGHT, 2, Value.STRING); |
276 | addFunction("RTRIM", RTRIM, VAR_ARGS, Value.STRING); |
277 | addFunction("SOUNDEX", SOUNDEX, 1, Value.STRING); |
278 | addFunction("SPACE", SPACE, 1, Value.STRING); |
279 | addFunction("SUBSTR", SUBSTR, VAR_ARGS, Value.STRING); |
280 | addFunction("SUBSTRING", SUBSTRING, VAR_ARGS, Value.STRING); |
281 | addFunction("UCASE", UCASE, 1, Value.STRING); |
282 | addFunction("LOWER", LOWER, 1, Value.STRING); |
283 | addFunction("UPPER", UPPER, 1, Value.STRING); |
284 | addFunction("POSITION", POSITION, 2, Value.INT); |
285 | addFunction("TRIM", TRIM, VAR_ARGS, Value.STRING); |
286 | addFunction("STRINGENCODE", STRINGENCODE, 1, Value.STRING); |
287 | addFunction("STRINGDECODE", STRINGDECODE, 1, Value.STRING); |
288 | addFunction("STRINGTOUTF8", STRINGTOUTF8, 1, Value.BYTES); |
289 | addFunction("UTF8TOSTRING", UTF8TOSTRING, 1, Value.STRING); |
290 | addFunction("XMLATTR", XMLATTR, 2, Value.STRING); |
291 | addFunctionWithNull("XMLNODE", XMLNODE, VAR_ARGS, Value.STRING); |
292 | addFunction("XMLCOMMENT", XMLCOMMENT, 1, Value.STRING); |
293 | addFunction("XMLCDATA", XMLCDATA, 1, Value.STRING); |
294 | addFunction("XMLSTARTDOC", XMLSTARTDOC, 0, Value.STRING); |
295 | addFunction("XMLTEXT", XMLTEXT, VAR_ARGS, Value.STRING); |
296 | addFunction("REGEXP_REPLACE", REGEXP_REPLACE, 3, Value.STRING); |
297 | addFunction("RPAD", RPAD, VAR_ARGS, Value.STRING); |
298 | addFunction("LPAD", LPAD, VAR_ARGS, Value.STRING); |
299 | addFunction("TO_CHAR", TO_CHAR, VAR_ARGS, Value.STRING); |
300 | addFunction("TRANSLATE", TRANSLATE, 3, Value.STRING); |
301 | |
302 | // date |
303 | addFunctionNotDeterministic("CURRENT_DATE", CURRENT_DATE, |
304 | 0, Value.DATE); |
305 | addFunctionNotDeterministic("CURDATE", CURDATE, |
306 | 0, Value.DATE); |
307 | // alias for MSSQLServer |
308 | addFunctionNotDeterministic("GETDATE", CURDATE, |
309 | 0, Value.DATE); |
310 | addFunctionNotDeterministic("CURRENT_TIME", CURRENT_TIME, |
311 | 0, Value.TIME); |
312 | addFunctionNotDeterministic("CURTIME", CURTIME, |
313 | 0, Value.TIME); |
314 | addFunctionNotDeterministic("CURRENT_TIMESTAMP", CURRENT_TIMESTAMP, |
315 | VAR_ARGS, Value.TIMESTAMP); |
316 | addFunctionNotDeterministic("NOW", NOW, |
317 | VAR_ARGS, Value.TIMESTAMP); |
318 | addFunction("DATEADD", DATE_ADD, |
319 | 3, Value.TIMESTAMP); |
320 | addFunction("TIMESTAMPADD", DATE_ADD, |
321 | 3, Value.LONG); |
322 | addFunction("DATEDIFF", DATE_DIFF, |
323 | 3, Value.LONG); |
324 | addFunction("TIMESTAMPDIFF", DATE_DIFF, |
325 | 3, Value.LONG); |
326 | addFunction("DAYNAME", DAY_NAME, |
327 | 1, Value.STRING); |
328 | addFunction("DAYNAME", DAY_NAME, |
329 | 1, Value.STRING); |
330 | addFunction("DAY", DAY_OF_MONTH, |
331 | 1, Value.INT); |
332 | addFunction("DAY_OF_MONTH", DAY_OF_MONTH, |
333 | 1, Value.INT); |
334 | addFunction("DAY_OF_WEEK", DAY_OF_WEEK, |
335 | 1, Value.INT); |
336 | addFunction("DAY_OF_YEAR", DAY_OF_YEAR, |
337 | 1, Value.INT); |
338 | addFunction("DAYOFMONTH", DAY_OF_MONTH, |
339 | 1, Value.INT); |
340 | addFunction("DAYOFWEEK", DAY_OF_WEEK, |
341 | 1, Value.INT); |
342 | addFunction("DAYOFYEAR", DAY_OF_YEAR, |
343 | 1, Value.INT); |
344 | addFunction("HOUR", HOUR, |
345 | 1, Value.INT); |
346 | addFunction("MINUTE", MINUTE, |
347 | 1, Value.INT); |
348 | addFunction("MONTH", MONTH, |
349 | 1, Value.INT); |
350 | addFunction("MONTHNAME", MONTH_NAME, |
351 | 1, Value.STRING); |
352 | addFunction("QUARTER", QUARTER, |
353 | 1, Value.INT); |
354 | addFunction("SECOND", SECOND, |
355 | 1, Value.INT); |
356 | addFunction("WEEK", WEEK, |
357 | 1, Value.INT); |
358 | addFunction("YEAR", YEAR, |
359 | 1, Value.INT); |
360 | addFunction("EXTRACT", EXTRACT, |
361 | 2, Value.INT); |
362 | addFunctionWithNull("FORMATDATETIME", FORMATDATETIME, |
363 | VAR_ARGS, Value.STRING); |
364 | addFunctionWithNull("PARSEDATETIME", PARSEDATETIME, |
365 | VAR_ARGS, Value.TIMESTAMP); |
366 | addFunction("ISO_YEAR", ISO_YEAR, |
367 | 1, Value.INT); |
368 | addFunction("ISO_WEEK", ISO_WEEK, |
369 | 1, Value.INT); |
370 | addFunction("ISO_DAY_OF_WEEK", ISO_DAY_OF_WEEK, |
371 | 1, Value.INT); |
372 | // system |
373 | addFunctionNotDeterministic("DATABASE", DATABASE, |
374 | 0, Value.STRING); |
375 | addFunctionNotDeterministic("USER", USER, |
376 | 0, Value.STRING); |
377 | addFunctionNotDeterministic("CURRENT_USER", CURRENT_USER, |
378 | 0, Value.STRING); |
379 | addFunctionNotDeterministic("IDENTITY", IDENTITY, |
380 | 0, Value.LONG); |
381 | addFunctionNotDeterministic("SCOPE_IDENTITY", SCOPE_IDENTITY, |
382 | 0, Value.LONG); |
383 | addFunctionNotDeterministic("IDENTITY_VAL_LOCAL", IDENTITY, |
384 | 0, Value.LONG); |
385 | addFunctionNotDeterministic("LAST_INSERT_ID", IDENTITY, |
386 | 0, Value.LONG); |
387 | addFunctionNotDeterministic("LASTVAL", IDENTITY, |
388 | 0, Value.LONG); |
389 | addFunctionNotDeterministic("AUTOCOMMIT", AUTOCOMMIT, |
390 | 0, Value.BOOLEAN); |
391 | addFunctionNotDeterministic("READONLY", READONLY, |
392 | 0, Value.BOOLEAN); |
393 | addFunction("DATABASE_PATH", DATABASE_PATH, |
394 | 0, Value.STRING); |
395 | addFunctionNotDeterministic("LOCK_TIMEOUT", LOCK_TIMEOUT, |
396 | 0, Value.INT); |
397 | addFunctionWithNull("IFNULL", IFNULL, |
398 | 2, Value.NULL); |
399 | addFunctionWithNull("ISNULL", IFNULL, |
400 | 2, Value.NULL); |
401 | addFunctionWithNull("CASEWHEN", CASEWHEN, |
402 | 3, Value.NULL); |
403 | addFunctionWithNull("CONVERT", CONVERT, |
404 | 1, Value.NULL); |
405 | addFunctionWithNull("CAST", CAST, |
406 | 1, Value.NULL); |
407 | addFunctionWithNull("TRUNCATE_VALUE", TRUNCATE_VALUE, |
408 | 3, Value.NULL); |
409 | addFunctionWithNull("COALESCE", COALESCE, |
410 | VAR_ARGS, Value.NULL); |
411 | addFunctionWithNull("NVL", COALESCE, |
412 | VAR_ARGS, Value.NULL); |
413 | addFunctionWithNull("NVL2", NVL2, |
414 | 3, Value.NULL); |
415 | addFunctionWithNull("NULLIF", NULLIF, |
416 | 2, Value.NULL); |
417 | addFunctionWithNull("CASE", CASE, |
418 | VAR_ARGS, Value.NULL); |
419 | addFunctionNotDeterministic("NEXTVAL", NEXTVAL, |
420 | VAR_ARGS, Value.LONG); |
421 | addFunctionNotDeterministic("CURRVAL", CURRVAL, |
422 | VAR_ARGS, Value.LONG); |
423 | addFunction("ARRAY_GET", ARRAY_GET, |
424 | 2, Value.STRING); |
425 | addFunction("ARRAY_CONTAINS", ARRAY_CONTAINS, |
426 | 2, Value.BOOLEAN, false, true, true); |
427 | addFunction("CSVREAD", CSVREAD, |
428 | VAR_ARGS, Value.RESULT_SET, false, false, false); |
429 | addFunction("CSVWRITE", CSVWRITE, |
430 | VAR_ARGS, Value.INT, false, false, true); |
431 | addFunctionNotDeterministic("MEMORY_FREE", MEMORY_FREE, |
432 | 0, Value.INT); |
433 | addFunctionNotDeterministic("MEMORY_USED", MEMORY_USED, |
434 | 0, Value.INT); |
435 | addFunctionNotDeterministic("LOCK_MODE", LOCK_MODE, |
436 | 0, Value.INT); |
437 | addFunctionNotDeterministic("SCHEMA", SCHEMA, |
438 | 0, Value.STRING); |
439 | addFunctionNotDeterministic("SESSION_ID", SESSION_ID, |
440 | 0, Value.INT); |
441 | addFunction("ARRAY_LENGTH", ARRAY_LENGTH, |
442 | 1, Value.INT); |
443 | addFunctionNotDeterministic("LINK_SCHEMA", LINK_SCHEMA, |
444 | 6, Value.RESULT_SET); |
445 | addFunctionWithNull("LEAST", LEAST, |
446 | VAR_ARGS, Value.NULL); |
447 | addFunctionWithNull("GREATEST", GREATEST, |
448 | VAR_ARGS, Value.NULL); |
449 | addFunctionNotDeterministic("CANCEL_SESSION", CANCEL_SESSION, |
450 | 1, Value.BOOLEAN); |
451 | addFunction("SET", SET, |
452 | 2, Value.NULL, false, false, true); |
453 | addFunction("FILE_READ", FILE_READ, |
454 | VAR_ARGS, Value.NULL, false, false, true); |
455 | addFunctionNotDeterministic("TRANSACTION_ID", TRANSACTION_ID, |
456 | 0, Value.STRING); |
457 | addFunctionWithNull("DECODE", DECODE, |
458 | VAR_ARGS, Value.NULL); |
459 | addFunctionNotDeterministic("DISK_SPACE_USED", DISK_SPACE_USED, |
460 | 1, Value.LONG); |
461 | addFunction("H2VERSION", H2VERSION, 0, Value.STRING); |
462 | |
463 | // TableFunction |
464 | addFunctionWithNull("TABLE", TABLE, |
465 | VAR_ARGS, Value.RESULT_SET); |
466 | addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, |
467 | VAR_ARGS, Value.RESULT_SET); |
468 | |
469 | // pseudo function |
470 | addFunctionWithNull("ROW_NUMBER", ROW_NUMBER, 0, Value.LONG); |
471 | |
472 | // ON DUPLICATE KEY VALUES function |
473 | addFunction("VALUES", VALUES, 1, Value.NULL, false, true, false); |
474 | } |
475 | |
476 | protected Function(Database database, FunctionInfo info) { |
477 | this.database = database; |
478 | this.info = info; |
479 | if (info.parameterCount == VAR_ARGS) { |
480 | varArgs = New.arrayList(); |
481 | } else { |
482 | args = new Expression[info.parameterCount]; |
483 | } |
484 | } |
485 | |
486 | private static void addFunction(String name, int type, int parameterCount, |
487 | int dataType, boolean nullIfParameterIsNull, boolean deterministic, |
488 | boolean bufferResultSetToLocalTemp) { |
489 | FunctionInfo info = new FunctionInfo(); |
490 | info.name = name; |
491 | info.type = type; |
492 | info.parameterCount = parameterCount; |
493 | info.dataType = dataType; |
494 | info.nullIfParameterIsNull = nullIfParameterIsNull; |
495 | info.deterministic = deterministic; |
496 | info.bufferResultSetToLocalTemp = bufferResultSetToLocalTemp; |
497 | FUNCTIONS.put(name, info); |
498 | } |
499 | |
500 | private static void addFunctionNotDeterministic(String name, int type, |
501 | int parameterCount, int dataType) { |
502 | addFunction(name, type, parameterCount, dataType, true, false, true); |
503 | } |
504 | |
505 | private static void addFunction(String name, int type, int parameterCount, |
506 | int dataType) { |
507 | addFunction(name, type, parameterCount, dataType, true, true, true); |
508 | } |
509 | |
510 | private static void addFunctionWithNull(String name, int type, |
511 | int parameterCount, int dataType) { |
512 | addFunction(name, type, parameterCount, dataType, false, true, true); |
513 | } |
514 | |
515 | /** |
516 | * Get the function info object for this function, or null if there is no |
517 | * such function. |
518 | * |
519 | * @param name the function name |
520 | * @return the function info |
521 | */ |
522 | private static FunctionInfo getFunctionInfo(String name) { |
523 | return FUNCTIONS.get(name); |
524 | } |
525 | |
526 | /** |
527 | * Get an instance of the given function for this database. |
528 | * If no function with this name is found, null is returned. |
529 | * |
530 | * @param database the database |
531 | * @param name the function name |
532 | * @return the function object or null |
533 | */ |
534 | public static Function getFunction(Database database, String name) { |
535 | if (!database.getSettings().databaseToUpper) { |
536 | // if not yet converted to uppercase, do it now |
537 | name = StringUtils.toUpperEnglish(name); |
538 | } |
539 | FunctionInfo info = getFunctionInfo(name); |
540 | if (info == null) { |
541 | return null; |
542 | } |
543 | switch(info.type) { |
544 | case TABLE: |
545 | case TABLE_DISTINCT: |
546 | return new TableFunction(database, info, Long.MAX_VALUE); |
547 | default: |
548 | return new Function(database, info); |
549 | } |
550 | } |
551 | |
552 | /** |
553 | * Set the parameter expression at the given index. |
554 | * |
555 | * @param index the index (0, 1,...) |
556 | * @param param the expression |
557 | */ |
558 | public void setParameter(int index, Expression param) { |
559 | if (varArgs != null) { |
560 | varArgs.add(param); |
561 | } else { |
562 | if (index >= args.length) { |
563 | throw DbException.get(ErrorCode.INVALID_PARAMETER_COUNT_2, |
564 | info.name, "" + args.length); |
565 | } |
566 | args[index] = param; |
567 | } |
568 | } |
569 | |
570 | private static strictfp double log10(double value) { |
571 | return roundMagic(StrictMath.log(value) / StrictMath.log(10)); |
572 | } |
573 | |
574 | @Override |
575 | public Value getValue(Session session) { |
576 | return getValueWithArgs(session, args); |
577 | } |
578 | |
579 | private Value getSimpleValue(Session session, Value v0, Expression[] args, |
580 | Value[] values) { |
581 | Value result; |
582 | switch (info.type) { |
583 | case ABS: |
584 | result = v0.getSignum() > 0 ? v0 : v0.negate(); |
585 | break; |
586 | case ACOS: |
587 | result = ValueDouble.get(Math.acos(v0.getDouble())); |
588 | break; |
589 | case ASIN: |
590 | result = ValueDouble.get(Math.asin(v0.getDouble())); |
591 | break; |
592 | case ATAN: |
593 | result = ValueDouble.get(Math.atan(v0.getDouble())); |
594 | break; |
595 | case CEILING: |
596 | result = ValueDouble.get(Math.ceil(v0.getDouble())); |
597 | break; |
598 | case COS: |
599 | result = ValueDouble.get(Math.cos(v0.getDouble())); |
600 | break; |
601 | case COSH: |
602 | result = ValueDouble.get(Math.cosh(v0.getDouble())); |
603 | break; |
604 | case COT: { |
605 | double d = Math.tan(v0.getDouble()); |
606 | if (d == 0.0) { |
607 | throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); |
608 | } |
609 | result = ValueDouble.get(1. / d); |
610 | break; |
611 | } |
612 | case DEGREES: |
613 | result = ValueDouble.get(Math.toDegrees(v0.getDouble())); |
614 | break; |
615 | case EXP: |
616 | result = ValueDouble.get(Math.exp(v0.getDouble())); |
617 | break; |
618 | case FLOOR: |
619 | result = ValueDouble.get(Math.floor(v0.getDouble())); |
620 | break; |
621 | case LN: |
622 | result = ValueDouble.get(Math.log(v0.getDouble())); |
623 | break; |
624 | case LOG: |
625 | if (database.getMode().logIsLogBase10) { |
626 | result = ValueDouble.get(Math.log10(v0.getDouble())); |
627 | } else { |
628 | result = ValueDouble.get(Math.log(v0.getDouble())); |
629 | } |
630 | break; |
631 | case LOG10: |
632 | result = ValueDouble.get(log10(v0.getDouble())); |
633 | break; |
634 | case PI: |
635 | result = ValueDouble.get(Math.PI); |
636 | break; |
637 | case RADIANS: |
638 | result = ValueDouble.get(Math.toRadians(v0.getDouble())); |
639 | break; |
640 | case RAND: { |
641 | if (v0 != null) { |
642 | session.getRandom().setSeed(v0.getInt()); |
643 | } |
644 | result = ValueDouble.get(session.getRandom().nextDouble()); |
645 | break; |
646 | } |
647 | case ROUNDMAGIC: |
648 | result = ValueDouble.get(roundMagic(v0.getDouble())); |
649 | break; |
650 | case SIGN: |
651 | result = ValueInt.get(v0.getSignum()); |
652 | break; |
653 | case SIN: |
654 | result = ValueDouble.get(Math.sin(v0.getDouble())); |
655 | break; |
656 | case SINH: |
657 | result = ValueDouble.get(Math.sinh(v0.getDouble())); |
658 | break; |
659 | case SQRT: |
660 | result = ValueDouble.get(Math.sqrt(v0.getDouble())); |
661 | break; |
662 | case TAN: |
663 | result = ValueDouble.get(Math.tan(v0.getDouble())); |
664 | break; |
665 | case TANH: |
666 | result = ValueDouble.get(Math.tanh(v0.getDouble())); |
667 | break; |
668 | case SECURE_RAND: |
669 | result = ValueBytes.getNoCopy( |
670 | MathUtils.secureRandomBytes(v0.getInt())); |
671 | break; |
672 | case EXPAND: |
673 | result = ValueBytes.getNoCopy( |
674 | CompressTool.getInstance().expand(v0.getBytesNoCopy())); |
675 | break; |
676 | case ZERO: |
677 | result = ValueInt.get(0); |
678 | break; |
679 | case RANDOM_UUID: |
680 | result = ValueUuid.getNewRandom(); |
681 | break; |
682 | // string |
683 | case ASCII: { |
684 | String s = v0.getString(); |
685 | if (s.length() == 0) { |
686 | result = ValueNull.INSTANCE; |
687 | } else { |
688 | result = ValueInt.get(s.charAt(0)); |
689 | } |
690 | break; |
691 | } |
692 | case BIT_LENGTH: |
693 | result = ValueLong.get(16 * length(v0)); |
694 | break; |
695 | case CHAR: |
696 | result = ValueString.get(String.valueOf((char) v0.getInt()), |
697 | database.getMode().treatEmptyStringsAsNull); |
698 | break; |
699 | case CHAR_LENGTH: |
700 | case LENGTH: |
701 | result = ValueLong.get(length(v0)); |
702 | break; |
703 | case OCTET_LENGTH: |
704 | result = ValueLong.get(2 * length(v0)); |
705 | break; |
706 | case CONCAT_WS: |
707 | case CONCAT: { |
708 | result = ValueNull.INSTANCE; |
709 | int start = 0; |
710 | String separator = ""; |
711 | if (info.type == CONCAT_WS) { |
712 | start = 1; |
713 | separator = getNullOrValue(session, args, values, 0).getString(); |
714 | } |
715 | for (int i = start; i < args.length; i++) { |
716 | Value v = getNullOrValue(session, args, values, i); |
717 | if (v == ValueNull.INSTANCE) { |
718 | continue; |
719 | } |
720 | if (result == ValueNull.INSTANCE) { |
721 | result = v; |
722 | } else { |
723 | String tmp = v.getString(); |
724 | if (!StringUtils.isNullOrEmpty(separator) |
725 | && !StringUtils.isNullOrEmpty(tmp)) { |
726 | tmp = separator.concat(tmp); |
727 | } |
728 | result = ValueString.get(result.getString().concat(tmp), |
729 | database.getMode().treatEmptyStringsAsNull); |
730 | } |
731 | } |
732 | if (info.type == CONCAT_WS) { |
733 | if (separator != null && result == ValueNull.INSTANCE) { |
734 | result = ValueString.get("", |
735 | database.getMode().treatEmptyStringsAsNull); |
736 | } |
737 | } |
738 | break; |
739 | } |
740 | case HEXTORAW: |
741 | result = ValueString.get(hexToRaw(v0.getString()), |
742 | database.getMode().treatEmptyStringsAsNull); |
743 | break; |
744 | case LOWER: |
745 | case LCASE: |
746 | // TODO this is locale specific, need to document or provide a way |
747 | // to set the locale |
748 | result = ValueString.get(v0.getString().toLowerCase(), |
749 | database.getMode().treatEmptyStringsAsNull); |
750 | break; |
751 | case RAWTOHEX: |
752 | result = ValueString.get(rawToHex(v0.getString()), |
753 | database.getMode().treatEmptyStringsAsNull); |
754 | break; |
755 | case SOUNDEX: |
756 | result = ValueString.get(getSoundex(v0.getString()), |
757 | database.getMode().treatEmptyStringsAsNull); |
758 | break; |
759 | case SPACE: { |
760 | int len = Math.max(0, v0.getInt()); |
761 | char[] chars = new char[len]; |
762 | for (int i = len - 1; i >= 0; i--) { |
763 | chars[i] = ' '; |
764 | } |
765 | result = ValueString.get(new String(chars), |
766 | database.getMode().treatEmptyStringsAsNull); |
767 | break; |
768 | } |
769 | case UPPER: |
770 | case UCASE: |
771 | // TODO this is locale specific, need to document or provide a way |
772 | // to set the locale |
773 | result = ValueString.get(v0.getString().toUpperCase(), |
774 | database.getMode().treatEmptyStringsAsNull); |
775 | break; |
776 | case STRINGENCODE: |
777 | result = ValueString.get(StringUtils.javaEncode(v0.getString()), |
778 | database.getMode().treatEmptyStringsAsNull); |
779 | break; |
780 | case STRINGDECODE: |
781 | result = ValueString.get(StringUtils.javaDecode(v0.getString()), |
782 | database.getMode().treatEmptyStringsAsNull); |
783 | break; |
784 | case STRINGTOUTF8: |
785 | result = ValueBytes.getNoCopy(v0.getString(). |
786 | getBytes(Constants.UTF8)); |
787 | break; |
788 | case UTF8TOSTRING: |
789 | result = ValueString.get(new String(v0.getBytesNoCopy(), |
790 | Constants.UTF8), |
791 | database.getMode().treatEmptyStringsAsNull); |
792 | break; |
793 | case XMLCOMMENT: |
794 | result = ValueString.get(StringUtils.xmlComment(v0.getString()), |
795 | database.getMode().treatEmptyStringsAsNull); |
796 | break; |
797 | case XMLCDATA: |
798 | result = ValueString.get(StringUtils.xmlCData(v0.getString()), |
799 | database.getMode().treatEmptyStringsAsNull); |
800 | break; |
801 | case XMLSTARTDOC: |
802 | result = ValueString.get(StringUtils.xmlStartDoc(), |
803 | database.getMode().treatEmptyStringsAsNull); |
804 | break; |
805 | case DAY_NAME: { |
806 | SimpleDateFormat dayName = new SimpleDateFormat( |
807 | "EEEE", Locale.ENGLISH); |
808 | result = ValueString.get(dayName.format(v0.getDate()), |
809 | database.getMode().treatEmptyStringsAsNull); |
810 | break; |
811 | } |
812 | case DAY_OF_MONTH: |
813 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
814 | Calendar.DAY_OF_MONTH)); |
815 | break; |
816 | case DAY_OF_WEEK: |
817 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
818 | Calendar.DAY_OF_WEEK)); |
819 | break; |
820 | case DAY_OF_YEAR: |
821 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
822 | Calendar.DAY_OF_YEAR)); |
823 | break; |
824 | case HOUR: |
825 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getTimestamp(), |
826 | Calendar.HOUR_OF_DAY)); |
827 | break; |
828 | case MINUTE: |
829 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getTimestamp(), |
830 | Calendar.MINUTE)); |
831 | break; |
832 | case MONTH: |
833 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
834 | Calendar.MONTH)); |
835 | break; |
836 | case MONTH_NAME: { |
837 | SimpleDateFormat monthName = new SimpleDateFormat("MMMM", |
838 | Locale.ENGLISH); |
839 | result = ValueString.get(monthName.format(v0.getDate()), |
840 | database.getMode().treatEmptyStringsAsNull); |
841 | break; |
842 | } |
843 | case QUARTER: |
844 | result = ValueInt.get((DateTimeUtils.getDatePart(v0.getDate(), |
845 | Calendar.MONTH) - 1) / 3 + 1); |
846 | break; |
847 | case SECOND: |
848 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getTimestamp(), |
849 | Calendar.SECOND)); |
850 | break; |
851 | case WEEK: |
852 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
853 | Calendar.WEEK_OF_YEAR)); |
854 | break; |
855 | case YEAR: |
856 | result = ValueInt.get(DateTimeUtils.getDatePart(v0.getDate(), |
857 | Calendar.YEAR)); |
858 | break; |
859 | case ISO_YEAR: |
860 | result = ValueInt.get(DateTimeUtils.getIsoYear(v0.getDate())); |
861 | break; |
862 | case ISO_WEEK: |
863 | result = ValueInt.get(DateTimeUtils.getIsoWeek(v0.getDate())); |
864 | break; |
865 | case ISO_DAY_OF_WEEK: |
866 | result = ValueInt.get(DateTimeUtils.getIsoDayOfWeek(v0.getDate())); |
867 | break; |
868 | case CURDATE: |
869 | case CURRENT_DATE: { |
870 | long now = session.getTransactionStart(); |
871 | // need to normalize |
872 | result = ValueDate.fromMillis(now); |
873 | break; |
874 | } |
875 | case CURTIME: |
876 | case CURRENT_TIME: { |
877 | long now = session.getTransactionStart(); |
878 | // need to normalize |
879 | result = ValueTime.fromMillis(now); |
880 | break; |
881 | } |
882 | case NOW: |
883 | case CURRENT_TIMESTAMP: { |
884 | long now = session.getTransactionStart(); |
885 | ValueTimestamp vt = ValueTimestamp.fromMillis(now); |
886 | if (v0 != null) { |
887 | Mode mode = database.getMode(); |
888 | vt = (ValueTimestamp) vt.convertScale( |
889 | mode.convertOnlyToSmallerScale, v0.getInt()); |
890 | } |
891 | result = vt; |
892 | break; |
893 | } |
894 | case DATABASE: |
895 | result = ValueString.get(database.getShortName(), |
896 | database.getMode().treatEmptyStringsAsNull); |
897 | break; |
898 | case USER: |
899 | case CURRENT_USER: |
900 | result = ValueString.get(session.getUser().getName(), |
901 | database.getMode().treatEmptyStringsAsNull); |
902 | break; |
903 | case IDENTITY: |
904 | result = session.getLastIdentity(); |
905 | break; |
906 | case SCOPE_IDENTITY: |
907 | result = session.getLastScopeIdentity(); |
908 | break; |
909 | case AUTOCOMMIT: |
910 | result = ValueBoolean.get(session.getAutoCommit()); |
911 | break; |
912 | case READONLY: |
913 | result = ValueBoolean.get(database.isReadOnly()); |
914 | break; |
915 | case DATABASE_PATH: { |
916 | String path = database.getDatabasePath(); |
917 | result = path == null ? |
918 | (Value) ValueNull.INSTANCE : ValueString.get(path, |
919 | database.getMode().treatEmptyStringsAsNull); |
920 | break; |
921 | } |
922 | case LOCK_TIMEOUT: |
923 | result = ValueInt.get(session.getLockTimeout()); |
924 | break; |
925 | case DISK_SPACE_USED: |
926 | result = ValueLong.get(getDiskSpaceUsed(session, v0)); |
927 | break; |
928 | case CAST: |
929 | case CONVERT: { |
930 | v0 = v0.convertTo(dataType); |
931 | Mode mode = database.getMode(); |
932 | v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale); |
933 | v0 = v0.convertPrecision(getPrecision(), false); |
934 | result = v0; |
935 | break; |
936 | } |
937 | case MEMORY_FREE: |
938 | session.getUser().checkAdmin(); |
939 | result = ValueInt.get(Utils.getMemoryFree()); |
940 | break; |
941 | case MEMORY_USED: |
942 | session.getUser().checkAdmin(); |
943 | result = ValueInt.get(Utils.getMemoryUsed()); |
944 | break; |
945 | case LOCK_MODE: |
946 | result = ValueInt.get(database.getLockMode()); |
947 | break; |
948 | case SCHEMA: |
949 | result = ValueString.get(session.getCurrentSchemaName(), |
950 | database.getMode().treatEmptyStringsAsNull); |
951 | break; |
952 | case SESSION_ID: |
953 | result = ValueInt.get(session.getId()); |
954 | break; |
955 | case IFNULL: { |
956 | result = v0; |
957 | if (v0 == ValueNull.INSTANCE) { |
958 | result = getNullOrValue(session, args, values, 1); |
959 | } |
960 | break; |
961 | } |
962 | case CASEWHEN: { |
963 | Value v; |
964 | if (v0 == ValueNull.INSTANCE || |
965 | !v0.getBoolean().booleanValue()) { |
966 | v = getNullOrValue(session, args, values, 2); |
967 | } else { |
968 | v = getNullOrValue(session, args, values, 1); |
969 | } |
970 | result = v.convertTo(dataType); |
971 | break; |
972 | } |
973 | case DECODE: { |
974 | int index = -1; |
975 | for (int i = 1, len = args.length - 1; i < len; i += 2) { |
976 | if (database.areEqual(v0, |
977 | getNullOrValue(session, args, values, i))) { |
978 | index = i + 1; |
979 | break; |
980 | } |
981 | } |
982 | if (index < 0 && args.length % 2 == 0) { |
983 | index = args.length - 1; |
984 | } |
985 | Value v = index < 0 ? ValueNull.INSTANCE : |
986 | getNullOrValue(session, args, values, index); |
987 | result = v.convertTo(dataType); |
988 | break; |
989 | } |
990 | case NVL2: { |
991 | Value v; |
992 | if (v0 == ValueNull.INSTANCE) { |
993 | v = getNullOrValue(session, args, values, 2); |
994 | } else { |
995 | v = getNullOrValue(session, args, values, 1); |
996 | } |
997 | result = v.convertTo(dataType); |
998 | break; |
999 | } |
1000 | case COALESCE: { |
1001 | result = v0; |
1002 | for (int i = 0; i < args.length; i++) { |
1003 | Value v = getNullOrValue(session, args, values, i); |
1004 | if (!(v == ValueNull.INSTANCE)) { |
1005 | result = v.convertTo(dataType); |
1006 | break; |
1007 | } |
1008 | } |
1009 | break; |
1010 | } |
1011 | case GREATEST: |
1012 | case LEAST: { |
1013 | result = ValueNull.INSTANCE; |
1014 | for (int i = 0; i < args.length; i++) { |
1015 | Value v = getNullOrValue(session, args, values, i); |
1016 | if (!(v == ValueNull.INSTANCE)) { |
1017 | v = v.convertTo(dataType); |
1018 | if (result == ValueNull.INSTANCE) { |
1019 | result = v; |
1020 | } else { |
1021 | int comp = database.compareTypeSave(result, v); |
1022 | if (info.type == GREATEST && comp < 0) { |
1023 | result = v; |
1024 | } else if (info.type == LEAST && comp > 0) { |
1025 | result = v; |
1026 | } |
1027 | } |
1028 | } |
1029 | } |
1030 | break; |
1031 | } |
1032 | case CASE: { |
1033 | Expression then = null; |
1034 | if (v0 == null) { |
1035 | // Searched CASE expression |
1036 | // (null, when, then) |
1037 | // (null, when, then, else) |
1038 | // (null, when, then, when, then) |
1039 | // (null, when, then, when, then, else) |
1040 | for (int i = 1, len = args.length - 1; i < len; i += 2) { |
1041 | Value when = args[i].getValue(session); |
1042 | if (!(when == ValueNull.INSTANCE) && |
1043 | when.getBoolean().booleanValue()) { |
1044 | then = args[i + 1]; |
1045 | break; |
1046 | } |
1047 | } |
1048 | } else { |
1049 | // Simple CASE expression |
1050 | // (expr, when, then) |
1051 | // (expr, when, then, else) |
1052 | // (expr, when, then, when, then) |
1053 | // (expr, when, then, when, then, else) |
1054 | if (!(v0 == ValueNull.INSTANCE)) { |
1055 | for (int i = 1, len = args.length - 1; i < len; i += 2) { |
1056 | Value when = args[i].getValue(session); |
1057 | if (database.areEqual(v0, when)) { |
1058 | then = args[i + 1]; |
1059 | break; |
1060 | } |
1061 | } |
1062 | } |
1063 | } |
1064 | if (then == null && args.length % 2 == 0) { |
1065 | // then = elsePart |
1066 | then = args[args.length - 1]; |
1067 | } |
1068 | Value v = then == null ? ValueNull.INSTANCE : then.getValue(session); |
1069 | result = v.convertTo(dataType); |
1070 | break; |
1071 | } |
1072 | case ARRAY_GET: { |
1073 | if (v0.getType() == Value.ARRAY) { |
1074 | Value v1 = getNullOrValue(session, args, values, 1); |
1075 | int element = v1.getInt(); |
1076 | Value[] list = ((ValueArray) v0).getList(); |
1077 | if (element < 1 || element > list.length) { |
1078 | result = ValueNull.INSTANCE; |
1079 | } else { |
1080 | result = list[element - 1]; |
1081 | } |
1082 | } else { |
1083 | result = ValueNull.INSTANCE; |
1084 | } |
1085 | break; |
1086 | } |
1087 | case ARRAY_LENGTH: { |
1088 | if (v0.getType() == Value.ARRAY) { |
1089 | Value[] list = ((ValueArray) v0).getList(); |
1090 | result = ValueInt.get(list.length); |
1091 | } else { |
1092 | result = ValueNull.INSTANCE; |
1093 | } |
1094 | break; |
1095 | } |
1096 | case ARRAY_CONTAINS: { |
1097 | result = ValueBoolean.get(false); |
1098 | if (v0.getType() == Value.ARRAY) { |
1099 | Value v1 = getNullOrValue(session, args, values, 1); |
1100 | Value[] list = ((ValueArray) v0).getList(); |
1101 | for (Value v : list) { |
1102 | if (v.equals(v1)) { |
1103 | result = ValueBoolean.get(true); |
1104 | break; |
1105 | } |
1106 | } |
1107 | } |
1108 | break; |
1109 | } |
1110 | case CANCEL_SESSION: { |
1111 | result = ValueBoolean.get(cancelStatement(session, v0.getInt())); |
1112 | break; |
1113 | } |
1114 | case TRANSACTION_ID: { |
1115 | result = session.getTransactionId(); |
1116 | break; |
1117 | } |
1118 | default: |
1119 | result = null; |
1120 | } |
1121 | return result; |
1122 | } |
1123 | |
1124 | private static boolean cancelStatement(Session session, int targetSessionId) { |
1125 | session.getUser().checkAdmin(); |
1126 | Session[] sessions = session.getDatabase().getSessions(false); |
1127 | for (Session s : sessions) { |
1128 | if (s.getId() == targetSessionId) { |
1129 | Command c = s.getCurrentCommand(); |
1130 | if (c == null) { |
1131 | return false; |
1132 | } |
1133 | c.cancel(); |
1134 | return true; |
1135 | } |
1136 | } |
1137 | return false; |
1138 | } |
1139 | |
1140 | private static long getDiskSpaceUsed(Session session, Value v0) { |
1141 | Parser p = new Parser(session); |
1142 | String sql = v0.getString(); |
1143 | Table table = p.parseTableName(sql); |
1144 | return table.getDiskSpaceUsed(); |
1145 | } |
1146 | |
1147 | private static Value getNullOrValue(Session session, Expression[] args, |
1148 | Value[] values, int i) { |
1149 | if (i >= args.length) { |
1150 | return null; |
1151 | } |
1152 | Value v = values[i]; |
1153 | if (v == null) { |
1154 | Expression e = args[i]; |
1155 | if (e == null) { |
1156 | return null; |
1157 | } |
1158 | v = values[i] = e.getValue(session); |
1159 | } |
1160 | return v; |
1161 | } |
1162 | |
1163 | private Value getValueWithArgs(Session session, Expression[] args) { |
1164 | Value[] values = new Value[args.length]; |
1165 | if (info.nullIfParameterIsNull) { |
1166 | for (int i = 0; i < args.length; i++) { |
1167 | Expression e = args[i]; |
1168 | Value v = e.getValue(session); |
1169 | if (v == ValueNull.INSTANCE) { |
1170 | return ValueNull.INSTANCE; |
1171 | } |
1172 | values[i] = v; |
1173 | } |
1174 | } |
1175 | Value v0 = getNullOrValue(session, args, values, 0); |
1176 | Value resultSimple = getSimpleValue(session, v0, args, values); |
1177 | if (resultSimple != null) { |
1178 | return resultSimple; |
1179 | } |
1180 | Value v1 = getNullOrValue(session, args, values, 1); |
1181 | Value v2 = getNullOrValue(session, args, values, 2); |
1182 | Value v3 = getNullOrValue(session, args, values, 3); |
1183 | Value v4 = getNullOrValue(session, args, values, 4); |
1184 | Value v5 = getNullOrValue(session, args, values, 5); |
1185 | Value result; |
1186 | switch (info.type) { |
1187 | case ATAN2: |
1188 | result = ValueDouble.get( |
1189 | Math.atan2(v0.getDouble(), v1.getDouble())); |
1190 | break; |
1191 | case BITAND: |
1192 | result = ValueLong.get(v0.getLong() & v1.getLong()); |
1193 | break; |
1194 | case BITOR: |
1195 | result = ValueLong.get(v0.getLong() | v1.getLong()); |
1196 | break; |
1197 | case BITXOR: |
1198 | result = ValueLong.get(v0.getLong() ^ v1.getLong()); |
1199 | break; |
1200 | case MOD: { |
1201 | long x = v1.getLong(); |
1202 | if (x == 0) { |
1203 | throw DbException.get(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); |
1204 | } |
1205 | result = ValueLong.get(v0.getLong() % x); |
1206 | break; |
1207 | } |
1208 | case POWER: |
1209 | result = ValueDouble.get(Math.pow( |
1210 | v0.getDouble(), v1.getDouble())); |
1211 | break; |
1212 | case ROUND: { |
1213 | double f = v1 == null ? 1. : Math.pow(10., v1.getDouble()); |
1214 | result = ValueDouble.get(Math.round(v0.getDouble() * f) / f); |
1215 | break; |
1216 | } |
1217 | case TRUNCATE: { |
1218 | if (v0.getType() == Value.TIMESTAMP) { |
1219 | java.sql.Timestamp d = v0.getTimestamp(); |
1220 | Calendar c = Calendar.getInstance(); |
1221 | c.setTime(d); |
1222 | c.set(Calendar.HOUR_OF_DAY, 0); |
1223 | c.set(Calendar.MINUTE, 0); |
1224 | c.set(Calendar.SECOND, 0); |
1225 | c.set(Calendar.MILLISECOND, 0); |
1226 | result = ValueTimestamp.fromMillis(c.getTimeInMillis()); |
1227 | } else { |
1228 | double d = v0.getDouble(); |
1229 | int p = v1 == null ? 0 : v1.getInt(); |
1230 | double f = Math.pow(10., p); |
1231 | double g = d * f; |
1232 | result = ValueDouble.get(((d < 0) ? Math.ceil(g) : Math.floor(g)) / f); |
1233 | } |
1234 | break; |
1235 | } |
1236 | case HASH: |
1237 | result = ValueBytes.getNoCopy(getHash(v0.getString(), |
1238 | v1.getBytesNoCopy(), v2.getInt())); |
1239 | break; |
1240 | case ENCRYPT: |
1241 | result = ValueBytes.getNoCopy(encrypt(v0.getString(), |
1242 | v1.getBytesNoCopy(), v2.getBytesNoCopy())); |
1243 | break; |
1244 | case DECRYPT: |
1245 | result = ValueBytes.getNoCopy(decrypt(v0.getString(), |
1246 | v1.getBytesNoCopy(), v2.getBytesNoCopy())); |
1247 | break; |
1248 | case COMPRESS: { |
1249 | String algorithm = null; |
1250 | if (v1 != null) { |
1251 | algorithm = v1.getString(); |
1252 | } |
1253 | result = ValueBytes.getNoCopy(CompressTool.getInstance(). |
1254 | compress(v0.getBytesNoCopy(), algorithm)); |
1255 | break; |
1256 | } |
1257 | case DIFFERENCE: |
1258 | result = ValueInt.get(getDifference( |
1259 | v0.getString(), v1.getString())); |
1260 | break; |
1261 | case INSERT: { |
1262 | if (v1 == ValueNull.INSTANCE || v2 == ValueNull.INSTANCE) { |
1263 | result = v1; |
1264 | } else { |
1265 | result = ValueString.get(insert(v0.getString(), |
1266 | v1.getInt(), v2.getInt(), v3.getString()), |
1267 | database.getMode().treatEmptyStringsAsNull); |
1268 | } |
1269 | break; |
1270 | } |
1271 | case LEFT: |
1272 | result = ValueString.get(left(v0.getString(), v1.getInt()), |
1273 | database.getMode().treatEmptyStringsAsNull); |
1274 | break; |
1275 | case LOCATE: { |
1276 | int start = v2 == null ? 0 : v2.getInt(); |
1277 | result = ValueInt.get(locate(v0.getString(), v1.getString(), start)); |
1278 | break; |
1279 | } |
1280 | case INSTR: { |
1281 | int start = v2 == null ? 0 : v2.getInt(); |
1282 | result = ValueInt.get(locate(v1.getString(), v0.getString(), start)); |
1283 | break; |
1284 | } |
1285 | case REPEAT: { |
1286 | int count = Math.max(0, v1.getInt()); |
1287 | result = ValueString.get(repeat(v0.getString(), count), |
1288 | database.getMode().treatEmptyStringsAsNull); |
1289 | break; |
1290 | } |
1291 | case REPLACE: { |
1292 | String s0 = v0.getString(); |
1293 | String s1 = v1.getString(); |
1294 | String s2 = (v2 == null) ? "" : v2.getString(); |
1295 | result = ValueString.get(replace(s0, s1, s2), |
1296 | database.getMode().treatEmptyStringsAsNull); |
1297 | break; |
1298 | } |
1299 | case RIGHT: |
1300 | result = ValueString.get(right(v0.getString(), v1.getInt()), |
1301 | database.getMode().treatEmptyStringsAsNull); |
1302 | break; |
1303 | case LTRIM: |
1304 | result = ValueString.get(StringUtils.trim(v0.getString(), |
1305 | true, false, v1 == null ? " " : v1.getString()), |
1306 | database.getMode().treatEmptyStringsAsNull); |
1307 | break; |
1308 | case TRIM: |
1309 | result = ValueString.get(StringUtils.trim(v0.getString(), |
1310 | true, true, v1 == null ? " " : v1.getString()), |
1311 | database.getMode().treatEmptyStringsAsNull); |
1312 | break; |
1313 | case RTRIM: |
1314 | result = ValueString.get(StringUtils.trim(v0.getString(), |
1315 | false, true, v1 == null ? " " : v1.getString()), |
1316 | database.getMode().treatEmptyStringsAsNull); |
1317 | break; |
1318 | case SUBSTR: |
1319 | case SUBSTRING: { |
1320 | String s = v0.getString(); |
1321 | int offset = v1.getInt(); |
1322 | if (offset < 0) { |
1323 | offset = s.length() + offset + 1; |
1324 | } |
1325 | int length = v2 == null ? s.length() : v2.getInt(); |
1326 | result = ValueString.get(substring(s, offset, length), |
1327 | database.getMode().treatEmptyStringsAsNull); |
1328 | break; |
1329 | } |
1330 | case POSITION: |
1331 | result = ValueInt.get(locate(v0.getString(), v1.getString(), 0)); |
1332 | break; |
1333 | case XMLATTR: |
1334 | result = ValueString.get( |
1335 | StringUtils.xmlAttr(v0.getString(), v1.getString()), |
1336 | database.getMode().treatEmptyStringsAsNull); |
1337 | break; |
1338 | case XMLNODE: { |
1339 | String attr = v1 == null ? |
1340 | null : v1 == ValueNull.INSTANCE ? null : v1.getString(); |
1341 | String content = v2 == null ? |
1342 | null : v2 == ValueNull.INSTANCE ? null : v2.getString(); |
1343 | boolean indent = v3 == null ? |
1344 | true : v3.getBoolean(); |
1345 | result = ValueString.get(StringUtils.xmlNode( |
1346 | v0.getString(), attr, content, indent), |
1347 | database.getMode().treatEmptyStringsAsNull); |
1348 | break; |
1349 | } |
1350 | case REGEXP_REPLACE: { |
1351 | String regexp = v1.getString(); |
1352 | String replacement = v2.getString(); |
1353 | try { |
1354 | result = ValueString.get( |
1355 | v0.getString().replaceAll(regexp, replacement), |
1356 | database.getMode().treatEmptyStringsAsNull); |
1357 | } catch (StringIndexOutOfBoundsException e) { |
1358 | throw DbException.get( |
1359 | ErrorCode.LIKE_ESCAPE_ERROR_1, e, replacement); |
1360 | } catch (PatternSyntaxException e) { |
1361 | throw DbException.get( |
1362 | ErrorCode.LIKE_ESCAPE_ERROR_1, e, regexp); |
1363 | } |
1364 | break; |
1365 | } |
1366 | case RPAD: |
1367 | result = ValueString.get(StringUtils.pad(v0.getString(), |
1368 | v1.getInt(), v2 == null ? null : v2.getString(), true), |
1369 | database.getMode().treatEmptyStringsAsNull); |
1370 | break; |
1371 | case LPAD: |
1372 | result = ValueString.get(StringUtils.pad(v0.getString(), |
1373 | v1.getInt(), v2 == null ? null : v2.getString(), false), |
1374 | database.getMode().treatEmptyStringsAsNull); |
1375 | break; |
1376 | case TO_CHAR: |
1377 | switch(v0.getType()){ |
1378 | case Value.TIME: |
1379 | case Value.DATE: |
1380 | case Value.TIMESTAMP: |
1381 | result = ValueString.get(toChar(v0.getTimestamp(), |
1382 | v1 == null ? null : v1.getString(), |
1383 | v2 == null ? null : v2.getString()), |
1384 | database.getMode().treatEmptyStringsAsNull); |
1385 | break; |
1386 | case Value.SHORT: |
1387 | case Value.INT: |
1388 | case Value.LONG: |
1389 | case Value.DECIMAL: |
1390 | case Value.DOUBLE: |
1391 | case Value.FLOAT: |
1392 | result = ValueString.get(toChar(v0.getBigDecimal(), |
1393 | v1 == null ? null : v1.getString(), |
1394 | v2 == null ? null : v2.getString()), |
1395 | database.getMode().treatEmptyStringsAsNull); |
1396 | break; |
1397 | default: |
1398 | result = ValueString.get(v0.getString(), |
1399 | database.getMode().treatEmptyStringsAsNull); |
1400 | } |
1401 | break; |
1402 | case TRANSLATE: { |
1403 | String matching = v1.getString(); |
1404 | String replacement = v2.getString(); |
1405 | result = ValueString.get( |
1406 | translate(v0.getString(), matching, replacement), |
1407 | database.getMode().treatEmptyStringsAsNull); |
1408 | break; |
1409 | } |
1410 | case H2VERSION: |
1411 | result = ValueString.get(Constants.getVersion(), |
1412 | database.getMode().treatEmptyStringsAsNull); |
1413 | break; |
1414 | case DATE_ADD: |
1415 | result = ValueTimestamp.get(dateadd( |
1416 | v0.getString(), v1.getInt(), v2.getTimestamp())); |
1417 | break; |
1418 | case DATE_DIFF: |
1419 | result = ValueLong.get(datediff( |
1420 | v0.getString(), v1.getTimestamp(), v2.getTimestamp())); |
1421 | break; |
1422 | case EXTRACT: { |
1423 | int field = getDatePart(v0.getString()); |
1424 | result = ValueInt.get(DateTimeUtils.getDatePart( |
1425 | v1.getTimestamp(), field)); |
1426 | break; |
1427 | } |
1428 | case FORMATDATETIME: { |
1429 | if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) { |
1430 | result = ValueNull.INSTANCE; |
1431 | } else { |
1432 | String locale = v2 == null ? |
1433 | null : v2 == ValueNull.INSTANCE ? null : v2.getString(); |
1434 | String tz = v3 == null ? |
1435 | null : v3 == ValueNull.INSTANCE ? null : v3.getString(); |
1436 | result = ValueString.get(DateTimeUtils.formatDateTime( |
1437 | v0.getTimestamp(), v1.getString(), locale, tz), |
1438 | database.getMode().treatEmptyStringsAsNull); |
1439 | } |
1440 | break; |
1441 | } |
1442 | case PARSEDATETIME: { |
1443 | if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) { |
1444 | result = ValueNull.INSTANCE; |
1445 | } else { |
1446 | String locale = v2 == null ? |
1447 | null : v2 == ValueNull.INSTANCE ? null : v2.getString(); |
1448 | String tz = v3 == null ? |
1449 | null : v3 == ValueNull.INSTANCE ? null : v3.getString(); |
1450 | java.util.Date d = DateTimeUtils.parseDateTime( |
1451 | v0.getString(), v1.getString(), locale, tz); |
1452 | result = ValueTimestamp.fromMillis(d.getTime()); |
1453 | } |
1454 | break; |
1455 | } |
1456 | case NULLIF: |
1457 | result = database.areEqual(v0, v1) ? ValueNull.INSTANCE : v0; |
1458 | break; |
1459 | // system |
1460 | case NEXTVAL: { |
1461 | Sequence sequence = getSequence(session, v0, v1); |
1462 | SequenceValue value = new SequenceValue(sequence); |
1463 | result = value.getValue(session); |
1464 | break; |
1465 | } |
1466 | case CURRVAL: { |
1467 | Sequence sequence = getSequence(session, v0, v1); |
1468 | result = ValueLong.get(sequence.getCurrentValue()); |
1469 | break; |
1470 | } |
1471 | case CSVREAD: { |
1472 | String fileName = v0.getString(); |
1473 | String columnList = v1 == null ? null : v1.getString(); |
1474 | Csv csv = new Csv(); |
1475 | String options = v2 == null ? null : v2.getString(); |
1476 | String charset = null; |
1477 | if (options != null && options.indexOf('=') >= 0) { |
1478 | charset = csv.setOptions(options); |
1479 | } else { |
1480 | charset = options; |
1481 | String fieldSeparatorRead = v3 == null ? null : v3.getString(); |
1482 | String fieldDelimiter = v4 == null ? null : v4.getString(); |
1483 | String escapeCharacter = v5 == null ? null : v5.getString(); |
1484 | Value v6 = getNullOrValue(session, args, values, 6); |
1485 | String nullString = v6 == null ? null : v6.getString(); |
1486 | setCsvDelimiterEscape(csv, fieldSeparatorRead, fieldDelimiter, |
1487 | escapeCharacter); |
1488 | csv.setNullString(nullString); |
1489 | } |
1490 | char fieldSeparator = csv.getFieldSeparatorRead(); |
1491 | String[] columns = StringUtils.arraySplit(columnList, |
1492 | fieldSeparator, true); |
1493 | try { |
1494 | ValueResultSet vr = ValueResultSet.get(csv.read(fileName, |
1495 | columns, charset)); |
1496 | result = vr; |
1497 | } catch (SQLException e) { |
1498 | throw DbException.convert(e); |
1499 | } |
1500 | break; |
1501 | } |
1502 | case LINK_SCHEMA: { |
1503 | session.getUser().checkAdmin(); |
1504 | Connection conn = session.createConnection(false); |
1505 | ResultSet rs = LinkSchema.linkSchema(conn, v0.getString(), |
1506 | v1.getString(), v2.getString(), v3.getString(), |
1507 | v4.getString(), v5.getString()); |
1508 | result = ValueResultSet.get(rs); |
1509 | break; |
1510 | } |
1511 | case CSVWRITE: { |
1512 | session.getUser().checkAdmin(); |
1513 | Connection conn = session.createConnection(false); |
1514 | Csv csv = new Csv(); |
1515 | String options = v2 == null ? null : v2.getString(); |
1516 | String charset = null; |
1517 | if (options != null && options.indexOf('=') >= 0) { |
1518 | charset = csv.setOptions(options); |
1519 | } else { |
1520 | charset = options; |
1521 | String fieldSeparatorWrite = v3 == null ? null : v3.getString(); |
1522 | String fieldDelimiter = v4 == null ? null : v4.getString(); |
1523 | String escapeCharacter = v5 == null ? null : v5.getString(); |
1524 | Value v6 = getNullOrValue(session, args, values, 6); |
1525 | String nullString = v6 == null ? null : v6.getString(); |
1526 | Value v7 = getNullOrValue(session, args, values, 7); |
1527 | String lineSeparator = v7 == null ? null : v7.getString(); |
1528 | setCsvDelimiterEscape(csv, fieldSeparatorWrite, fieldDelimiter, |
1529 | escapeCharacter); |
1530 | csv.setNullString(nullString); |
1531 | if (lineSeparator != null) { |
1532 | csv.setLineSeparator(lineSeparator); |
1533 | } |
1534 | } |
1535 | try { |
1536 | int rows = csv.write(conn, v0.getString(), v1.getString(), |
1537 | charset); |
1538 | result = ValueInt.get(rows); |
1539 | } catch (SQLException e) { |
1540 | throw DbException.convert(e); |
1541 | } |
1542 | break; |
1543 | } |
1544 | case SET: { |
1545 | Variable var = (Variable) args[0]; |
1546 | session.setVariable(var.getName(), v1); |
1547 | result = v1; |
1548 | break; |
1549 | } |
1550 | case FILE_READ: { |
1551 | session.getUser().checkAdmin(); |
1552 | String fileName = v0.getString(); |
1553 | boolean blob = args.length == 1; |
1554 | try { |
1555 | InputStream in = new AutoCloseInputStream( |
1556 | FileUtils.newInputStream(fileName)); |
1557 | if (blob) { |
1558 | result = database.getLobStorage().createBlob(in, -1); |
1559 | } else { |
1560 | Reader reader; |
1561 | if (v1 == ValueNull.INSTANCE) { |
1562 | reader = new InputStreamReader(in); |
1563 | } else { |
1564 | reader = new InputStreamReader(in, v1.getString()); |
1565 | } |
1566 | result = database.getLobStorage().createClob(reader, -1); |
1567 | } |
1568 | } catch (IOException e) { |
1569 | throw DbException.convertIOException(e, fileName); |
1570 | } |
1571 | break; |
1572 | } |
1573 | case TRUNCATE_VALUE: { |
1574 | result = v0.convertPrecision(v1.getLong(), v2.getBoolean()); |
1575 | break; |
1576 | } |
1577 | case XMLTEXT: |
1578 | if (v1 == null) { |
1579 | result = ValueString.get(StringUtils.xmlText( |
1580 | v0.getString()), |
1581 | database.getMode().treatEmptyStringsAsNull); |
1582 | } else { |
1583 | result = ValueString.get(StringUtils.xmlText( |
1584 | v0.getString(), v1.getBoolean()), |
1585 | database.getMode().treatEmptyStringsAsNull); |
1586 | } |
1587 | break; |
1588 | case VALUES: |
1589 | result = session.getVariable(args[0].getSchemaName() + "." + |
1590 | args[0].getTableName() + "." + args[0].getColumnName()); |
1591 | break; |
1592 | default: |
1593 | throw DbException.throwInternalError("type=" + info.type); |
1594 | } |
1595 | return result; |
1596 | } |
1597 | |
1598 | private Sequence getSequence(Session session, Value v0, Value v1) { |
1599 | String schemaName, sequenceName; |
1600 | if (v1 == null) { |
1601 | Parser p = new Parser(session); |
1602 | String sql = v0.getString(); |
1603 | Expression expr = p.parseExpression(sql); |
1604 | if (expr instanceof ExpressionColumn) { |
1605 | ExpressionColumn seq = (ExpressionColumn) expr; |
1606 | schemaName = seq.getOriginalTableAliasName(); |
1607 | if (schemaName == null) { |
1608 | schemaName = session.getCurrentSchemaName(); |
1609 | sequenceName = sql; |
1610 | } else { |
1611 | sequenceName = seq.getColumnName(); |
1612 | } |
1613 | } else { |
1614 | throw DbException.getSyntaxError(sql, 1); |
1615 | } |
1616 | } else { |
1617 | schemaName = v0.getString(); |
1618 | sequenceName = v1.getString(); |
1619 | } |
1620 | Schema s = database.findSchema(schemaName); |
1621 | if (s == null) { |
1622 | schemaName = StringUtils.toUpperEnglish(schemaName); |
1623 | s = database.getSchema(schemaName); |
1624 | } |
1625 | Sequence seq = s.findSequence(sequenceName); |
1626 | if (seq == null) { |
1627 | sequenceName = StringUtils.toUpperEnglish(sequenceName); |
1628 | seq = s.getSequence(sequenceName); |
1629 | } |
1630 | return seq; |
1631 | } |
1632 | |
1633 | private static long length(Value v) { |
1634 | switch (v.getType()) { |
1635 | case Value.BLOB: |
1636 | case Value.CLOB: |
1637 | case Value.BYTES: |
1638 | case Value.JAVA_OBJECT: |
1639 | return v.getPrecision(); |
1640 | default: |
1641 | return v.getString().length(); |
1642 | } |
1643 | } |
1644 | |
1645 | private static byte[] getPaddedArrayCopy(byte[] data, int blockSize) { |
1646 | int size = MathUtils.roundUpInt(data.length, blockSize); |
1647 | byte[] newData = DataUtils.newBytes(size); |
1648 | System.arraycopy(data, 0, newData, 0, data.length); |
1649 | return newData; |
1650 | } |
1651 | |
1652 | private static byte[] decrypt(String algorithm, byte[] key, byte[] data) { |
1653 | BlockCipher cipher = CipherFactory.getBlockCipher(algorithm); |
1654 | byte[] newKey = getPaddedArrayCopy(key, cipher.getKeyLength()); |
1655 | cipher.setKey(newKey); |
1656 | byte[] newData = getPaddedArrayCopy(data, BlockCipher.ALIGN); |
1657 | cipher.decrypt(newData, 0, newData.length); |
1658 | return newData; |
1659 | } |
1660 | |
1661 | private static byte[] encrypt(String algorithm, byte[] key, byte[] data) { |
1662 | BlockCipher cipher = CipherFactory.getBlockCipher(algorithm); |
1663 | byte[] newKey = getPaddedArrayCopy(key, cipher.getKeyLength()); |
1664 | cipher.setKey(newKey); |
1665 | byte[] newData = getPaddedArrayCopy(data, BlockCipher.ALIGN); |
1666 | cipher.encrypt(newData, 0, newData.length); |
1667 | return newData; |
1668 | } |
1669 | |
1670 | private static byte[] getHash(String algorithm, byte[] bytes, int iterations) { |
1671 | if (!"SHA256".equalsIgnoreCase(algorithm)) { |
1672 | throw DbException.getInvalidValueException("algorithm", algorithm); |
1673 | } |
1674 | for (int i = 0; i < iterations; i++) { |
1675 | bytes = SHA256.getHash(bytes, false); |
1676 | } |
1677 | return bytes; |
1678 | } |
1679 | |
1680 | /** |
1681 | * Check if a given string is a valid date part string. |
1682 | * |
1683 | * @param part the string |
1684 | * @return true if it is |
1685 | */ |
1686 | public static boolean isDatePart(String part) { |
1687 | Integer p = DATE_PART.get(StringUtils.toUpperEnglish(part)); |
1688 | return p != null; |
1689 | } |
1690 | |
1691 | private static int getDatePart(String part) { |
1692 | Integer p = DATE_PART.get(StringUtils.toUpperEnglish(part)); |
1693 | if (p == null) { |
1694 | throw DbException.getInvalidValueException("date part", part); |
1695 | } |
1696 | return p.intValue(); |
1697 | } |
1698 | |
1699 | private static Timestamp dateadd(String part, int count, Timestamp d) { |
1700 | int field = getDatePart(part); |
1701 | Calendar calendar = Calendar.getInstance(); |
1702 | int nanos = d.getNanos() % 1000000; |
1703 | calendar.setTime(d); |
1704 | calendar.add(field, count); |
1705 | long t = calendar.getTime().getTime(); |
1706 | Timestamp ts = new Timestamp(t); |
1707 | ts.setNanos(ts.getNanos() + nanos); |
1708 | return ts; |
1709 | } |
1710 | |
1711 | /** |
1712 | * Calculate the number of crossed unit boundaries between two timestamps. |
1713 | * This method is supported for MS SQL Server compatibility. |
1714 | * <pre> |
1715 | * DATEDIFF(YEAR, '2004-12-31', '2005-01-01') = 1 |
1716 | * </pre> |
1717 | * |
1718 | * @param part the part |
1719 | * @param d1 the first date |
1720 | * @param d2 the second date |
1721 | * @return the number of crossed boundaries |
1722 | */ |
1723 | private static long datediff(String part, Timestamp d1, Timestamp d2) { |
1724 | int field = getDatePart(part); |
1725 | Calendar calendar = Calendar.getInstance(); |
1726 | long t1 = d1.getTime(), t2 = d2.getTime(); |
1727 | // need to convert to UTC, otherwise we get inconsistent results with |
1728 | // certain time zones (those that are 30 minutes off) |
1729 | TimeZone zone = calendar.getTimeZone(); |
1730 | calendar.setTime(d1); |
1731 | t1 += zone.getOffset(calendar.get(Calendar.ERA), |
1732 | calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), |
1733 | calendar.get(Calendar.DAY_OF_MONTH), |
1734 | calendar.get(Calendar.DAY_OF_WEEK), |
1735 | calendar.get(Calendar.MILLISECOND)); |
1736 | calendar.setTime(d2); |
1737 | t2 += zone.getOffset(calendar.get(Calendar.ERA), |
1738 | calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), |
1739 | calendar.get(Calendar.DAY_OF_MONTH), |
1740 | calendar.get(Calendar.DAY_OF_WEEK), |
1741 | calendar.get(Calendar.MILLISECOND)); |
1742 | switch (field) { |
1743 | case Calendar.MILLISECOND: |
1744 | return t2 - t1; |
1745 | case Calendar.SECOND: |
1746 | case Calendar.MINUTE: |
1747 | case Calendar.HOUR_OF_DAY: { |
1748 | // first 'normalize' the numbers so both are not negative |
1749 | long hour = 60 * 60 * 1000; |
1750 | long add = Math.min(t1 / hour * hour, t2 / hour * hour); |
1751 | t1 -= add; |
1752 | t2 -= add; |
1753 | switch (field) { |
1754 | case Calendar.SECOND: |
1755 | return t2 / 1000 - t1 / 1000; |
1756 | case Calendar.MINUTE: |
1757 | return t2 / (60 * 1000) - t1 / (60 * 1000); |
1758 | case Calendar.HOUR_OF_DAY: |
1759 | return t2 / hour - t1 / hour; |
1760 | default: |
1761 | throw DbException.throwInternalError("field:" + field); |
1762 | } |
1763 | } |
1764 | case Calendar.DATE: |
1765 | return t2 / (24 * 60 * 60 * 1000) - t1 / (24 * 60 * 60 * 1000); |
1766 | default: |
1767 | break; |
1768 | } |
1769 | calendar.setTimeInMillis(t1); |
1770 | int year1 = calendar.get(Calendar.YEAR); |
1771 | int month1 = calendar.get(Calendar.MONTH); |
1772 | calendar.setTimeInMillis(t2); |
1773 | int year2 = calendar.get(Calendar.YEAR); |
1774 | int month2 = calendar.get(Calendar.MONTH); |
1775 | int result = year2 - year1; |
1776 | if (field == Calendar.MONTH) { |
1777 | result = 12 * result + (month2 - month1); |
1778 | } |
1779 | return result; |
1780 | } |
1781 | |
1782 | private static String substring(String s, int start, int length) { |
1783 | int len = s.length(); |
1784 | start--; |
1785 | if (start < 0) { |
1786 | start = 0; |
1787 | } |
1788 | if (length < 0) { |
1789 | length = 0; |
1790 | } |
1791 | start = (start > len) ? len : start; |
1792 | if (start + length > len) { |
1793 | length = len - start; |
1794 | } |
1795 | return s.substring(start, start + length); |
1796 | } |
1797 | |
1798 | private static String replace(String s, String replace, String with) { |
1799 | if (s == null || replace == null || with == null) { |
1800 | return null; |
1801 | } |
1802 | if (replace.length() == 0) { |
1803 | // avoid out of memory |
1804 | return s; |
1805 | } |
1806 | StringBuilder buff = new StringBuilder(s.length()); |
1807 | int start = 0; |
1808 | int len = replace.length(); |
1809 | while (true) { |
1810 | int i = s.indexOf(replace, start); |
1811 | if (i == -1) { |
1812 | break; |
1813 | } |
1814 | buff.append(s.substring(start, i)).append(with); |
1815 | start = i + len; |
1816 | } |
1817 | buff.append(s.substring(start)); |
1818 | return buff.toString(); |
1819 | } |
1820 | |
1821 | private static String repeat(String s, int count) { |
1822 | StringBuilder buff = new StringBuilder(s.length() * count); |
1823 | while (count-- > 0) { |
1824 | buff.append(s); |
1825 | } |
1826 | return buff.toString(); |
1827 | } |
1828 | |
1829 | private static String rawToHex(String s) { |
1830 | int length = s.length(); |
1831 | StringBuilder buff = new StringBuilder(4 * length); |
1832 | for (int i = 0; i < length; i++) { |
1833 | String hex = Integer.toHexString(s.charAt(i) & 0xffff); |
1834 | for (int j = hex.length(); j < 4; j++) { |
1835 | buff.append('0'); |
1836 | } |
1837 | buff.append(hex); |
1838 | } |
1839 | return buff.toString(); |
1840 | } |
1841 | |
1842 | private static int locate(String search, String s, int start) { |
1843 | if (start < 0) { |
1844 | int i = s.length() + start; |
1845 | return s.lastIndexOf(search, i) + 1; |
1846 | } |
1847 | int i = (start == 0) ? 0 : start - 1; |
1848 | return s.indexOf(search, i) + 1; |
1849 | } |
1850 | |
1851 | private static String right(String s, int count) { |
1852 | if (count < 0) { |
1853 | count = 0; |
1854 | } else if (count > s.length()) { |
1855 | count = s.length(); |
1856 | } |
1857 | return s.substring(s.length() - count); |
1858 | } |
1859 | |
1860 | private static String left(String s, int count) { |
1861 | if (count < 0) { |
1862 | count = 0; |
1863 | } else if (count > s.length()) { |
1864 | count = s.length(); |
1865 | } |
1866 | return s.substring(0, count); |
1867 | } |
1868 | |
1869 | private static String insert(String s1, int start, int length, String s2) { |
1870 | if (s1 == null) { |
1871 | return s2; |
1872 | } |
1873 | if (s2 == null) { |
1874 | return s1; |
1875 | } |
1876 | int len1 = s1.length(); |
1877 | int len2 = s2.length(); |
1878 | start--; |
1879 | if (start < 0 || length <= 0 || len2 == 0 || start > len1) { |
1880 | return s1; |
1881 | } |
1882 | if (start + length > len1) { |
1883 | length = len1 - start; |
1884 | } |
1885 | return s1.substring(0, start) + s2 + s1.substring(start + length); |
1886 | } |
1887 | |
1888 | private static String hexToRaw(String s) { |
1889 | // TODO function hextoraw compatibility with oracle |
1890 | int len = s.length(); |
1891 | if (len % 4 != 0) { |
1892 | throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s); |
1893 | } |
1894 | StringBuilder buff = new StringBuilder(len / 4); |
1895 | for (int i = 0; i < len; i += 4) { |
1896 | try { |
1897 | char raw = (char) Integer.parseInt(s.substring(i, i + 4), 16); |
1898 | buff.append(raw); |
1899 | } catch (NumberFormatException e) { |
1900 | throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s); |
1901 | } |
1902 | } |
1903 | return buff.toString(); |
1904 | } |
1905 | |
1906 | private static int getDifference(String s1, String s2) { |
1907 | // TODO function difference: compatibility with SQL Server and HSQLDB |
1908 | s1 = getSoundex(s1); |
1909 | s2 = getSoundex(s2); |
1910 | int e = 0; |
1911 | for (int i = 0; i < 4; i++) { |
1912 | if (s1.charAt(i) == s2.charAt(i)) { |
1913 | e++; |
1914 | } |
1915 | } |
1916 | return e; |
1917 | } |
1918 | |
1919 | private static String translate(String original, String findChars, |
1920 | String replaceChars) { |
1921 | if (StringUtils.isNullOrEmpty(original) || |
1922 | StringUtils.isNullOrEmpty(findChars)) { |
1923 | return original; |
1924 | } |
1925 | // if it stays null, then no replacements have been made |
1926 | StringBuilder buff = null; |
1927 | // if shorter than findChars, then characters are removed |
1928 | // (if null, we don't access replaceChars at all) |
1929 | int replaceSize = replaceChars == null ? 0 : replaceChars.length(); |
1930 | for (int i = 0, size = original.length(); i < size; i++) { |
1931 | char ch = original.charAt(i); |
1932 | int index = findChars.indexOf(ch); |
1933 | if (index >= 0) { |
1934 | if (buff == null) { |
1935 | buff = new StringBuilder(size); |
1936 | if (i > 0) { |
1937 | buff.append(original.substring(0, i)); |
1938 | } |
1939 | } |
1940 | if (index < replaceSize) { |
1941 | ch = replaceChars.charAt(index); |
1942 | } |
1943 | } |
1944 | if (buff != null) { |
1945 | buff.append(ch); |
1946 | } |
1947 | } |
1948 | return buff == null ? original : buff.toString(); |
1949 | } |
1950 | |
1951 | private static double roundMagic(double d) { |
1952 | if ((d < 0.0000000000001) && (d > -0.0000000000001)) { |
1953 | return 0.0; |
1954 | } |
1955 | if ((d > 1000000000000.) || (d < -1000000000000.)) { |
1956 | return d; |
1957 | } |
1958 | StringBuilder s = new StringBuilder(); |
1959 | s.append(d); |
1960 | if (s.toString().indexOf('E') >= 0) { |
1961 | return d; |
1962 | } |
1963 | int len = s.length(); |
1964 | if (len < 16) { |
1965 | return d; |
1966 | } |
1967 | if (s.toString().indexOf('.') > len - 3) { |
1968 | return d; |
1969 | } |
1970 | s.delete(len - 2, len); |
1971 | len -= 2; |
1972 | char c1 = s.charAt(len - 2); |
1973 | char c2 = s.charAt(len - 3); |
1974 | char c3 = s.charAt(len - 4); |
1975 | if ((c1 == '0') && (c2 == '0') && (c3 == '0')) { |
1976 | s.setCharAt(len - 1, '0'); |
1977 | } else if ((c1 == '9') && (c2 == '9') && (c3 == '9')) { |
1978 | s.setCharAt(len - 1, '9'); |
1979 | s.append('9'); |
1980 | s.append('9'); |
1981 | s.append('9'); |
1982 | } |
1983 | return Double.parseDouble(s.toString()); |
1984 | } |
1985 | |
1986 | private static String getSoundex(String s) { |
1987 | int len = s.length(); |
1988 | char[] chars = { '0', '0', '0', '0' }; |
1989 | char lastDigit = '0'; |
1990 | for (int i = 0, j = 0; i < len && j < 4; i++) { |
1991 | char c = s.charAt(i); |
1992 | char newDigit = c > SOUNDEX_INDEX.length ? |
1993 | 0 : SOUNDEX_INDEX[c]; |
1994 | if (newDigit != 0) { |
1995 | if (j == 0) { |
1996 | chars[j++] = c; |
1997 | lastDigit = newDigit; |
1998 | } else if (newDigit <= '6') { |
1999 | if (newDigit != lastDigit) { |
2000 | chars[j++] = newDigit; |
2001 | lastDigit = newDigit; |
2002 | } |
2003 | } else if (newDigit == '7') { |
2004 | lastDigit = newDigit; |
2005 | } |
2006 | } |
2007 | } |
2008 | return new String(chars); |
2009 | } |
2010 | |
2011 | @Override |
2012 | public int getType() { |
2013 | return dataType; |
2014 | } |
2015 | |
2016 | @Override |
2017 | public void mapColumns(ColumnResolver resolver, int level) { |
2018 | for (Expression e : args) { |
2019 | if (e != null) { |
2020 | e.mapColumns(resolver, level); |
2021 | } |
2022 | } |
2023 | } |
2024 | |
2025 | /** |
2026 | * Check if the parameter count is correct. |
2027 | * |
2028 | * @param len the number of parameters set |
2029 | * @throws DbException if the parameter count is incorrect |
2030 | */ |
2031 | protected void checkParameterCount(int len) { |
2032 | int min = 0, max = Integer.MAX_VALUE; |
2033 | switch (info.type) { |
2034 | case COALESCE: |
2035 | case CSVREAD: |
2036 | case LEAST: |
2037 | case GREATEST: |
2038 | min = 1; |
2039 | break; |
2040 | case NOW: |
2041 | case CURRENT_TIMESTAMP: |
2042 | case RAND: |
2043 | max = 1; |
2044 | break; |
2045 | case COMPRESS: |
2046 | case LTRIM: |
2047 | case RTRIM: |
2048 | case TRIM: |
2049 | case FILE_READ: |
2050 | case ROUND: |
2051 | case XMLTEXT: |
2052 | case TRUNCATE: |
2053 | min = 1; |
2054 | max = 2; |
2055 | break; |
2056 | case TO_CHAR: |
2057 | min = 1; |
2058 | max = 3; |
2059 | break; |
2060 | case REPLACE: |
2061 | case LOCATE: |
2062 | case INSTR: |
2063 | case SUBSTR: |
2064 | case SUBSTRING: |
2065 | case LPAD: |
2066 | case RPAD: |
2067 | min = 2; |
2068 | max = 3; |
2069 | break; |
2070 | case CONCAT: |
2071 | case CONCAT_WS: |
2072 | case CSVWRITE: |
2073 | min = 2; |
2074 | break; |
2075 | case XMLNODE: |
2076 | min = 1; |
2077 | max = 4; |
2078 | break; |
2079 | case FORMATDATETIME: |
2080 | case PARSEDATETIME: |
2081 | min = 2; |
2082 | max = 4; |
2083 | break; |
2084 | case CURRVAL: |
2085 | case NEXTVAL: |
2086 | min = 1; |
2087 | max = 2; |
2088 | break; |
2089 | case DECODE: |
2090 | case CASE: |
2091 | min = 3; |
2092 | break; |
2093 | default: |
2094 | DbException.throwInternalError("type=" + info.type); |
2095 | } |
2096 | boolean ok = (len >= min) && (len <= max); |
2097 | if (!ok) { |
2098 | throw DbException.get( |
2099 | ErrorCode.INVALID_PARAMETER_COUNT_2, |
2100 | info.name, min + ".." + max); |
2101 | } |
2102 | } |
2103 | |
2104 | /** |
2105 | * This method is called after all the parameters have been set. |
2106 | * It checks if the parameter count is correct. |
2107 | * |
2108 | * @throws DbException if the parameter count is incorrect. |
2109 | */ |
2110 | public void doneWithParameters() { |
2111 | if (info.parameterCount == VAR_ARGS) { |
2112 | int len = varArgs.size(); |
2113 | checkParameterCount(len); |
2114 | args = new Expression[len]; |
2115 | varArgs.toArray(args); |
2116 | varArgs = null; |
2117 | } else { |
2118 | int len = args.length; |
2119 | if (len > 0 && args[len - 1] == null) { |
2120 | throw DbException.get( |
2121 | ErrorCode.INVALID_PARAMETER_COUNT_2, |
2122 | info.name, "" + len); |
2123 | } |
2124 | } |
2125 | } |
2126 | |
2127 | public void setDataType(Column col) { |
2128 | dataType = col.getType(); |
2129 | precision = col.getPrecision(); |
2130 | displaySize = col.getDisplaySize(); |
2131 | scale = col.getScale(); |
2132 | } |
2133 | |
2134 | @Override |
2135 | public Expression optimize(Session session) { |
2136 | boolean allConst = info.deterministic; |
2137 | for (int i = 0; i < args.length; i++) { |
2138 | Expression e = args[i]; |
2139 | if (e == null) { |
2140 | continue; |
2141 | } |
2142 | e = e.optimize(session); |
2143 | args[i] = e; |
2144 | if (!e.isConstant()) { |
2145 | allConst = false; |
2146 | } |
2147 | } |
2148 | int t, s, d; |
2149 | long p; |
2150 | Expression p0 = args.length < 1 ? null : args[0]; |
2151 | switch (info.type) { |
2152 | case IFNULL: |
2153 | case NULLIF: |
2154 | case COALESCE: |
2155 | case LEAST: |
2156 | case GREATEST: { |
2157 | t = Value.UNKNOWN; |
2158 | s = 0; |
2159 | p = 0; |
2160 | d = 0; |
2161 | for (Expression e : args) { |
2162 | if (e != ValueExpression.getNull()) { |
2163 | int type = e.getType(); |
2164 | if (type != Value.UNKNOWN && type != Value.NULL) { |
2165 | t = Value.getHigherOrder(t, type); |
2166 | s = Math.max(s, e.getScale()); |
2167 | p = Math.max(p, e.getPrecision()); |
2168 | d = Math.max(d, e.getDisplaySize()); |
2169 | } |
2170 | } |
2171 | } |
2172 | if (t == Value.UNKNOWN) { |
2173 | t = Value.STRING; |
2174 | s = 0; |
2175 | p = Integer.MAX_VALUE; |
2176 | d = Integer.MAX_VALUE; |
2177 | } |
2178 | break; |
2179 | } |
2180 | case CASE: |
2181 | case DECODE: { |
2182 | t = Value.UNKNOWN; |
2183 | s = 0; |
2184 | p = 0; |
2185 | d = 0; |
2186 | // (expr, when, then) |
2187 | // (expr, when, then, else) |
2188 | // (expr, when, then, when, then) |
2189 | // (expr, when, then, when, then, else) |
2190 | for (int i = 2, len = args.length; i < len; i += 2) { |
2191 | Expression then = args[i]; |
2192 | if (then != ValueExpression.getNull()) { |
2193 | int type = then.getType(); |
2194 | if (type != Value.UNKNOWN && type != Value.NULL) { |
2195 | t = Value.getHigherOrder(t, type); |
2196 | s = Math.max(s, then.getScale()); |
2197 | p = Math.max(p, then.getPrecision()); |
2198 | d = Math.max(d, then.getDisplaySize()); |
2199 | } |
2200 | } |
2201 | } |
2202 | if (args.length % 2 == 0) { |
2203 | Expression elsePart = args[args.length - 1]; |
2204 | if (elsePart != ValueExpression.getNull()) { |
2205 | int type = elsePart.getType(); |
2206 | if (type != Value.UNKNOWN && type != Value.NULL) { |
2207 | t = Value.getHigherOrder(t, type); |
2208 | s = Math.max(s, elsePart.getScale()); |
2209 | p = Math.max(p, elsePart.getPrecision()); |
2210 | d = Math.max(d, elsePart.getDisplaySize()); |
2211 | } |
2212 | } |
2213 | } |
2214 | if (t == Value.UNKNOWN) { |
2215 | t = Value.STRING; |
2216 | s = 0; |
2217 | p = Integer.MAX_VALUE; |
2218 | d = Integer.MAX_VALUE; |
2219 | } |
2220 | break; |
2221 | } |
2222 | case CASEWHEN: |
2223 | t = Value.getHigherOrder(args[1].getType(), args[2].getType()); |
2224 | p = Math.max(args[1].getPrecision(), args[2].getPrecision()); |
2225 | d = Math.max(args[1].getDisplaySize(), args[2].getDisplaySize()); |
2226 | s = Math.max(args[1].getScale(), args[2].getScale()); |
2227 | break; |
2228 | case NVL2: |
2229 | switch (args[1].getType()) { |
2230 | case Value.STRING: |
2231 | case Value.CLOB: |
2232 | case Value.STRING_FIXED: |
2233 | case Value.STRING_IGNORECASE: |
2234 | t = args[1].getType(); |
2235 | break; |
2236 | default: |
2237 | t = Value.getHigherOrder(args[1].getType(), args[2].getType()); |
2238 | break; |
2239 | } |
2240 | p = Math.max(args[1].getPrecision(), args[2].getPrecision()); |
2241 | d = Math.max(args[1].getDisplaySize(), args[2].getDisplaySize()); |
2242 | s = Math.max(args[1].getScale(), args[2].getScale()); |
2243 | break; |
2244 | case CAST: |
2245 | case CONVERT: |
2246 | case TRUNCATE_VALUE: |
2247 | // data type, precision and scale is already set |
2248 | t = dataType; |
2249 | p = precision; |
2250 | s = scale; |
2251 | d = displaySize; |
2252 | break; |
2253 | case TRUNCATE: |
2254 | t = p0.getType(); |
2255 | s = p0.getScale(); |
2256 | p = p0.getPrecision(); |
2257 | d = p0.getDisplaySize(); |
2258 | if (t == Value.NULL) { |
2259 | t = Value.INT; |
2260 | p = ValueInt.PRECISION; |
2261 | d = ValueInt.DISPLAY_SIZE; |
2262 | s = 0; |
2263 | } else if (t == Value.TIMESTAMP) { |
2264 | t = Value.DATE; |
2265 | p = ValueDate.PRECISION; |
2266 | s = 0; |
2267 | d = ValueDate.DISPLAY_SIZE; |
2268 | } |
2269 | break; |
2270 | case ABS: |
2271 | case FLOOR: |
2272 | case ROUND: |
2273 | t = p0.getType(); |
2274 | s = p0.getScale(); |
2275 | p = p0.getPrecision(); |
2276 | d = p0.getDisplaySize(); |
2277 | if (t == Value.NULL) { |
2278 | t = Value.INT; |
2279 | p = ValueInt.PRECISION; |
2280 | d = ValueInt.DISPLAY_SIZE; |
2281 | s = 0; |
2282 | } |
2283 | break; |
2284 | case SET: { |
2285 | Expression p1 = args[1]; |
2286 | t = p1.getType(); |
2287 | p = p1.getPrecision(); |
2288 | s = p1.getScale(); |
2289 | d = p1.getDisplaySize(); |
2290 | if (!(p0 instanceof Variable)) { |
2291 | throw DbException.get( |
2292 | ErrorCode.CAN_ONLY_ASSIGN_TO_VARIABLE_1, p0.getSQL()); |
2293 | } |
2294 | break; |
2295 | } |
2296 | case FILE_READ: { |
2297 | if (args.length == 1) { |
2298 | t = Value.BLOB; |
2299 | } else { |
2300 | t = Value.CLOB; |
2301 | } |
2302 | p = Integer.MAX_VALUE; |
2303 | s = 0; |
2304 | d = Integer.MAX_VALUE; |
2305 | break; |
2306 | } |
2307 | case SUBSTRING: |
2308 | case SUBSTR: { |
2309 | t = info.dataType; |
2310 | p = args[0].getPrecision(); |
2311 | s = 0; |
2312 | if (args[1].isConstant()) { |
2313 | // if only two arguments are used, |
2314 | // subtract offset from first argument length |
2315 | p -= args[1].getValue(session).getLong() - 1; |
2316 | } |
2317 | if (args.length == 3 && args[2].isConstant()) { |
2318 | // if the third argument is constant it is at most this value |
2319 | p = Math.min(p, args[2].getValue(session).getLong()); |
2320 | } |
2321 | p = Math.max(0, p); |
2322 | d = MathUtils.convertLongToInt(p); |
2323 | break; |
2324 | } |
2325 | default: |
2326 | t = info.dataType; |
2327 | DataType type = DataType.getDataType(t); |
2328 | p = PRECISION_UNKNOWN; |
2329 | d = 0; |
2330 | s = type.defaultScale; |
2331 | } |
2332 | dataType = t; |
2333 | precision = p; |
2334 | scale = s; |
2335 | displaySize = d; |
2336 | if (allConst) { |
2337 | Value v = getValue(session); |
2338 | if (v == ValueNull.INSTANCE) { |
2339 | if (info.type == CAST || info.type == CONVERT) { |
2340 | return this; |
2341 | } |
2342 | } |
2343 | return ValueExpression.get(v); |
2344 | } |
2345 | return this; |
2346 | } |
2347 | |
2348 | @Override |
2349 | public void setEvaluatable(TableFilter tableFilter, boolean b) { |
2350 | for (Expression e : args) { |
2351 | if (e != null) { |
2352 | e.setEvaluatable(tableFilter, b); |
2353 | } |
2354 | } |
2355 | } |
2356 | |
2357 | @Override |
2358 | public int getScale() { |
2359 | return scale; |
2360 | } |
2361 | |
2362 | @Override |
2363 | public long getPrecision() { |
2364 | if (precision == PRECISION_UNKNOWN) { |
2365 | calculatePrecisionAndDisplaySize(); |
2366 | } |
2367 | return precision; |
2368 | } |
2369 | |
2370 | @Override |
2371 | public int getDisplaySize() { |
2372 | if (precision == PRECISION_UNKNOWN) { |
2373 | calculatePrecisionAndDisplaySize(); |
2374 | } |
2375 | return displaySize; |
2376 | } |
2377 | |
2378 | private void calculatePrecisionAndDisplaySize() { |
2379 | switch (info.type) { |
2380 | case ENCRYPT: |
2381 | case DECRYPT: |
2382 | precision = args[2].getPrecision(); |
2383 | displaySize = args[2].getDisplaySize(); |
2384 | break; |
2385 | case COMPRESS: |
2386 | precision = args[0].getPrecision(); |
2387 | displaySize = args[0].getDisplaySize(); |
2388 | break; |
2389 | case CHAR: |
2390 | precision = 1; |
2391 | displaySize = 1; |
2392 | break; |
2393 | case CONCAT: |
2394 | precision = 0; |
2395 | displaySize = 0; |
2396 | for (Expression e : args) { |
2397 | precision += e.getPrecision(); |
2398 | displaySize = MathUtils.convertLongToInt( |
2399 | (long) displaySize + e.getDisplaySize()); |
2400 | if (precision < 0) { |
2401 | precision = Long.MAX_VALUE; |
2402 | } |
2403 | } |
2404 | break; |
2405 | case HEXTORAW: |
2406 | precision = (args[0].getPrecision() + 3) / 4; |
2407 | displaySize = MathUtils.convertLongToInt(precision); |
2408 | break; |
2409 | case LCASE: |
2410 | case LTRIM: |
2411 | case RIGHT: |
2412 | case RTRIM: |
2413 | case UCASE: |
2414 | case LOWER: |
2415 | case UPPER: |
2416 | case TRIM: |
2417 | case STRINGDECODE: |
2418 | case UTF8TOSTRING: |
2419 | case TRUNCATE: |
2420 | precision = args[0].getPrecision(); |
2421 | displaySize = args[0].getDisplaySize(); |
2422 | break; |
2423 | case RAWTOHEX: |
2424 | precision = args[0].getPrecision() * 4; |
2425 | displaySize = MathUtils.convertLongToInt(precision); |
2426 | break; |
2427 | case SOUNDEX: |
2428 | precision = 4; |
2429 | displaySize = (int) precision; |
2430 | break; |
2431 | case DAY_NAME: |
2432 | case MONTH_NAME: |
2433 | // day and month names may be long in some languages |
2434 | precision = 20; |
2435 | displaySize = (int) precision; |
2436 | break; |
2437 | default: |
2438 | DataType type = DataType.getDataType(dataType); |
2439 | precision = type.defaultPrecision; |
2440 | displaySize = type.defaultDisplaySize; |
2441 | } |
2442 | } |
2443 | |
2444 | @Override |
2445 | public String getSQL() { |
2446 | StatementBuilder buff = new StatementBuilder(info.name); |
2447 | if (info.type == CASE) { |
2448 | if (args[0] != null) { |
2449 | buff.append(" ").append(args[0].getSQL()); |
2450 | } |
2451 | for (int i = 1, len = args.length - 1; i < len; i += 2) { |
2452 | buff.append(" WHEN ").append(args[i].getSQL()); |
2453 | buff.append(" THEN ").append(args[i + 1].getSQL()); |
2454 | } |
2455 | if (args.length % 2 == 0) { |
2456 | buff.append(" ELSE ").append(args[args.length - 1].getSQL()); |
2457 | } |
2458 | return buff.append(" END").toString(); |
2459 | } |
2460 | buff.append('('); |
2461 | switch (info.type) { |
2462 | case CAST: { |
2463 | buff.append(args[0].getSQL()).append(" AS "). |
2464 | append(new Column(null, dataType, precision, |
2465 | scale, displaySize).getCreateSQL()); |
2466 | break; |
2467 | } |
2468 | case CONVERT: { |
2469 | buff.append(args[0].getSQL()).append(','). |
2470 | append(new Column(null, dataType, precision, |
2471 | scale, displaySize).getCreateSQL()); |
2472 | break; |
2473 | } |
2474 | case EXTRACT: { |
2475 | ValueString v = (ValueString) ((ValueExpression) args[0]).getValue(null); |
2476 | buff.append(v.getString()).append(" FROM ").append(args[1].getSQL()); |
2477 | break; |
2478 | } |
2479 | default: { |
2480 | for (Expression e : args) { |
2481 | buff.appendExceptFirst(", "); |
2482 | buff.append(e.getSQL()); |
2483 | } |
2484 | } |
2485 | } |
2486 | return buff.append(')').toString(); |
2487 | } |
2488 | |
2489 | @Override |
2490 | public void updateAggregate(Session session) { |
2491 | for (Expression e : args) { |
2492 | if (e != null) { |
2493 | e.updateAggregate(session); |
2494 | } |
2495 | } |
2496 | } |
2497 | |
2498 | public int getFunctionType() { |
2499 | return info.type; |
2500 | } |
2501 | |
2502 | @Override |
2503 | public String getName() { |
2504 | return info.name; |
2505 | } |
2506 | |
2507 | @Override |
2508 | public ValueResultSet getValueForColumnList(Session session, |
2509 | Expression[] argList) { |
2510 | switch (info.type) { |
2511 | case CSVREAD: { |
2512 | String fileName = argList[0].getValue(session).getString(); |
2513 | if (fileName == null) { |
2514 | throw DbException.get(ErrorCode.PARAMETER_NOT_SET_1, "fileName"); |
2515 | } |
2516 | String columnList = argList.length < 2 ? |
2517 | null : argList[1].getValue(session).getString(); |
2518 | Csv csv = new Csv(); |
2519 | String options = argList.length < 3 ? |
2520 | null : argList[2].getValue(session).getString(); |
2521 | String charset = null; |
2522 | if (options != null && options.indexOf('=') >= 0) { |
2523 | charset = csv.setOptions(options); |
2524 | } else { |
2525 | charset = options; |
2526 | String fieldSeparatorRead = argList.length < 4 ? |
2527 | null : argList[3].getValue(session).getString(); |
2528 | String fieldDelimiter = argList.length < 5 ? |
2529 | null : argList[4].getValue(session).getString(); |
2530 | String escapeCharacter = argList.length < 6 ? |
2531 | null : argList[5].getValue(session).getString(); |
2532 | setCsvDelimiterEscape(csv, fieldSeparatorRead, fieldDelimiter, |
2533 | escapeCharacter); |
2534 | } |
2535 | char fieldSeparator = csv.getFieldSeparatorRead(); |
2536 | String[] columns = StringUtils.arraySplit(columnList, fieldSeparator, true); |
2537 | ResultSet rs = null; |
2538 | ValueResultSet x; |
2539 | try { |
2540 | rs = csv.read(fileName, columns, charset); |
2541 | x = ValueResultSet.getCopy(rs, 0); |
2542 | } catch (SQLException e) { |
2543 | throw DbException.convert(e); |
2544 | } finally { |
2545 | csv.close(); |
2546 | JdbcUtils.closeSilently(rs); |
2547 | } |
2548 | return x; |
2549 | } |
2550 | default: |
2551 | break; |
2552 | } |
2553 | return (ValueResultSet) getValueWithArgs(session, argList); |
2554 | } |
2555 | |
2556 | private static void setCsvDelimiterEscape(Csv csv, String fieldSeparator, |
2557 | String fieldDelimiter, String escapeCharacter) { |
2558 | if (fieldSeparator != null) { |
2559 | csv.setFieldSeparatorWrite(fieldSeparator); |
2560 | if (fieldSeparator.length() > 0) { |
2561 | char fs = fieldSeparator.charAt(0); |
2562 | csv.setFieldSeparatorRead(fs); |
2563 | } |
2564 | } |
2565 | if (fieldDelimiter != null) { |
2566 | char fd = fieldDelimiter.length() == 0 ? |
2567 | 0 : fieldDelimiter.charAt(0); |
2568 | csv.setFieldDelimiter(fd); |
2569 | } |
2570 | if (escapeCharacter != null) { |
2571 | char ec = escapeCharacter.length() == 0 ? |
2572 | 0 : escapeCharacter.charAt(0); |
2573 | csv.setEscapeCharacter(ec); |
2574 | } |
2575 | } |
2576 | |
2577 | @Override |
2578 | public Expression[] getArgs() { |
2579 | return args; |
2580 | } |
2581 | |
2582 | @Override |
2583 | public boolean isEverything(ExpressionVisitor visitor) { |
2584 | for (Expression e : args) { |
2585 | if (e != null && !e.isEverything(visitor)) { |
2586 | return false; |
2587 | } |
2588 | } |
2589 | switch (visitor.getType()) { |
2590 | case ExpressionVisitor.DETERMINISTIC: |
2591 | case ExpressionVisitor.QUERY_COMPARABLE: |
2592 | case ExpressionVisitor.READONLY: |
2593 | return info.deterministic; |
2594 | case ExpressionVisitor.EVALUATABLE: |
2595 | case ExpressionVisitor.GET_DEPENDENCIES: |
2596 | case ExpressionVisitor.INDEPENDENT: |
2597 | case ExpressionVisitor.NOT_FROM_RESOLVER: |
2598 | case ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL: |
2599 | case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID: |
2600 | case ExpressionVisitor.GET_COLUMNS: |
2601 | return true; |
2602 | default: |
2603 | throw DbException.throwInternalError("type=" + visitor.getType()); |
2604 | } |
2605 | } |
2606 | |
2607 | @Override |
2608 | public int getCost() { |
2609 | int cost = 3; |
2610 | for (Expression e : args) { |
2611 | if (e != null) { |
2612 | cost += e.getCost(); |
2613 | } |
2614 | } |
2615 | return cost; |
2616 | } |
2617 | |
2618 | @Override |
2619 | public boolean isDeterministic() { |
2620 | return info.deterministic; |
2621 | } |
2622 | |
2623 | @Override |
2624 | public boolean isBufferResultSetToLocalTemp() { |
2625 | return info.bufferResultSetToLocalTemp; |
2626 | } |
2627 | |
2628 | } |