diff --git a/heidisql.lpi b/heidisql.lpi
index 6cd372f3..ba2f2bbb 100644
--- a/heidisql.lpi
+++ b/heidisql.lpi
@@ -82,6 +82,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -91,7 +123,7 @@
-
+
diff --git a/heidisql.lpr b/heidisql.lpr
index 70393e70..2f8dacad 100644
--- a/heidisql.lpr
+++ b/heidisql.lpr
@@ -11,13 +11,12 @@ uses
{$ENDIF}
Interfaces, // this includes the LCL widgetset
SysUtils,
- Forms, main,
+ Forms,
{ you can add units after this }
- apphelpers,
- dbconnection,
- //gnugettext
- dbstructures,
- dbstructures.mysql, About, generic_types
+ main, apphelpers, dbconnection, { gnugettext,}
+ dbstructures, dbstructures.mysql, About, generic_types,
+ dbstructures.interbase, dbstructures.mssql, dbstructures.postgresql,
+ dbstructures.sqlite, change_password, loginform
;
{$R *.res}
@@ -34,7 +33,7 @@ begin
DefaultFormatSettings.ShortDateFormat := 'yyyy/mm/dd';
DefaultFormatSettings.LongTimeFormat := 'hh:nn:ss';
- //AppSettings := TAppSettings.Create;
+ AppSettings := TAppSettings.Create;
//SecondInstMsgId := RegisterWindowMessage(APPNAME);
if false then begin // (not AppSettings.ReadBool(asAllowMultipleInstances)) and CheckForSecondInstance then begin
//AppSettings.Free;
diff --git a/out/functions-mariadb.ini b/out/functions-mariadb.ini
new file mode 100644
index 00000000..f01db1b6
--- /dev/null
+++ b/out/functions-mariadb.ini
@@ -0,0 +1,1632 @@
+[ABS]
+declaration=X
+category=Numeric Functions
+description=Returns the absolute (non-negative) value of X. If X is not a number, it is\nconverted to a numeric type.\n\nExamples\n--------\n\nSELECT ABS(42);\n+---------+\n| ABS(42) |\n+---------+\n| 42 |\n+---------+\n\nSELECT ABS(-42);\n+----------+\n| ABS(-42) |\n+----------+\n| 42 |\n+----------+\n\nSELECT ABS(DATE '1994-01-01');\n+------------------------+\n| ABS(DATE '1994-01-01') |\n+------------------------+\n| 19940101 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/abs/
+[ACOS]
+declaration=X
+category=Numeric Functions
+description=Returns the arc cosine of X, that is, the value whose cosine is X. Returns\nNULL if X is not in the range -1 to 1.\n\nExamples\n--------\n\nSELECT ACOS(1);\n+---------+\n| ACOS(1) |\n+---------+\n| 0 |\n+---------+\n\nSELECT ACOS(1.0001);\n+--------------+\n| ACOS(1.0001) |\n+--------------+\n| NULL |\n+--------------+\n\nSELECT ACOS(0);\n+-----------------+\n| ACOS(0) |\n+-----------------+\n| 1.5707963267949 |\n+-----------------+\n\nSELECT ACOS(0.234);\n+------------------+\n| ACOS(0.234) |\n+------------------+\n| 1.33460644244679 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/acos/
+[ADDDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, ADDDATE() is a\nsynonym for DATE_ADD(). The related function SUBDATE() is a synonym for\nDATE_SUB(). For information on the INTERVAL unit argument, see the discussion\nfor DATE_ADD().\n\nWhen invoked with the days form of the second argument, MariaDB treats it as\nan integer number of days to be added to expr.\n\nExamples\n--------\n\nSELECT DATE_ADD('2008-01-02', INTERVAL 31 DAY);\n+-----------------------------------------+\n| DATE_ADD('2008-01-02', INTERVAL 31 DAY) |\n+-----------------------------------------+\n| 2008-02-02 |\n+-----------------------------------------+\n\nSELECT ADDDATE('2008-01-02', INTERVAL 31 DAY);\n+----------------------------------------+\n| ADDDATE('2008-01-02', INTERVAL 31 DAY) |\n+----------------------------------------+\n| 2008-02-02 |\n+----------------------------------------+\n\nSELECT ADDDATE('2008-01-02', 31);\n+---------------------------+\n| ADDDATE('2008-01-02', 31) |\n+---------------------------+\n| 2008-02-02 |\n+---------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d, ADDDATE(d, 10) from t1;\n+---------------------+---------------------+\n| d | ADDDATE(d, 10) |\n+---------------------+---------------------+\n| 2007-01-30 21:31:07 | 2007-02-09 21:31:07 |\n| 1983-10-15 06:42:51 | 1983-10-25 06:42:51 |\n| 2011-04-21 12:34:56 | 2011-05-01 12:34:56 |\n| 2011-10-30 06:31:41 | 2011-11-09 06:31:41 |\n| 2011-01-30 14:03:25 | 2011-02-09 14:03:25 |\n ...
+[ADDTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=ADDTIME() adds expr2 to expr1 and returns the result. expr1 is a time or\ndatetime expression, and expr2 is a time expression.\n\nExamples\n--------\n\nSELECT ADDTIME('2007-12-31 23:59:59.999999', '1 1:1:1.000002');\n+---------------------------------------------------------+\n| ADDTIME('2007-12-31 23:59:59.999999', '1 1:1:1.000002') |\n+---------------------------------------------------------+\n| 2008-01-02 01:01:01.000001 |\n+---------------------------------------------------------+\n\nSELECT ADDTIME('01:00:00.999999', '02:00:00.999998');\n+-----------------------------------------------+\n| ADDTIME('01:00:00.999999', '02:00:00.999998') |\n+-----------------------------------------------+\n| 03:00:01.999997 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/addtime/
+[ADD_MONTHS]
+declaration=date, months
+category=Date and Time Functions
+description=ADD_MONTHS adds an integer months to a given date (DATE, DATETIME or\nTIMESTAMP), returning the resulting date.\n\nmonths can be positive or negative. If months is not a whole number, then it\nwill be rounded to the nearest whole number (not truncated).\n\nThe resulting day component will remain the same as that specified in date,\nunless the resulting month has fewer days than the day component of the given\ndate, in which case the day will be the last day of the resulting month.\n\nReturns NULL if given an invalid date, or a NULL argument.\n\nExamples\n--------\n\nSELECT ADD_MONTHS('2012-01-31', 2);\n+-----------------------------+\n| ADD_MONTHS('2012-01-31', 2) |\n+-----------------------------+\n| 2012-03-31 |\n+-----------------------------+\n\nSELECT ADD_MONTHS('2012-01-31', -5);\n+------------------------------+\n| ADD_MONTHS('2012-01-31', -5) |\n+------------------------------+\n| 2011-08-31 |\n+------------------------------+\n\nSELECT ADD_MONTHS('2011-01-31', 1);\n+-----------------------------+\n| ADD_MONTHS('2011-01-31', 1) |\n+-----------------------------+\n| 2011-02-28 |\n+-----------------------------+\n\nSELECT ADD_MONTHS('2012-01-31', 1);\n+-----------------------------+\n| ADD_MONTHS('2012-01-31', 1) |\n+-----------------------------+\n| 2012-02-29 |\n+-----------------------------+\n\nSELECT ADD_MONTHS('2012-01-31', 2);\n+-----------------------------+\n| ADD_MONTHS('2012-01-31', 2) |\n+-----------------------------+\n| 2012-03-31 |\n+-----------------------------+\n\n ...
+[AES_DECRYPT]
+declaration=crypt_str,key_str
+category=Encryption Functions
+description=This function allows decryption of data using the official AES (Advanced\nEncryption Standard) algorithm. For more information, see the description of\nAES_ENCRYPT().\n\nMariaDB starting with 11.2\n--------------------------\nFrom MariaDB 11.2, the function supports an initialization vector, and control\nof the block encryption mode. The default mode is specified by the\nblock_encryption_mode system variable, which can be changed when calling the\nfunction with a mode. mode is aes-{128,192,256}-{ecb,cbc,ctr} for example:\n"AES-128-cbc".\n\nFor modes that require it, the initialization_vector iv should be 16 bytes (it\ncan be longer, but the extra bytes are ignored). A shorter iv, where one is\nrequired, results in the function returning NULL. Calling RANDOM_BYTES(16)\nwill generate a random series of bytes that can be used for the iv.\n\nExamples\n--------\n\nFrom MariaDB 11.2.0:\n\nSELECT HEX(AES_ENCRYPT('foo', 'bar', '0123456789abcdef', 'aes-128-ctr')) AS x; \n+--------+\n| x |\n+--------+\n| C57C4B |\n+--------+\n\nSELECT AES_DECRYPT(x'C57C4B', 'bar', '0123456789abcdef', 'aes-128-ctr'); \n+------------------------------------------------------------------+\n| AES_DECRYPT(x'C57C4B', 'bar', '0123456789abcdef', 'aes-128-ctr') |\n+------------------------------------------------------------------+\n| foo |\n+------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/aes_decrypt/
+[AES_ENCRYPT]
+declaration=str,key_str
+category=Encryption Functions
+description=AES_ENCRYPT() and AES_DECRYPT() allow encryption and decryption of data using\nthe official AES (Advanced Encryption Standard) algorithm, previously known as\n"Rijndael." Encoding with a 128-bit key length is used (from MariaDB 11.2.0,\nthis is the default, and can be changed). 128 bits is much faster and is\nsecure enough for most purposes.\n\nAES_ENCRYPT() encrypts a string str using the key key_str, and returns a\nbinary string.\n\nAES_DECRYPT() decrypts the encrypted string and returns the original string.\n\nThe input arguments may be any length. If either argument is NULL, the result\nof this function is also NULL.\n\nBecause AES is a block-level algorithm, padding is used to encode uneven\nlength strings and so the result string length may be calculated using this\nformula:\n\n16 x (trunc(string_length / 16) + 1)\n\nIf AES_DECRYPT() detects invalid data or incorrect padding, it returns NULL.\nHowever, it is possible for AES_DECRYPT() to return a non-NULL value (possibly\ngarbage) if the input data or the key is invalid.\n\nMariaDB starting with 11.2\n--------------------------\nFrom MariaDB 11.2, the function supports an initialization vector, and control\nof the block encryption mode. The default mode is specified by the\nblock_encryption_mode system variable, which can be changed when calling the\nfunction with a mode. mode is aes-{128,192,256}-{ecb,cbc,ctr} for example:\n"AES-128-cbc".\n\nAES_ENCRYPT(str, key) can no longer be used in persistent virtual columns (and\nthe like).\n\nExamples\n--------\n\nINSERT INTO t VALUES (AES_ENCRYPT('text',SHA2('password',512)));\n\nFrom MariaDB 11.2.0:\n\nSELECT HEX(AES_ENCRYPT('foo', 'bar', '0123456789abcdef', 'aes-256-cbc')) AS x;\n+----------------------------------+\n| x |\n+----------------------------------+\n| 42A3EB91E6DFC40A900D278F99E0726E |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/aes_encrypt/
+[ASCII]
+declaration=str
+category=String Functions
+description=Returns the numeric ASCII value of the leftmost character of the string\nargument. Returns 0 if the given string is empty and NULL if it is NULL.\n\nASCII() works for 8-bit characters.\n\nExamples\n--------\n\nSELECT ASCII(9);\n+----------+\n| ASCII(9) |\n+----------+\n| 57 |\n+----------+\n\nSELECT ASCII('9');\n+------------+\n| ASCII('9') |\n+------------+\n| 57 |\n+------------+\n\nSELECT ASCII('abc');\n+--------------+\n| ASCII('abc') |\n+--------------+\n| 97 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/ascii/
+[ASIN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc sine of X, that is, the value whose sine is X. Returns NULL if\nX is not in the range -1 to 1.\n\nExamples\n--------\n\nSELECT ASIN(0.2);\n+--------------------+\n| ASIN(0.2) |\n+--------------------+\n| 0.2013579207903308 |\n+--------------------+\n\nSELECT ASIN('foo');\n+-------------+\n| ASIN('foo') |\n+-------------+\n| 0 |\n+-------------+\n\nSHOW WARNINGS;\n+---------+------+-----------------------------------------+\n| Level | Code | Message |\n+---------+------+-----------------------------------------+\n| Warning | 1292 | Truncated incorrect DOUBLE value: 'foo' |\n+---------+------+-----------------------------------------+\n\nURL: https://mariadb.com/kb/en/asin/
+[ATAN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc tangent of X, that is, the value whose tangent is X.\n\nExamples\n--------\n\nSELECT ATAN(2);\n+--------------------+\n| ATAN(2) |\n+--------------------+\n| 1.1071487177940904 |\n+--------------------+\n\nSELECT ATAN(-2);\n+---------------------+\n| ATAN(-2) |\n+---------------------+\n| -1.1071487177940904 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/atan/
+[ATAN2]
+declaration=Y,X
+category=Numeric Functions
+description=Returns the arc tangent of the two variables X and Y. It is similar to\ncalculating the arc tangent of Y / X, except that the signs of both arguments\nare used to determine the quadrant of the result.\n\nExamples\n--------\n\nSELECT ATAN(-2,2);\n+---------------------+\n| ATAN(-2,2) |\n+---------------------+\n| -0.7853981633974483 |\n+---------------------+\n\nSELECT ATAN2(PI(),0);\n+--------------------+\n| ATAN2(PI(),0) |\n+--------------------+\n| 1.5707963267948966 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/atan2/
+[AVG]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the average value of expr. The DISTINCT option can be used to return\nthe average of the distinct values of expr. NULL values are ignored. It is an\naggregate function, and so can be used with the GROUP BY clause.\n\nAVG() returns NULL if there were no matching rows.\n\nAVG() can be used as a window function.\n\nExamples\n--------\n\nCREATE TABLE sales (sales_value INT);\n\nINSERT INTO sales VALUES(10),(20),(20),(40);\n\nSELECT AVG(sales_value) FROM sales;\n+------------------+\n| AVG(sales_value) |\n+------------------+\n| 22.5000 |\n+------------------+\n\nSELECT AVG(DISTINCT(sales_value)) FROM sales;\n+----------------------------+\n| AVG(DISTINCT(sales_value)) |\n+----------------------------+\n| 23.3333 |\n+----------------------------+\n\nCommonly, AVG() is used with a GROUP BY clause:\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87), ('Tatiana', 'Tuning', 83);\n\nSELECT name, AVG(score) FROM student GROUP BY name;\n+---------+------------+\n| name | AVG(score) |\n+---------+------------+\n| Chun | 74.0000 |\n| Esben | 37.0000 |\n| Kaolin | 72.0000 |\n| Tatiana | 85.0000 |\n+---------+------------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\n ...
+[BENCHMARK]
+declaration=count,expr
+category=Information Functions
+description=The BENCHMARK() function executes the expression expr repeatedly count times.\nIt may be used to time how quickly MariaDB processes the expression. The\nresult value is always 0. The intended use is from within the mariadb client,\nwhich reports query execution times.\n\nExamples\n--------\n\nSELECT BENCHMARK(1000000,ENCODE('hello','goodbye'));\n+----------------------------------------------+\n| BENCHMARK(1000000,ENCODE('hello','goodbye')) |\n+----------------------------------------------+\n| 0 |\n+----------------------------------------------+\n1 row in set (0.21 sec)\n\nURL: https://mariadb.com/kb/en/benchmark/
+[BIGINT]
+declaration=M
+category=Data Types
+description=A large integer. The signed range is -9223372036854775808 to\n9223372036854775807. The unsigned range is 0 to 18446744073709551615.\n\nIf a column has been set to ZEROFILL, all values will be prepended by zeros so\nthat the BIGINT value contains a number of M digits.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nSERIAL is an alias for:\n\nBIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE\n\nINT8 is a synonym for BIGINT.\n\nExamples\n--------\n\nCREATE TABLE bigints (a BIGINT,b BIGINT UNSIGNED,c BIGINT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO bigints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column 'b' at row 1\n\nINSERT INTO bigints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column 'c' at row 1\n\nINSERT INTO bigints VALUES (-10,10,10);\n\nINSERT INTO bigints VALUES\n(9223372036854775808,9223372036854775808,9223372036854775808);\nERROR 1264 (22003): Out of range value for column 'a' at row 1\n\nINSERT INTO bigints VALUES\n(9223372036854775807,9223372036854775808,9223372036854775808);\n\nSELECT * FROM bigints;\n+---------------------+---------------------+----------------------+\n| a | b | c |\n+---------------------+---------------------+----------------------+\n| -10 | 10 | 00000000000000000010 |\n| 9223372036854775807 | 9223372036854775808 | 09223372036854775808 |\n+---------------------+---------------------+----------------------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO bigints VALUES (-10,-10,-10);\n ...
+[BIN]
+declaration=N
+category=String Functions
+description=Returns a string representation of the binary value of the given longlong\n(that is, BIGINT) number. This is equivalent to CONV(N,10,2). The argument\nshould be positive. If it is a FLOAT, it will be truncated. Returns NULL if\nthe argument is NULL.\n\nExamples\n--------\n\nSELECT BIN(12);\n+---------+\n| BIN(12) |\n+---------+\n| 1100 |\n+---------+\n\nURL: https://mariadb.com/kb/en/bin/
+[BINARY]
+declaration=M
+category=Data Types
+description=The BINARY type is similar to the CHAR type, but stores binary byte strings\nrather than non-binary character strings. M represents the column length in\nbytes.\n\nIt contains no character set, and comparison and sorting are based on the\nnumeric value of the bytes.\n\nIf the maximum length is exceeded, and SQL strict mode is not enabled , the\nextra characters will be dropped with a warning. If strict mode is enabled, an\nerror will occur.\n\nBINARY values are right-padded with 0x00 (the zero byte) to the specified\nlength when inserted. The padding is not removed on select, so this needs to\nbe taken into account when sorting and comparing, where all bytes are\nsignificant. The zero byte, 0x00 is less than a space for comparison purposes.\n\nExamples\n--------\n\nInserting too many characters, first with strict mode off, then with it on:\n\nCREATE TABLE bins (a BINARY(10));\n\nINSERT INTO bins VALUES('12345678901');\nQuery OK, 1 row affected, 1 warning (0.04 sec)\n\nSELECT * FROM bins;\n+------------+\n| a |\n+------------+\n| 1234567890 |\n+------------+\n\nSET sql_mode='STRICT_ALL_TABLES';\n\nINSERT INTO bins VALUES('12345678901');\nERROR 1406 (22001): Data too long for column 'a' at row 1\n\nSorting is performed with the byte value:\n\nTRUNCATE bins;\n\nINSERT INTO bins VALUES('A'),('B'),('a'),('b');\n\nSELECT * FROM bins ORDER BY a;\n+------+\n| a |\n+------+\n| A |\n| B |\n ...
+[BINLOG_GTID_POS]
+declaration=binlog_filename,binlog_offset
+category=Information Functions
+description=The BINLOG_GTID_POS() function takes as input an old-style binary log position\nin the form of a file name and a file offset. It looks up the position in the\ncurrent binlog, and returns a string representation of the corresponding GTID\nposition. If the position is not found in the current binlog, NULL is returned.\n\nExamples\n--------\n\nSELECT BINLOG_GTID_POS("master-bin.000001", 600);\n\nURL: https://mariadb.com/kb/en/binlog_gtid_pos/
+[BIT]
+declaration=M
+category=Data Types
+description=A bit-field type. M indicates the number of bits per value, from 1 to 64. The\ndefault is 1 if M is omitted.\n\nBit values can be inserted with b'value' notation, where value is the bit\nvalue in 0's and 1's.\n\nBit fields are automatically zero-padded from the left to the full length of\nthe bit, so for example in a BIT(4) field, '10' is equivalent to '0010'.\n\nBits are returned as binary, so to display them, either add 0, or use a\nfunction such as HEX, OCT or BIN to convert them.\n\nExamples\n--------\n\nCREATE TABLE b ( b1 BIT(8) );\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO b VALUES (b'11111111');\n\nINSERT INTO b VALUES (b'01010101');\n\nINSERT INTO b VALUES (b'1111111111111');\nERROR 1406 (22001): Data too long for column 'b1' at row 1\n\nSELECT b1+0, HEX(b1), OCT(b1), BIN(b1) FROM b;\n+------+---------+---------+----------+\n| b1+0 | HEX(b1) | OCT(b1) | BIN(b1) |\n+------+---------+---------+----------+\n| 255 | FF | 377 | 11111111 |\n| 85 | 55 | 125 | 1010101 |\n+------+---------+---------+----------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO b VALUES (b'11111111'),(b'01010101'),(b'1111111111111');\nQuery OK, 3 rows affected, 1 warning (0.10 sec)\nRecords: 3 Duplicates: 0 Warnings: 1\n\nSHOW WARNINGS;\n+---------+------+---------------------------------------------+\n| Level | Code | Message |\n+---------+------+---------------------------------------------+\n| Warning | 1264 | Out of range value for column 'b1' at row 3 |\n+---------+------+---------------------------------------------+\n\nSELECT b1+0, HEX(b1), OCT(b1), BIN(b1) FROM b;\n+------+---------+---------+----------+\n| b1+0 | HEX(b1) | OCT(b1) | BIN(b1) |\n ...
+[BIT_AND]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise AND of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_AND will return a value with all bits set to 1. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_AND can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n ('a',111),('a',110),('a',100),\n ('b','000'),('b',001),('b',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_AND(NULL);\n+----------------------+\n| BIT_AND(NULL) |\n+----------------------+\n| 18446744073709551615 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/bit_and/
+[BIT_COUNT]
+declaration=N
+category=Bit Functions
+description=Returns the number of bits that are set in the argument N.\n\nExamples\n--------\n\nSELECT BIT_COUNT(29), BIT_COUNT(b'101010');\n+---------------+----------------------+\n| BIT_COUNT(29) | BIT_COUNT(b'101010') |\n+---------------+----------------------+\n| 4 | 3 |\n+---------------+----------------------+\n\nURL: https://mariadb.com/kb/en/bit_count/
+[BIT_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the given string argument in bits. If the argument is\nnot a string, it will be converted to string. If the argument is NULL, it\nreturns NULL.\n\nExamples\n--------\n\nSELECT BIT_LENGTH('text');\n+--------------------+\n| BIT_LENGTH('text') |\n+--------------------+\n| 32 |\n+--------------------+\n\nSELECT BIT_LENGTH('');\n+----------------+\n| BIT_LENGTH('') |\n+----------------+\n| 0 |\n+----------------+\n\nCompatibility\n-------------\n\nPostgreSQL and Sybase support BIT_LENGTH().\n\nURL: https://mariadb.com/kb/en/bit_length/
+[BIT_OR]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise OR of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_OR will return a value with all bits set to 0. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_OR can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n ('a',111),('a',110),('a',100),\n ('b','000'),('b',001),('b',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_OR(NULL);\n+--------------+\n| BIT_OR(NULL) |\n+--------------+\n| 0 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/bit_or/
+[BIT_XOR]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise XOR of all bits in expr. The calculation is performed with\n64-bit (BIGINT) precision. It is an aggregate function, and so can be used\nwith the GROUP BY clause.\n\nIf no rows match, BIT_XOR will return a value with all bits set to 0. NULL\nvalues have no effect on the result unless all results are NULL, which is\ntreated as no match.\n\nBIT_XOR can be used as a window function with the addition of the over_clause.\n\nExamples\n--------\n\nCREATE TABLE vals (x INT);\n\nINSERT INTO vals VALUES(111),(110),(100);\n\nSELECT BIT_AND(x), BIT_OR(x), BIT_XOR(x) FROM vals;\n+------------+-----------+------------+\n| BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+------------+-----------+------------+\n| 100 | 111 | 101 |\n+------------+-----------+------------+\n\nAs an aggregate function:\n\nCREATE TABLE vals2 (category VARCHAR(1), x INT);\n\nINSERT INTO vals2 VALUES\n ('a',111),('a',110),('a',100),\n ('b','000'),('b',001),('b',011);\n\nSELECT category, BIT_AND(x), BIT_OR(x), BIT_XOR(x) \n FROM vals GROUP BY category;\n+----------+------------+-----------+------------+\n| category | BIT_AND(x) | BIT_OR(x) | BIT_XOR(x) |\n+----------+------------+-----------+------------+\n| a | 100 | 111 | 101 |\n| b | 0 | 11 | 10 |\n+----------+------------+-----------+------------+\n\nNo match:\n\nSELECT BIT_XOR(NULL);\n+---------------+\n| BIT_XOR(NULL) |\n+---------------+\n| 0 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/bit_xor/
+[BLOB]
+declaration=M
+category=Data Types
+description=A BLOB column with a maximum length of 65,535 (216 - 1) bytes. Each BLOB value\nis stored using a two-byte length prefix that indicates the number of bytes in\nthe value.\n\nAn optional length M can be given for this type. If this is done, MariaDB\ncreates the column as the smallest BLOB type large enough to hold values M\nbytes long.\n\nBLOBS can also be used to store dynamic columns.\n\nBLOB and TEXT columns can both be assigned a DEFAULT value.\n\nIndexing\n--------\n\nMariaDB starting with 10.4\n--------------------------\nFrom MariaDB 10.4, it is possible to set a unique index on a column that uses\nthe BLOB data type. In previous releases this was not possible, as the index\nwould only guarantee the uniqueness of a fixed number of characters.\n\nOracle Mode\n-----------\n\nIn Oracle mode from MariaDB 10.3, BLOB is a synonym for LONGBLOB.\n\nURL: https://mariadb.com/kb/en/blob/
+[CAST]
+declaration=expr AS type
+category=String Functions
+description=The CAST() function takes a value of one type and produces a value of another\ntype, similar to the CONVERT() function.\n\nThe type can be one of the following values:\n\n* BINARY\n* CHAR\n* DATE\n* DATETIME\n* DECIMAL[(M[,D])]\n* DOUBLE\n* FLOAT (from MariaDB 10.4.5)\n* INTEGER\nShort for SIGNED INTEGER\n\n* SIGNED [INTEGER]\n* UNSIGNED [INTEGER]\n* TIME\n* VARCHAR (in Oracle mode, from MariaDB 10.3)\n\nThe main difference between CAST and CONVERT() is that CONVERT(expr,type) is\nODBC syntax while CAST(expr as type) and CONVERT(... USING ...) are SQL92\nsyntax.\n\nIn MariaDB 10.4 and later, you can use the CAST() function with the INTERVAL\nkeyword.\n\nUntil MariaDB 5.5.31, X'HHHH', the standard SQL syntax for binary string\nliterals, erroneously worked in the same way as 0xHHHH. In 5.5.31 it was\nintentionally changed to behave as a string in all contexts (and never as a\nnumber).\n\nThis introduced an incompatibility with previous versions of MariaDB, and all\nversions of MySQL (see the example below).\n\nExamples\n--------\n\nSimple casts:\n\nSELECT CAST("abc" AS BINARY);\nSELECT CAST("1" AS UNSIGNED INTEGER);\nSELECT CAST(123 AS CHAR CHARACTER SET utf8)\n\nNote that when one casts to CHAR without specifying the character set, the\ncollation_connection character set collation will be used. When used with CHAR\nCHARACTER SET, the default collation for that character set will be used.\n\nSELECT COLLATION(CAST(123 AS CHAR));\n+------------------------------+\n ...
+[CEIL]
+declaration=X
+category=Numeric Functions
+description=CEIL() is a synonym for CEILING().\n\nURL: https://mariadb.com/kb/en/ceil/
+[CEILING]
+declaration=X
+category=Numeric Functions
+description=Returns the smallest integer value not less than X.\n\nExamples\n--------\n\nSELECT CEILING(1.23);\n+---------------+\n| CEILING(1.23) |\n+---------------+\n| 2 |\n+---------------+\n\nSELECT CEILING(-1.23);\n+----------------+\n| CEILING(-1.23) |\n+----------------+\n| -1 |\n+----------------+\n\nURL: https://mariadb.com/kb/en/ceiling/
+[CHAR]
+declaration=M
+category=Data Types
+description=A fixed-length string that is always right-padded with spaces to the specified\nlength when stored. M represents the column length in characters. The range of\nM is 0 to 255. If M is omitted, the length is 1.\n\nCHAR(0) columns can contain 2 values: an empty string or NULL. Such columns\ncannot be part of an index. The CONNECT storage engine does not support\nCHAR(0).\n\nNote: Trailing spaces are removed when CHAR values are retrieved unless the\nPAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.\n\nBefore MariaDB 10.2, all collations were of type PADSPACE, meaning that CHAR\n(as well as VARCHAR and TEXT) values are compared without regard for trailing\nspaces. This does not apply to the LIKE pattern-matching operator, which takes\ninto account trailing spaces.\n\nIf a unique index consists of a column where trailing pad characters are\nstripped or ignored, inserts into that column where values differ only by the\nnumber of trailing pad characters will result in a duplicate-key error.\n\nExamples\n--------\n\nTrailing spaces:\n\nCREATE TABLE strtest (c CHAR(10));\nINSERT INTO strtest VALUES('Maria ');\n\nSELECT c='Maria',c='Maria ' FROM strtest;\n+-----------+--------------+\n| c='Maria' | c='Maria ' |\n+-----------+--------------+\n| 1 | 1 |\n+-----------+--------------+\n\nSELECT c LIKE 'Maria',c LIKE 'Maria ' FROM strtest;\n+----------------+-------------------+\n| c LIKE 'Maria' | c LIKE 'Maria ' |\n+----------------+-------------------+\n| 1 | 0 |\n+----------------+-------------------+\n\nNO PAD Collations\n-----------------\n\nNO PAD collations regard trailing spaces as normal characters. You can get a\nlist of all NO PAD collations by querying the Information Schema Collations\ntable, for example:\n\nSELECT collation_name FROM information_schema.collations \n ...
+[CHARSET]
+declaration=str
+category=Information Functions
+description=Returns the character set of the string argument. If str is not a string, it\nis considered as a binary string (so the function returns 'binary'). This\napplies to NULL, too. The return value is a string in the utf8 character set.\n\nExamples\n--------\n\nSELECT CHARSET('abc');\n+----------------+\n| CHARSET('abc') |\n+----------------+\n| latin1 |\n+----------------+\n\nSELECT CHARSET(CONVERT('abc' USING utf8));\n+------------------------------------+\n| CHARSET(CONVERT('abc' USING utf8)) |\n+------------------------------------+\n| utf8 |\n+------------------------------------+\n\nSELECT CHARSET(USER());\n+-----------------+\n| CHARSET(USER()) |\n+-----------------+\n| utf8 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/charset/
+[CHAR_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the given string argument, measured in characters. A\nmulti-byte character counts as a single character. This means that for a\nstring containing five two-byte characters, LENGTH() (or OCTET_LENGTH() in\nOracle mode) returns 10, whereas CHAR_LENGTH() returns 5. If the argument is\nNULL, it returns NULL.\n\nIf the argument is not a string value, it is converted into a string.\n\nIt is synonymous with the CHARACTER_LENGTH() function.\n\nExamples\n--------\n\nSELECT CHAR_LENGTH('MariaDB');\n+------------------------+\n| CHAR_LENGTH('MariaDB') |\n+------------------------+\n| 7 |\n+------------------------+\n\nWhen Oracle mode from MariaDB 10.3 is not set:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 2 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nIn Oracle mode from MariaDB 10.3:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 1 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nURL: https://mariadb.com/kb/en/char_length/
+[CHR]
+declaration=N
+category=String Functions
+description=CHR() interprets each argument N as an integer and returns a VARCHAR(1) string\nconsisting of the character given by the code values of the integer. The\ncharacter set and collation of the string are set according to the values of\nthe character_set_database and collation_database system variables.\n\nCHR() is similar to the CHAR() function, but only accepts a single argument.\n\nCHR() is available in all sql_modes.\n\nExamples\n--------\n\nSELECT CHR(67);\n+---------+\n| CHR(67) |\n+---------+\n| C |\n+---------+\n\nSELECT CHR('67');\n+-----------+\n| CHR('67') |\n+-----------+\n| C |\n+-----------+\n\nSELECT CHR('C');\n+----------+\n| CHR('C') |\n+----------+\n| |\n+----------+\n1 row in set, 1 warning (0.000 sec)\n\nSHOW WARNINGS;\n+---------+------+----------------------------------------+\n| Level | Code | Message |\n+---------+------+----------------------------------------+\n| Warning | 1292 | Truncated incorrect INTEGER value: 'C' |\n+---------+------+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/chr/
+[COALESCE]
+declaration=value,...
+category=Comparison Operators
+description=Returns the first non-NULL value in the list, or NULL if there are no non-NULL\nvalues. At least one parameter must be passed.\n\nThe function is useful when substituting a default value for null values when\ndisplaying data.\n\nSee also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT COALESCE(NULL,1);\n+------------------+\n| COALESCE(NULL,1) |\n+------------------+\n| 1 |\n+------------------+\n\nSELECT COALESCE(NULL,NULL,NULL);\n+--------------------------+\n| COALESCE(NULL,NULL,NULL) |\n+--------------------------+\n| NULL |\n+--------------------------+\n\nWhen two arguments are given, COALESCE() is the same as IFNULL():\n\nSET @a=NULL, @b=1;\n\nSELECT COALESCE(@a, @b), IFNULL(@a, @b);\n+------------------+----------------+\n| COALESCE(@a, @b) | IFNULL(@a, @b) |\n+------------------+----------------+\n| 1 | 1 |\n+------------------+----------------+\n\nHex type confusion:\n\nCREATE TABLE t1 (a INT, b VARCHAR(10));\nINSERT INTO t1 VALUES (0x31, 0x61),(COALESCE(0x31), COALESCE(0x61));\n\nSELECT * FROM t1;\n+------+------+\n| a | b |\n+------+------+\n| 49 | a |\n| 1 | a |\n+------+------+\n\nThe reason for the differing results above is that when 0x31 is inserted\n ...
+[COERCIBILITY]
+declaration=str
+category=Information Functions
+description=Returns the collation coercibility value of the string argument. Coercibility\ndefines what will be converted to what in case of collation conflict, with an\nexpression with higher coercibility being converted to the collation of an\nexpression with lower coercibility.\n\n+-----------------------------+---------------------------+------------------+\n| Coercibility | Description | Example |\n+-----------------------------+---------------------------+------------------+\n| 0 | Explicit | Value using a |\n| | | COLLATE clause |\n+-----------------------------+---------------------------+------------------+\n| 1 | No collation | Concatenated |\n| | | strings using |\n| | | different |\n| | | collations |\n+-----------------------------+---------------------------+------------------+\n| 2 | Implicit | A string data |\n| | | type column |\n| | | value, CAST to |\n| | | a string data |\n| | | type |\n+-----------------------------+---------------------------+------------------+\n| 3 | System constant | DATABASE(), |\n| | | USER() return |\n| | | value |\n+-----------------------------+---------------------------+------------------+\n| 4 | Coercible | Literal string |\n+-----------------------------+---------------------------+------------------+\n| 5 | Numeric | Numeric and |\n| | | temporal values |\n+-----------------------------+---------------------------+------------------+\n| 6 | Ignorable | NULL or derived |\n| | | from NULL |\n+-----------------------------+---------------------------+------------------+\n\nExamples\n--------\n\nSELECT COERCIBILITY('abc' COLLATE latin1_swedish_ci);\n+-----------------------------------------------+\n| COERCIBILITY('abc' COLLATE latin1_swedish_ci) |\n+-----------------------------------------------+\n| 0 |\n+-----------------------------------------------+\n\nSELECT COERCIBILITY(CAST(1 AS CHAR));\n+-------------------------------+\n| COERCIBILITY(CAST(1 AS CHAR)) |\n+-------------------------------+\n| 2 |\n ...
+[COLLATION]
+declaration=str
+category=Information Functions
+description=Returns the collation of the string argument. If str is not a string, it is\nconsidered as a binary string (so the function returns 'binary'). This applies\nto NULL, too. The return value is a string in the utf8 character set.\n\nSee Character Sets and Collations.\n\nExamples\n--------\n\nSELECT COLLATION('abc');\n+-------------------+\n| COLLATION('abc') |\n+-------------------+\n| latin1_swedish_ci |\n+-------------------+\n\nSELECT COLLATION(_utf8'abc');\n+-----------------------+\n| COLLATION(_utf8'abc') |\n+-----------------------+\n| utf8_general_ci |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/collation/
+[COLUMN_ADD]
+declaration=dyncol_blob, column_nr, value [as type], [column_nr, value [as type]]...
+category=Dynamic Column Functions
+description=Adds or updates dynamic columns.\n\n* dyncol_blob must be either a valid dynamic columns blob (for example,\nCOLUMN_CREATE returns such blob), or an empty string.\n* column_name specifies the name of the column to be added. If dyncol_blob\nalready has a column with this name, it will be overwritten.\n* value specifies the new value for the column. Passing a NULL value will\ncause the column to be deleted.\n* as type is optional. See #datatypes section for a discussion about types.\n\nThe return value is a dynamic column blob after the modifications.\n\nExamples\n--------\n\nUPDATE t1 SET dyncol_blob=COLUMN_ADD(dyncol_blob, "column_name", "value")\nWHERE id=1;\n\nNote: COLUMN_ADD() is a regular function (just like CONCAT()), hence, in order\nto update the value in the table you have to use the UPDATE ... SET\ndynamic_col=COLUMN_ADD(dynamic_col, ....) pattern.\n\nURL: https://mariadb.com/kb/en/column_add/
+[COLUMN_CHECK]
+declaration=dyncol_blob
+category=Dynamic Column Functions
+description=Check if dyncol_blob is a valid packed dynamic columns blob. Return value of 1\nmeans the blob is valid, return value of 0 means it is not.\n\nRationale: Normally, one works with valid dynamic column blobs. Functions like\nCOLUMN_CREATE, COLUMN_ADD, COLUMN_DELETE always return valid dynamic column\nblobs. However, if a dynamic column blob is accidentally truncated, or\ntranscoded from one character set to another, it will be corrupted. This\nfunction can be used to check if a value in a blob field is a valid dynamic\ncolumn blob.\n\nURL: https://mariadb.com/kb/en/column_check/
+[COLUMN_CREATE]
+declaration=column_nr, value [as type], [column_nr, value [as type]]...
+category=Dynamic Column Functions
+description=Returns a dynamic columns blob that stores the specified columns with values.\n\nThe return value is suitable for\n\n* storing in a table\n* further modification with other dynamic columns functions\n\nThe as type part allows one to specify the value type. In most cases, this is\nredundant because MariaDB will be able to deduce the type of the value.\nExplicit type specification may be needed when the type of the value is not\napparent. For example, a literal '2012-12-01' has a CHAR type by default, one\nwill need to specify '2012-12-01' AS DATE to have it stored as a date. See\nDynamic Columns:Datatypes for further details.\n\nExamples\n--------\n\nINSERT INTO tbl SET dyncol_blob=COLUMN_CREATE("column_name", "value");\n\nURL: https://mariadb.com/kb/en/column_create/
+[COLUMN_DELETE]
+declaration=dyncol_blob, column_nr, column_nr...
+category=Dynamic Column Functions
+description=Deletes a dynamic column with the specified name. Multiple names can be given.\nThe return value is a dynamic column blob after the modification.\n\nURL: https://mariadb.com/kb/en/column_delete/
+[COLUMN_EXISTS]
+declaration=dyncol_blob, column_nr
+category=Dynamic Column Functions
+description=Checks if a column with name column_name exists in dyncol_blob. If yes, return\n1, otherwise return 0. See dynamic columns for more information.\n\nURL: https://mariadb.com/kb/en/column_exists/
+[COLUMN_GET]
+declaration=dyncol_blob, column_nr as type
+category=Dynamic Column Functions
+description=Gets the value of a dynamic column by its name. If no column with the given\nname exists, NULL will be returned.\n\ncolumn_name as type requires that one specify the datatype of the dynamic\ncolumn they are reading.\n\nThis may seem counter-intuitive: why would one need to specify which datatype\nthey're retrieving? Can't the dynamic columns system figure the datatype from\nthe data being stored?\n\nThe answer is: SQL is a statically-typed language. The SQL interpreter needs\nto know the datatypes of all expressions before the query is run (for example,\nwhen one is using prepared statements and runs "select COLUMN_GET(...)", the\nprepared statement API requires the server to inform the client about the\ndatatype of the column being read before the query is executed and the server\ncan see what datatype the column actually has).\n\nLengths\n-------\n\nIf you're running queries like:\n\nSELECT COLUMN_GET(blob, 'colname' as CHAR) ...\n\nwithout specifying a maximum length (i.e. using as CHAR, not as CHAR(n)),\nMariaDB will report the maximum length of the resultset column to be\n16,777,216. This may cause excessive memory usage in some client libraries,\nbecause they try to pre-allocate a buffer of maximum resultset width. To avoid\nthis problem, use CHAR(n) whenever you're using COLUMN_GET in the select list.\n\nSee Dynamic Columns:Datatypes for more information about datatypes.\n\nURL: https://mariadb.com/kb/en/column_get/
+[COLUMN_JSON]
+declaration=dyncol_blob
+category=Dynamic Column Functions
+description=Returns a JSON representation of data in dyncol_blob. Can also be used to\ndisplay nested columns. See dynamic columns for more information.\n\nExample\n-------\n\nselect item_name, COLUMN_JSON(dynamic_cols) from assets;\n+-----------------+----------------------------------------+\n| item_name | COLUMN_JSON(dynamic_cols) |\n+-----------------+----------------------------------------+\n| MariaDB T-shirt | {"size":"XL","color":"blue"} |\n| Thinkpad Laptop | {"color":"black","warranty":"3 years"} |\n+-----------------+----------------------------------------+\n\nLimitation: COLUMN_JSON will decode nested dynamic columns at a nesting level\nof not more than 10 levels deep. Dynamic columns that are nested deeper than\n10 levels will be shown as BINARY string, without encoding.\n\nURL: https://mariadb.com/kb/en/column_json/
+[COLUMN_LIST]
+declaration=dyncol_blob
+category=Dynamic Column Functions
+description=Returns a comma-separated list of column names. The names are quoted with\nbackticks.\n\nSee dynamic columns for more information.\n\nURL: https://mariadb.com/kb/en/column_list/
+[COMMIT]
+declaration=the keyword WORK is simply noise and can be omitted without changing the effect
+category=Transactions
+description=The optional AND CHAIN clause is a convenience for initiating a new\ntransaction as soon as the old transaction terminates. If AND CHAIN is\nspecified, then there is effectively nothing between the old and new\ntransactions, although they remain separate. The characteristics of the new\ntransaction will be the same as the characteristics of the old one - that is,\nthe new transaction will have the same access mode, isolation level and\ndiagnostics area size (we'll discuss all of these shortly) as the transaction\njust terminated.\n\nRELEASE tells the server to disconnect the client immediately after the\ncurrent transaction.\n\nThere are NO RELEASE and AND NO CHAIN options. By default, commits do not\nRELEASE or CHAIN, but it's possible to change this default behavior with the\ncompletion_type server system variable. In this case, the AND NO CHAIN and NO\nRELEASE options override the server default.\n\nURL: https://mariadb.com/kb/en/commit/
+[COMPRESS]
+declaration=string_to_compress
+category=Encryption Functions
+description=Compresses a string and returns the result as a binary string. This function\nrequires MariaDB to have been compiled with a compression library such as\nzlib. Otherwise, the return value is always NULL. The compressed string can be\nuncompressed with UNCOMPRESS().\n\nThe have_compress server system variable indicates whether a compression\nlibrary is present.\n\nExamples\n--------\n\nSELECT LENGTH(COMPRESS(REPEAT('a',1000)));\n+------------------------------------+\n| LENGTH(COMPRESS(REPEAT('a',1000))) |\n+------------------------------------+\n| 21 |\n+------------------------------------+\n\nSELECT LENGTH(COMPRESS(''));\n+----------------------+\n| LENGTH(COMPRESS('')) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSELECT LENGTH(COMPRESS('a'));\n+-----------------------+\n| LENGTH(COMPRESS('a')) |\n+-----------------------+\n| 13 |\n+-----------------------+\n\nSELECT LENGTH(COMPRESS(REPEAT('a',16)));\n+----------------------------------+\n| LENGTH(COMPRESS(REPEAT('a',16))) |\n+----------------------------------+\n| 15 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/compress/
+[CONCAT]
+declaration=str1,str2,...
+category=String Functions
+description=Returns the string that results from concatenating the arguments. May have one\nor more arguments. If all arguments are non-binary strings, the result is a\nnon-binary string. If the arguments include any binary strings, the result is\na binary string. A numeric argument is converted to its equivalent binary\nstring form; if you want to avoid that, you can use an explicit type cast, as\nin this example:\n\nSELECT CONCAT(CAST(int_col AS CHAR), char_col);\n\nCONCAT() returns NULL if any argument is NULL.\n\nA NULL parameter hides all information contained in other parameters from the\nresult. Sometimes this is not desirable; to avoid this, you can:\n\n* Use the CONCAT_WS() function with an empty separator, because that function\nis NULL-safe.\n* Use IFNULL() to turn NULLs into empty strings.\n\nOracle Mode\n-----------\n\nIn Oracle mode, CONCAT ignores NULL.\n\nExamples\n--------\n\nSELECT CONCAT('Ma', 'ria', 'DB');\n+---------------------------+\n| CONCAT('Ma', 'ria', 'DB') |\n+---------------------------+\n| MariaDB |\n+---------------------------+\n\nSELECT CONCAT('Ma', 'ria', NULL, 'DB');\n+---------------------------------+\n| CONCAT('Ma', 'ria', NULL, 'DB') |\n+---------------------------------+\n| NULL |\n+---------------------------------+\n\nSELECT CONCAT(42.0);\n+--------------+\n| CONCAT(42.0) |\n+--------------+\n| 42.0 |\n+--------------+\n\nUsing IFNULL() to handle NULLs:\n\nSELECT CONCAT('The value of @v is: ', IFNULL(@v, ''));\n ...
+[CONCAT_WS]
+declaration=separator,str1,str2,...
+category=String Functions
+description=CONCAT_WS() stands for Concatenate With Separator and is a special form of\nCONCAT(). The first argument is the separator for the rest of the arguments.\nThe separator is added between the strings to be concatenated. The separator\ncan be a string, as can the rest of the arguments.\n\nIf the separator is NULL, the result is NULL; all other NULL values are\nskipped. This makes CONCAT_WS() suitable when you want to concatenate some\nvalues and avoid losing all information if one of them is NULL.\n\nExamples\n--------\n\nSELECT CONCAT_WS(',','First name','Second name','Last Name');\n+-------------------------------------------------------+\n| CONCAT_WS(',','First name','Second name','Last Name') |\n+-------------------------------------------------------+\n| First name,Second name,Last Name |\n+-------------------------------------------------------+\n\nSELECT CONCAT_WS('-','Floor',NULL,'Room');\n+------------------------------------+\n| CONCAT_WS('-','Floor',NULL,'Room') |\n+------------------------------------+\n| Floor-Room |\n+------------------------------------+\n\nIn some cases, remember to include a space in the separator string:\n\nSET @a = 'gnu', @b = 'penguin', @c = 'sea lion';\nQuery OK, 0 rows affected (0.00 sec)\n\nSELECT CONCAT_WS(', ', @a, @b, @c);\n+-----------------------------+\n| CONCAT_WS(', ', @a, @b, @c) |\n+-----------------------------+\n| gnu, penguin, sea lion |\n+-----------------------------+\n\nUsing CONCAT_WS() to handle NULLs:\n\nSET @a = 'a', @b = NULL, @c = 'c';\n\nSELECT CONCAT_WS('', @a, @b, @c);\n+---------------------------+\n| CONCAT_WS('', @a, @b, @c) |\n+---------------------------+\n| ac |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/concat_ws/
+[CONNECTION_ID]
+declaration=
+category=Information Functions
+description=Returns the connection ID for the connection. Every connection (including\nevents) has an ID that is unique among the set of currently connected clients.\n\nUntil MariaDB 10.3.1, returns MYSQL_TYPE_LONGLONG, or bigint(10), in all\ncases. From MariaDB 10.3.1, returns MYSQL_TYPE_LONG, or int(10), when the\nresult would fit within 32-bits.\n\nExamples\n--------\n\nSELECT CONNECTION_ID();\n+-----------------+\n| CONNECTION_ID() |\n+-----------------+\n| 3 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/connection_id/
+[CONTAINS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether a geometry g1 completely contains geometry\ng2. CONTAINS() is based on the original MySQL implementation and uses object\nbounding rectangles, while ST_CONTAINS() uses object shapes.\n\nThis tests the opposite relationship to Within().\n\nURL: https://mariadb.com/kb/en/contains/
+[CONV]
+declaration=N,from_base,to_base
+category=Numeric Functions
+description=Converts numbers between different number bases. Returns a string\nrepresentation of the number N, converted from base from_base to base to_base.\n\nReturns NULL if any argument is NULL, or if the second or third argument are\nnot in the allowed range.\n\nThe argument N is interpreted as an integer, but may be specified as an\ninteger or a string. The minimum base is 2 and the maximum base is 36 (prior\nto MariaDB 11.4.0) or 62 (from MariaDB 11.4.0). If to_base is a negative\nnumber, N is regarded as a signed number. Otherwise, N is treated as unsigned.\nCONV() works with 64-bit precision.\n\nSome shortcuts for this function are also available: BIN(), OCT(), HEX(),\nUNHEX(). Also, MariaDB allows binary literal values and hexadecimal literal\nvalues.\n\nExamples\n--------\n\nSELECT CONV('a',16,2);\n+----------------+\n| CONV('a',16,2) |\n+----------------+\n| 1010 |\n+----------------+\n\nSELECT CONV('6E',18,8);\n+-----------------+\n| CONV('6E',18,8) |\n+-----------------+\n| 172 |\n+-----------------+\n\nSELECT CONV(-17,10,-18);\n+------------------+\n| CONV(-17,10,-18) |\n+------------------+\n| -H |\n+------------------+\n\nSELECT CONV(12+'10'+'10'+0xa,10,10);\n+------------------------------+\n| CONV(12+'10'+'10'+0xa,10,10) |\n+------------------------------+\n| 42 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/conv/
+[CONVERT]
+declaration=expr,type
+category=String Functions
+description=The CONVERT() and CAST() functions take a value of one type and produce a\nvalue of another type.\n\nThe type can be one of the following values:\n\n* BINARY\n* CHAR\n* DATE\n* DATETIME\n* DECIMAL[(M[,D])]\n* DOUBLE\n* FLOAT (from MariaDB 10.4.5)\n* INTEGER\nShort for SIGNED INTEGER\n\n* SIGNED [INTEGER]\n* UNSIGNED [INTEGER]\n* TIME\n* VARCHAR (in Oracle mode, from MariaDB 10.3)\n\nNote that in MariaDB, INT and INTEGER are the same thing.\n\nBINARY produces a string with the BINARY data type. If the optional length is\ngiven, BINARY(N) causes the cast to use no more than N bytes of the argument.\nValues shorter than the given number in bytes are padded with 0x00 bytes to\nmake them equal the length value.\n\nCHAR(N) causes the cast to use no more than the number of characters given in\nthe argument.\n\nThe main difference between the CAST() and CONVERT() is that\nCONVERT(expr,type) is ODBC syntax while CAST(expr as type) and CONVERT(...\nUSING ...) are SQL92 syntax.\n\nCONVERT() with USING is used to convert data between different character sets.\nIn MariaDB, transcoding names are the same as the corresponding character set\nnames. For example, this statement converts the string 'abc' in the default\ncharacter set to the corresponding string in the utf8 character set:\n\nSELECT CONVERT('abc' USING utf8);\n\nExamples\n--------\n\nSELECT enum_col FROM tbl_name \nORDER BY CAST(enum_col AS CHAR);\n\nConverting a BINARY to string to permit the LOWER function to work:\n\nSET @x = 'AardVark';\n ...
+[CONVERT_TZ]
+declaration=dt,from_tz,to_tz
+category=Date and Time Functions
+description=CONVERT_TZ() converts a datetime value dt from the time zone given by from_tz\nto the time zone given by to_tz and returns the resulting value.\n\nIn order to use named time zones, such as GMT, MET or Africa/Johannesburg, the\ntime_zone tables must be loaded (see mysql_tzinfo_to_sql).\n\nNo conversion will take place if the value falls outside of the supported\nTIMESTAMP range ('1970-01-01 00:00:01' to '2038-01-19 05:14:07' UTC) when\nconverted from from_tz to UTC.\n\nThis function returns NULL if the arguments are invalid (or named time zones\nhave not been loaded).\n\nSee time zones for more information.\n\nExamples\n--------\n\nSELECT CONVERT_TZ('2016-01-01 12:00:00','+00:00','+10:00');\n+-----------------------------------------------------+\n| CONVERT_TZ('2016-01-01 12:00:00','+00:00','+10:00') |\n+-----------------------------------------------------+\n| 2016-01-01 22:00:00 |\n+-----------------------------------------------------+\n\nUsing named time zones (with the time zone tables loaded):\n\nSELECT CONVERT_TZ('2016-01-01 12:00:00','GMT','Africa/Johannesburg');\n+---------------------------------------------------------------+\n| CONVERT_TZ('2016-01-01 12:00:00','GMT','Africa/Johannesburg') |\n+---------------------------------------------------------------+\n| 2016-01-01 14:00:00 |\n+---------------------------------------------------------------+\n\nThe value is out of the TIMESTAMP range, so no conversion takes place:\n\nSELECT CONVERT_TZ('1969-12-31 22:00:00','+00:00','+10:00');\n+-----------------------------------------------------+\n| CONVERT_TZ('1969-12-31 22:00:00','+00:00','+10:00') |\n+-----------------------------------------------------+\n| 1969-12-31 22:00:00 |\n+-----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/convert_tz/
+[COS]
+declaration=X
+category=Numeric Functions
+description=Returns the cosine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT COS(PI());\n+-----------+\n| COS(PI()) |\n+-----------+\n| -1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/cos/
+[COT]
+declaration=X
+category=Numeric Functions
+description=Returns the cotangent of X.\n\nExamples\n--------\n\nSELECT COT(42);\n+--------------------+\n| COT(42) |\n+--------------------+\n| 0.4364167060752729 |\n+--------------------+\n\nSELECT COT(12);\n+---------------------+\n| COT(12) |\n+---------------------+\n| -1.5726734063976893 |\n+---------------------+\n\nSELECT COT(0);\nERROR 1690 (22003): DOUBLE value is out of range in 'cot(0)'\n\nURL: https://mariadb.com/kb/en/cot/
+[COUNT]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns a count of the number of non-NULL values of expr in the rows retrieved\nby a SELECT statement. The result is a BIGINT value. It is an aggregate\nfunction, and so can be used with the GROUP BY clause.\n\nCOUNT(*) counts the total number of rows in a table.\n\nCOUNT() returns 0 if there were no matching rows.\n\nCOUNT() can be used as a window function.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87), ('Tatiana', 'Tuning', 83);\n\nSELECT COUNT(*) FROM student;\n+----------+\n| COUNT(*) |\n+----------+\n| 8 |\n+----------+\n\nCOUNT(DISTINCT) example:\n\nSELECT COUNT(DISTINCT (name)) FROM student;\n+------------------------+\n| COUNT(DISTINCT (name)) |\n+------------------------+\n| 4 |\n+------------------------+\n\nAs a window function\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87);\n\nSELECT name, test, score, COUNT(score) OVER (PARTITION BY name) \n AS tests_written FROM student_test;\n ...
+[CRC32]
+declaration=expr
+category=Numeric Functions
+description=Computes a cyclic redundancy check (CRC) value and returns a 32-bit unsigned\nvalue. The result is NULL if the argument is NULL. The argument is expected to\nbe a string and (if possible) is treated as one if it is not.\n\nUses the ISO 3309 polynomial that used by zlib and many others. MariaDB 10.8\nintroduced the CRC32C() function, which uses the alternate Castagnoli\npolynomia.\n\nMariaDB starting with 10.8\n--------------------------\nOften, CRC is computed in pieces. To facilitate this, MariaDB 10.8.0\nintroduced an optional parameter: CRC32('MariaDB')=CRC32(CRC32('Maria'),'DB').\n\nExamples\n--------\n\nSELECT CRC32('MariaDB');\n+------------------+\n| CRC32('MariaDB') |\n+------------------+\n| 4227209140 |\n+------------------+\n\nSELECT CRC32('mariadb');\n+------------------+\n| CRC32('mariadb') |\n+------------------+\n| 2594253378 |\n+------------------+\n\nFrom MariaDB 10.8.0\n\nSELECT CRC32(CRC32('Maria'),'DB');\n+----------------------------+\n| CRC32(CRC32('Maria'),'DB') |\n+----------------------------+\n| 4227209140 |\n+----------------------------+\n\nURL: https://mariadb.com/kb/en/crc32/
+[CRC32C]
+declaration=[par,]expr
+category=Numeric Functions
+description=MariaDB has always included a native unary function CRC32() that computes the\nCRC-32 of a string using the ISO 3309 polynomial that used by zlib and many\nothers.\n\nInnoDB and MyRocks use a different polynomial, which was implemented in SSE4.2\ninstructions that were introduced in the Intel Nehalem microarchitecture. This\nis commonly called CRC-32C (Castagnoli).\n\nThe CRC32C function uses the Castagnoli polynomial.\n\nThis allows SELECT…INTO DUMPFILE to be used for the creation of files with\nvalid checksums, such as a logically empty InnoDB redo log file ib_logfile0\ncorresponding to a particular log sequence number.\n\nThe optional parameter allows the checksum to be computed in pieces:\nCRC32C('MariaDB')=CRC32C(CRC32C('Maria'),'DB').\n\nExamples\n--------\n\nSELECT CRC32C('MariaDB');\n+-------------------+\n| CRC32C('MariaDB') |\n+-------------------+\n| 809606978 |\n+-------------------+\n\nSELECT CRC32C(CRC32C('Maria'),'DB');\n+------------------------------+\n| CRC32C(CRC32C('Maria'),'DB') |\n+------------------------------+\n| 809606978 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/crc32c/
+[CROSSES]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 if g1 spatially crosses g2. Returns NULL if g1 is a Polygon or a\nMultiPolygon, or if g2 is a Point or a MultiPoint. Otherwise, returns 0.\n\nThe term spatially crosses denotes a spatial relation between two given\ngeometries that has the following properties:\n\n* The two geometries intersect\n* Their intersection results in a geometry that has a dimension that is one\n less than the maximum dimension of the two given geometries\n* Their intersection is not equal to either of the two given geometries\n\nCROSSES() is based on the original MySQL implementation, and uses object\nbounding rectangles, while ST_CROSSES() uses object shapes.\n\nURL: https://mariadb.com/kb/en/crosses/
+[CUME_DIST]
+declaration=
+category=Window Functions
+description=CUME_DIST() is a window function that returns the cumulative distribution of a\ngiven row. The following formula is used to calculate the value:\n\n(number of rows <= current row) / (total rows)\n\nExamples\n--------\n\ncreate table t1 (\n pk int primary key,\n a int,\n b int\n);\n\ninsert into t1 values\n( 1 , 0, 10),\n( 2 , 0, 10),\n( 3 , 1, 10),\n( 4 , 1, 10),\n( 8 , 2, 10),\n( 5 , 2, 20),\n( 6 , 2, 20),\n( 7 , 2, 20),\n( 9 , 4, 20),\n(10 , 4, 20);\n\nselect pk, a, b,\n rank() over (order by a) as rank,\n percent_rank() over (order by a) as pct_rank,\n cume_dist() over (order by a) as cume_dist\nfrom t1;\n+----+------+------+------+--------------+--------------+\n| pk | a | b | rank | pct_rank | cume_dist |\n+----+------+------+------+--------------+--------------+\n| 1 | 0 | 10 | 1 | 0.0000000000 | 0.2000000000 |\n| 2 | 0 | 10 | 1 | 0.0000000000 | 0.2000000000 |\n| 3 | 1 | 10 | 3 | 0.2222222222 | 0.4000000000 |\n| 4 | 1 | 10 | 3 | 0.2222222222 | 0.4000000000 |\n| 5 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 6 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 7 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 8 | 2 | 10 | 5 | 0.4444444444 | 0.8000000000 |\n| 9 | 4 | 20 | 9 | 0.8888888889 | 1.0000000000 |\n| 10 | 4 | 20 | 9 | 0.8888888889 | 1.0000000000 |\n+----+------+------+------+--------------+--------------+\n\nselect pk, a, b,\n percent_rank() over (order by pk) as pct_rank,\n cume_dist() over (order by pk) as cume_dist\nfrom t1 order by pk;\n ...
+[CURDATE]
+declaration=
+category=Date and Time Functions
+description=CURDATE returns the current date as a value in 'YYYY-MM-DD' or YYYYMMDD\nformat, depending on whether the function is used in a string or numeric\ncontext.\n\nCURRENT_DATE and CURRENT_DATE() are synonyms.\n\nExamples\n--------\n\nSELECT CURDATE();\n+------------+\n| CURDATE() |\n+------------+\n| 2019-03-05 |\n+------------+\n\nIn a numeric context (note this is not performing date calculations):\n\nSELECT CURDATE() +0;\n+--------------+\n| CURDATE() +0 |\n+--------------+\n| 20190305 |\n+--------------+\n\nData calculation:\n\nSELECT CURDATE() - INTERVAL 5 DAY;\n+----------------------------+\n| CURDATE() - INTERVAL 5 DAY |\n+----------------------------+\n| 2019-02-28 |\n+----------------------------+\n\nURL: https://mariadb.com/kb/en/curdate/
+[CURRENT_DATE]
+declaration=
+category=Date and Time Functions
+description=CURRENT_DATE and CURRENT_DATE() are synonyms for CURDATE().\n\nURL: https://mariadb.com/kb/en/current_date/
+[CURRENT_ROLE]
+declaration=
+category=Information Functions
+description=Returns the current role name. This determines your access privileges. The\nreturn value is a string in the utf8 character set.\n\nIf there is no current role, NULL is returned.\n\nThe output of SELECT CURRENT_ROLE is equivalent to the contents of the\nENABLED_ROLES Information Schema table.\n\nUSER() returns the combination of user and host used to login. CURRENT_USER()\nreturns the account used to determine current connection's privileges.\n\nStatements using the CURRENT_ROLE function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| NULL |\n+--------------+\n\nSET ROLE staff;\n\nSELECT CURRENT_ROLE;\n+--------------+\n| CURRENT_ROLE |\n+--------------+\n| staff |\n+--------------+\n\nURL: https://mariadb.com/kb/en/current_role/
+[CURRENT_TIME]
+declaration=[precision]
+category=Date and Time Functions
+description=CURRENT_TIME and CURRENT_TIME() are synonyms for CURTIME().\n\nURL: https://mariadb.com/kb/en/current_time/
+[CURRENT_TIMESTAMP]
+declaration=[precision]
+category=Date and Time Functions
+description=CURRENT_TIMESTAMP and CURRENT_TIMESTAMP() are synonyms for NOW().\n\nURL: https://mariadb.com/kb/en/current_timestamp/
+[CURRENT_USER]
+declaration=
+category=Information Functions
+description=Returns the user name and host name combination for the MariaDB account that\nthe server used to authenticate the current client. This account determines\nyour access privileges. The return value is a string in the utf8 character set.\n\nThe value of CURRENT_USER() can differ from the value of USER().\nCURRENT_ROLE() returns the current active role.\n\nStatements using the CURRENT_USER function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nshell> mysql --user="anonymous"\n\nselect user(),current_user();\n+---------------------+----------------+\n| user() | current_user() |\n+---------------------+----------------+\n| anonymous@localhost | @localhost |\n+---------------------+----------------+\n\nWhen calling CURRENT_USER() in a stored procedure, it returns the owner of the\nstored procedure, as defined with DEFINER.\n\nURL: https://mariadb.com/kb/en/current_user/
+[CURTIME]
+declaration=[precision]
+category=Date and Time Functions
+description=Returns the current time as a value in 'HH:MM:SS' or HHMMSS.uuuuuu format,\ndepending on whether the function is used in a string or numeric context. The\nvalue is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nExamples\n--------\n\nSELECT CURTIME();\n+-----------+\n| CURTIME() |\n+-----------+\n| 12:45:39 |\n+-----------+\n\nSELECT CURTIME() + 0;\n+---------------+\n| CURTIME() + 0 |\n+---------------+\n| 124545.000000 |\n+---------------+\n\nWith precision:\n\nSELECT CURTIME(2);\n+-------------+\n| CURTIME(2) |\n+-------------+\n| 09:49:08.09 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/curtime/
+[DATABASE]
+declaration=
+category=Information Functions
+description=Returns the default (current) database name as a string in the utf8 character\nset. If there is no default database, DATABASE() returns NULL. Within a stored\nroutine, the default database is the database that the routine is associated\nwith, which is not necessarily the same as the database that is the default in\nthe calling context.\n\nSCHEMA() is a synonym for DATABASE().\n\nTo select a default database, the USE statement can be run. Another way to set\nthe default database is specifying its name at mariadb command line client\nstartup.\n\nExamples\n--------\n\nSELECT DATABASE();\n+------------+\n| DATABASE() |\n+------------+\n| NULL |\n+------------+\n\nUSE test;\nDatabase changed\n\nSELECT DATABASE();\n+------------+\n| DATABASE() |\n+------------+\n| test |\n+------------+\n\nURL: https://mariadb.com/kb/en/database/
+[DATEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=DATEDIFF() returns (expr1 – expr2) expressed as a value in days from one date\nto the other. expr1 and expr2 are date or date-and-time expressions. Only the\ndate parts of the values are used in the calculation.\n\nExamples\n--------\n\nSELECT DATEDIFF('2007-12-31 23:59:59','2007-12-30');\n+----------------------------------------------+\n| DATEDIFF('2007-12-31 23:59:59','2007-12-30') |\n+----------------------------------------------+\n| 1 |\n+----------------------------------------------+\n\nSELECT DATEDIFF('2010-11-30 23:59:59','2010-12-31');\n+----------------------------------------------+\n| DATEDIFF('2010-11-30 23:59:59','2010-12-31') |\n+----------------------------------------------+\n| -31 |\n+----------------------------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2011-05-23 10:56:05 |\n+---------------------+\n\nSELECT d, DATEDIFF(NOW(),d) FROM t1;\n+---------------------+-------------------+\n| d | DATEDIFF(NOW(),d) |\n+---------------------+-------------------+\n| 2007-01-30 21:31:07 | 1574 |\n| 1983-10-15 06:42:51 | 10082 |\n| 2011-04-21 12:34:56 | 32 |\n| 2011-10-30 06:31:41 | -160 |\n| 2011-01-30 14:03:25 | 113 |\n| 2004-10-07 11:19:34 | 2419 |\n+---------------------+-------------------+\n\nURL: https://mariadb.com/kb/en/datediff/
+[DATETIME]
+declaration=microsecond precision
+category=Data Types
+description=A date and time combination.\n\nMariaDB displays DATETIME values in 'YYYY-MM-DD HH:MM:SS.ffffff' format, but\nallows assignment of values to DATETIME columns using either strings or\nnumbers. For details, see date and time literals.\n\nDATETIME columns also accept CURRENT_TIMESTAMP as the default value.\n\nMariaDB 10.1.2 introduced the --mysql56-temporal-format option, on by default,\nwhich allows MariaDB to store DATETMEs using the same low-level format MySQL\n5.6 uses. For more information, see Internal Format, below.\n\nFor storage requirements, see Data Type Storage Requirements.\n\nSupported Values\n----------------\n\nMariaDB stores values that use the DATETIME data type in a format that\nsupports values between 1000-01-01 00:00:00.000000 and 9999-12-31\n23:59:59.999999.\n\nMariaDB can also store microseconds with a precision between 0 and 6. If no\nmicrosecond precision is specified, then 0 is used by default.\n\nMariaDB also supports '0000-00-00' as a special zero-date value, unless\nNO_ZERO_DATE is specified in the SQL_MODE. Similarly, individual components of\na date can be set to 0 (for example: '2015-00-12'), unless NO_ZERO_IN_DATE is\nspecified in the SQL_MODE. In many cases, the result of en expression\ninvolving a zero-date, or a date with zero-parts, is NULL. If the\nALLOW_INVALID_DATES SQL_MODE is enabled, if the day part is in the range\nbetween 1 and 31, the date does not produce any error, even for months that\nhave less than 31 days.\n\nOracle Mode\n-----------\n\nMariaDB starting with 10.3\n--------------------------\nIn Oracle mode from MariaDB 10.3, DATE with a time portion is a synonym for\nDATETIME. See also mariadb_schema.\n\nInternal Format\n---------------\n\nIn MariaDB 10.1.2 a new temporal format was introduced from MySQL 5.6 that\nalters how the TIME, DATETIME and TIMESTAMP columns operate at lower levels.\nThese changes allow these temporal data types to have fractional parts and\nnegative values. You can disable this feature using the\nmysql56_temporal_format system variable.\n\n ...
+[DATE_ADD]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=Performs date arithmetic. The date argument specifies the starting date or\ndatetime value. expr is an expression specifying the interval value to be\nadded or subtracted from the starting date. expr is a string; it may start\nwith a "-" for negative intervals. unit is a keyword indicating the units in\nwhich the expression should be interpreted. See Date and Time Units for a\ncomplete list of permitted units.\n\nExamples\n--------\n\nSELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND;\n+-------------------------------------------+\n| '2008-12-31 23:59:59' + INTERVAL 1 SECOND |\n+-------------------------------------------+\n| 2009-01-01 00:00:00 |\n+-------------------------------------------+\n\nSELECT INTERVAL 1 DAY + '2008-12-31';\n+-------------------------------+\n| INTERVAL 1 DAY + '2008-12-31' |\n+-------------------------------+\n| 2009-01-01 |\n+-------------------------------+\n\nSELECT '2005-01-01' - INTERVAL 1 SECOND;\n+----------------------------------+\n| '2005-01-01' - INTERVAL 1 SECOND |\n+----------------------------------+\n| 2004-12-31 23:59:59 |\n+----------------------------------+\n\nSELECT DATE_ADD('2000-12-31 23:59:59', INTERVAL 1 SECOND);\n+----------------------------------------------------+\n| DATE_ADD('2000-12-31 23:59:59', INTERVAL 1 SECOND) |\n+----------------------------------------------------+\n| 2001-01-01 00:00:00 |\n+----------------------------------------------------+\n\nSELECT DATE_ADD('2010-12-31 23:59:59', INTERVAL 1 DAY);\n+-------------------------------------------------+\n| DATE_ADD('2010-12-31 23:59:59', INTERVAL 1 DAY) |\n+-------------------------------------------------+\n| 2011-01-01 23:59:59 |\n+-------------------------------------------------+\n\nSELECT DATE_ADD('2100-12-31 23:59:59', INTERVAL '1:1' MINUTE_SECOND);\n+---------------------------------------------------------------+\n| DATE_ADD('2100-12-31 23:59:59', INTERVAL '1:1' MINUTE_SECOND) |\n+---------------------------------------------------------------+\n| 2101-01-01 00:01:00 |\n ...
+[DATE_FORMAT]
+declaration=date, format[, locale]
+category=Date and Time Functions
+description=Formats the date value according to the format string.\n\nThe language used for the names is controlled by the value of the\nlc_time_names system variable. See server locale for more on the supported\nlocales.\n\nThe options that can be used by DATE_FORMAT(), as well as its inverse\nSTR_TO_DATE() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix 'th', 'nd', 'st' or |\n| | 'rd''. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n| %k | Hour with 1 digits between 0-23. |\n+---------------------------+------------------------------------------------+\n| %l | Hour with 1 digits between 1-12. |\n+---------------------------+------------------------------------------------+\n| %M | Full month name in current locale (Variable |\n| | lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %m | Month with 2 digits. |\n+---------------------------+------------------------------------------------+\n ...
+[DATE_SUB]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=Performs date arithmetic. The date argument specifies the starting date or\ndatetime value. expr is an expression specifying the interval value to be\nadded or subtracted from the starting date. expr is a string; it may start\nwith a "-" for negative intervals. unit is a keyword indicating the units in\nwhich the expression should be interpreted. See Date and Time Units for a\ncomplete list of permitted units.\n\nSee also DATE_ADD().\n\nExamples\n--------\n\nSELECT DATE_SUB('1998-01-02', INTERVAL 31 DAY);\n+-----------------------------------------+\n| DATE_SUB('1998-01-02', INTERVAL 31 DAY) |\n+-----------------------------------------+\n| 1997-12-02 |\n+-----------------------------------------+\n\nSELECT DATE_SUB('2005-01-01 00:00:00', INTERVAL '1 1:1:1' DAY_SECOND);\n+----------------------------------------------------------------+\n| DATE_SUB('2005-01-01 00:00:00', INTERVAL '1 1:1:1' DAY_SECOND) |\n+----------------------------------------------------------------+\n| 2004-12-30 22:58:59 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/date_sub/
+[DAY]
+declaration=date
+category=Date and Time Functions
+description=DAY() is a synonym for DAYOFMONTH().\n\nURL: https://mariadb.com/kb/en/day/
+[DAYNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the name of the weekday for date. The language used for the name is\ncontrolled by the value of the lc_time_names system variable. See server\nlocale for more on the supported locales.\n\nExamples\n--------\n\nSELECT DAYNAME('2007-02-03');\n+-----------------------+\n| DAYNAME('2007-02-03') |\n+-----------------------+\n| Saturday |\n+-----------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d, DAYNAME(d) FROM t1;\n+---------------------+------------+\n| d | DAYNAME(d) |\n+---------------------+------------+\n| 2007-01-30 21:31:07 | Tuesday |\n| 1983-10-15 06:42:51 | Saturday |\n| 2011-04-21 12:34:56 | Thursday |\n| 2011-10-30 06:31:41 | Sunday |\n| 2011-01-30 14:03:25 | Sunday |\n| 2004-10-07 11:19:34 | Thursday |\n+---------------------+------------+\n\nChanging the locale:\n\nSET lc_time_names = 'fr_CA';\n\nSELECT DAYNAME('2013-04-01');\n+-----------------------+\n| DAYNAME('2013-04-01') |\n+-----------------------+\n| lundi |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/dayname/
+[DAYOFMONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the month for date, in the range 1 to 31, or 0 for dates\nsuch as '0000-00-00' or '2008-00-00' which have a zero day part.\n\nDAY() is a synonym.\n\nExamples\n--------\n\nSELECT DAYOFMONTH('2007-02-03');\n+--------------------------+\n| DAYOFMONTH('2007-02-03') |\n+--------------------------+\n| 3 |\n+--------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d FROM t1 where DAYOFMONTH(d) = 30;\n+---------------------+\n| d |\n+---------------------+\n| 2007-01-30 21:31:07 |\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/dayofmonth/
+[DAYOFWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the week index for the date (1 = Sunday, 2 = Monday, ..., 7\n= Saturday). These index values correspond to the ODBC standard.\n\nThis contrasts with WEEKDAY() which follows a different index numbering (0 =\nMonday, 1 = Tuesday, ... 6 = Sunday).\n\nExamples\n--------\n\nSELECT DAYOFWEEK('2007-02-03');\n+-------------------------+\n| DAYOFWEEK('2007-02-03') |\n+-------------------------+\n| 7 |\n+-------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d, DAYNAME(d), DAYOFWEEK(d), WEEKDAY(d) from t1;\n+---------------------+------------+--------------+------------+\n| d | DAYNAME(d) | DAYOFWEEK(d) | WEEKDAY(d) |\n+---------------------+------------+--------------+------------+\n| 2007-01-30 21:31:07 | Tuesday | 3 | 1 |\n| 1983-10-15 06:42:51 | Saturday | 7 | 5 |\n| 2011-04-21 12:34:56 | Thursday | 5 | 3 |\n| 2011-10-30 06:31:41 | Sunday | 1 | 6 |\n| 2011-01-30 14:03:25 | Sunday | 1 | 6 |\n| 2004-10-07 11:19:34 | Thursday | 5 | 3 |\n+---------------------+------------+--------------+------------+\n\nURL: https://mariadb.com/kb/en/dayofweek/
+[DAYOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the year for date, in the range 1 to 366.\n\nExamples\n--------\n\nSELECT DAYOFYEAR('2018-02-16');\n+-------------------------+\n| DAYOFYEAR('2018-02-16') |\n+-------------------------+\n| 47 |\n+-------------------------+\n\nURL: https://mariadb.com/kb/en/dayofyear/
+[DECIMAL]
+declaration=M[,D]
+category=Data Types
+description=A packed "exact" fixed-point number. M is the total number of digits (the\nprecision) and D is the number of digits after the decimal point (the scale).\n\n* The decimal point and (for negative numbers) the "-" sign are not\ncounted in M. \n* If D is 0, values have no decimal point or fractional\npart and on INSERT the value will be rounded to the nearest DECIMAL. \n* The maximum number of digits (M) for DECIMAL is 65. \n* The maximum number of supported decimals (D) is 30 before MariadB 10.2.1 and\n38 afterwards. \n* If D is omitted, the default is 0. If M is omitted, the default is 10.\n\nUNSIGNED, if specified, disallows negative values.\n\nZEROFILL, if specified, pads the number with zeros, up to the total number of\ndigits specified by M.\n\nAll basic calculations (+, -, *, /) with DECIMAL columns are done with a\nprecision of 65 digits.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nDEC, NUMERIC and FIXED are synonyms, as well as NUMBER in Oracle mode from\nMariaDB 10.3.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DECIMAL UNSIGNED ZEROFILL);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4.0),(5.2),(5.7);\nQuery OK, 6 rows affected, 2 warnings (0.16 sec)\nRecords: 6 Duplicates: 0 Warnings: 2\n\nNote (Code 1265): Data truncated for column 'd' at row 5\nNote (Code 1265): Data truncated for column 'd' at row 6\n\nSELECT * FROM t1;\n+------------+\n| d |\n+------------+\n| 0000000001 |\n| 0000000002 |\n| 0000000003 |\n| 0000000004 |\n| 0000000005 |\n| 0000000006 |\n+------------+\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n ...
+[DECODE]
+declaration=crypt_str,pass_str
+category=Encryption Functions
+description=In the default mode, DECODE decrypts the encrypted string crypt_str using\npass_str as the password. crypt_str should be a string returned from ENCODE().\nThe resulting string will be the original string only if pass_str is the same.\n\nIn Oracle mode from MariaDB 10.3.2, DECODE compares expr to the search\nexpressions, in order. If it finds a match, the corresponding result\nexpression is returned. If no matches are found, the default expression is\nreturned, or NULL if no default is provided.\n\nNULLs are treated as equivalent.\n\nDECODE_ORACLE is a synonym for the Oracle-mode version of the function, and is\navailable in all modes.\n\nExamples\n--------\n\nFrom MariaDB 10.3.2:\n\nSELECT DECODE_ORACLE(2+1,3*1,'found1',3*2,'found2','default');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+1,3*1,'found1',3*2,'found2','default') |\n+--------------------------------------------------------+\n| found1 |\n+--------------------------------------------------------+\n\nSELECT DECODE_ORACLE(2+4,3*1,'found1',3*2,'found2','default');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+4,3*1,'found1',3*2,'found2','default') |\n+--------------------------------------------------------+\n| found2 |\n+--------------------------------------------------------+\n\nSELECT DECODE_ORACLE(2+2,3*1,'found1',3*2,'found2','default');\n+--------------------------------------------------------+\n| DECODE_ORACLE(2+2,3*1,'found1',3*2,'found2','default') |\n+--------------------------------------------------------+\n| default |\n+--------------------------------------------------------+\n\nNulls are treated as equivalent:\n\nSELECT DECODE_ORACLE(NULL,NULL,'Nulls are equivalent','Nulls are not\nequivalent');\n+----------------------------------------------------------------------------+\n| DECODE_ORACLE(NULL,NULL,'Nulls are equivalent','Nulls are not equivalent') |\n+----------------------------------------------------------------------------+\n| Nulls are equivalent |\n+----------------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/decode/
+[DECODE_HISTOGRAM]
+declaration=hist_type,histogram
+category=Information Functions
+description=Returns a string of comma separated numeric values corresponding to a\nprobability distribution represented by the histogram of type hist_type\n(SINGLE_PREC_HB or DOUBLE_PREC_HB). The hist_type and histogram would be\ncommonly used from the mysql.column_stats table.\n\nSee Histogram Based Statistics for details.\n\nExamples\n--------\n\nCREATE TABLE origin (\n i INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n v INT UNSIGNED NOT NULL\n);\n\nINSERT INTO origin(v) VALUES \n (1),(2),(3),(4),(5),(10),(20),\n (30),(40),(50),(60),(70),(80),\n (90),(100),(200),(400),(800);\n\nSET histogram_size=10,histogram_type=SINGLE_PREC_HB;\n\nANALYZE TABLE origin PERSISTENT FOR ALL;\n+-------------+---------+----------+-----------------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+-------------+---------+----------+-----------------------------------------+\n| test.origin | analyze | status | Engine-independent statistics collected |\n| test.origin | analyze | status | OK |\n+-------------+---------+----------+-----------------------------------------+\n\nSELECT db_name,table_name,column_name,hist_type,\n hex(histogram),decode_histogram(hist_type,histogram)\n FROM mysql.column_stats WHERE db_name='test' and table_name='origin';\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n| db_name | table_name | column_name | hist_type | hex(histogram) |\ndecode_histogram(hist_type,histogram) |\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n| test | origin | i | SINGLE_PREC_HB | 0F2D3C5A7887A5C3D2F0 |\n0.059,0.118,0.059,0.118,0.118,0.059,0.118,0.118,0.059,0.118,0.059 |\n| test | origin | v | SINGLE_PREC_HB | 000001060C0F161C1F7F |\n0.000,0.000,0.004,0.020,0.024,0.012,0.027,0.024,0.012,0.376,0.502 |\n+---------+------------+-------------+----------------+----------------------+-\n-----------------------------------------------------------------+\n\nSET histogram_size=20,histogram_type=DOUBLE_PREC_HB;\n\nANALYZE TABLE origin PERSISTENT FOR ALL;\n+-------------+---------+----------+-----------------------------------------+\n ...
+[DEFAULT]
+declaration=col_name
+category=Information Functions
+description=Returns the default value for a table column. If the column has no default\nvalue (and is not NULLABLE - NULLABLE fields have a NULL default), an error is\nreturned.\n\nFor integer columns using AUTO_INCREMENT, 0 is returned.\n\nWhen using DEFAULT as a value to set in an INSERT or UPDATE statement, you can\nuse the bare keyword DEFAULT without the parentheses and argument to refer to\nthe column in context. You can only use DEFAULT as a bare keyword if you are\nusing it alone without a surrounding expression or function.\n\nExamples\n--------\n\nSelect only non-default values for a column:\n\nSELECT i FROM t WHERE i != DEFAULT(i);\n\nUpdate values to be one greater than the default value:\n\nUPDATE t SET i = DEFAULT(i)+1 WHERE i < 100;\n\nWhen referring to the default value exactly in UPDATE or INSERT, you can omit\nthe argument:\n\nINSERT INTO t (i) VALUES (DEFAULT);\nUPDATE t SET i = DEFAULT WHERE i < 100;\n\nCREATE OR REPLACE TABLE t (\n i INT NOT NULL AUTO_INCREMENT,\n j INT NOT NULL,\n k INT DEFAULT 3,\n l INT NOT NULL DEFAULT 4,\n m INT,\n PRIMARY KEY (i)\n);\n\nDESC t;\n+-------+---------+------+-----+---------+----------------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+---------+------+-----+---------+----------------+\n| i | int(11) | NO | PRI | NULL | auto_increment |\n| j | int(11) | NO | | NULL | |\n| k | int(11) | YES | | 3 | |\n| l | int(11) | NO | | 4 | |\n| m | int(11) | YES | | NULL | |\n+-------+---------+------+-----+---------+----------------+\n\nINSERT INTO t (j) VALUES (1);\nINSERT INTO t (j,m) VALUES (2,2);\n ...
+[DEGREES]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from radians to degrees.\n\nThis is the converse of the RADIANS() function.\n\nExamples\n--------\n\nSELECT DEGREES(PI());\n+---------------+\n| DEGREES(PI()) |\n+---------------+\n| 180 |\n+---------------+\n\nSELECT DEGREES(PI() / 2);\n+-------------------+\n| DEGREES(PI() / 2) |\n+-------------------+\n| 90 |\n+-------------------+\n\nSELECT DEGREES(45);\n+-----------------+\n| DEGREES(45) |\n+-----------------+\n| 2578.3100780887 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/degrees/
+[DENSE_RANK]
+declaration=
+category=Window Functions
+description=DENSE_RANK() is a window function that displays the number of a given row,\nstarting at one and following the ORDER BY sequence of the window function,\nwith identical values receiving the same result. Unlike the RANK() function,\nthere are no skipped values if the preceding results are identical. It is also\nsimilar to the ROW_NUMBER() function except that in that function, identical\nvalues will receive a different row number for each result.\n\nExamples\n--------\n\nThe distinction between DENSE_RANK(), RANK() and ROW_NUMBER():\n\nCREATE TABLE student(course VARCHAR(10), mark int, name varchar(10));\n\nINSERT INTO student VALUES \n ('Maths', 60, 'Thulile'),\n ('Maths', 60, 'Pritha'),\n ('Maths', 70, 'Voitto'),\n ('Maths', 55, 'Chun'),\n ('Biology', 60, 'Bilal'),\n ('Biology', 70, 'Roger');\n\nSELECT \n RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS rank,\n DENSE_RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS dense_rank,\n ROW_NUMBER() OVER (PARTITION BY course ORDER BY mark DESC) AS row_num,\n course, mark, name\nFROM student ORDER BY course, mark DESC;\n+------+------------+---------+---------+------+---------+\n| rank | dense_rank | row_num | course | mark | name |\n+------+------------+---------+---------+------+---------+\n| 1 | 1 | 1 | Biology | 70 | Roger |\n| 2 | 2 | 2 | Biology | 60 | Bilal |\n| 1 | 1 | 1 | Maths | 70 | Voitto |\n| 2 | 2 | 2 | Maths | 60 | Thulile |\n| 2 | 2 | 3 | Maths | 60 | Pritha |\n| 4 | 3 | 4 | Maths | 55 | Chun |\n+------+------------+---------+---------+------+---------+\n\nURL: https://mariadb.com/kb/en/dense_rank/
+[DES_DECRYPT]
+declaration=crypt_str[,key_str]
+category=Encryption Functions
+description=Decrypts a string encrypted with DES_ENCRYPT(). If an error occurs, this\nfunction returns NULL.\n\nThis function works only if MariaDB has been configured with TLS support.\n\nIf no key_str argument is given, DES_DECRYPT() examines the first byte of the\nencrypted string to determine the DES key number that was used to encrypt the\noriginal string, and then reads the key from the DES key file to decrypt the\nmessage. For this to work, the user must have the SUPER privilege. The key\nfile can be specified with the --des-key-file server option.\n\nIf you pass this function a key_str argument, that string is used as the key\nfor decrypting the message.\n\nIf the crypt_str argument does not appear to be an encrypted string, MariaDB\nreturns the given crypt_str.\n\nURL: https://mariadb.com/kb/en/des_decrypt/
+[DES_ENCRYPT]
+declaration=str[,{key_num|key_str}]
+category=Encryption Functions
+description=Encrypts the string with the given key using the Triple-DES algorithm.\n\nThis function works only if MariaDB has been configured with TLS support.\n\nThe encryption key to use is chosen based on the second argument to\nDES_ENCRYPT(), if one was given. With no argument, the first key from the DES\nkey file is used. With a key_num argument, the given key number (0-9) from the\nDES key file is used. With a key_str argument, the given key string is used to\nencrypt str.\n\nThe key file can be specified with the --des-key-file server option.\n\nThe return string is a binary string where the first character is CHAR(128 |\nkey_num). If an error occurs, DES_ENCRYPT() returns NULL.\n\nThe 128 is added to make it easier to recognize an encrypted key. If you use a\nstring key, key_num is 127.\n\nThe string length for the result is given by this formula:\n\nnew_len = orig_len + (8 - (orig_len % 8)) + 1\n\nEach line in the DES key file has the following format:\n\nkey_num des_key_str\n\nEach key_num value must be a number in the range from 0 to 9. Lines in the\nfile may be in any order. des_key_str is the string that is used to encrypt\nthe message. There should be at least one space between the number and the\nkey. The first key is the default key that is used if you do not specify any\nkey argument to DES_ENCRYPT().\n\nYou can tell MariaDB to read new key values from the key file with the FLUSH\nDES_KEY_FILE statement. This requires the RELOAD privilege.\n\nOne benefit of having a set of default keys is that it gives applications a\nway to check for the existence of encrypted column values, without giving the\nend user the right to decrypt those values.\n\nExamples\n--------\n\nSELECT customer_address FROM customer_table \n WHERE crypted_credit_card = DES_ENCRYPT('credit_card_number');\n\nURL: https://mariadb.com/kb/en/des_encrypt/
+[DISJOINT]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether g1 is spatially disjoint from (does not\nintersect) g2.\n\nDISJOINT() tests the opposite relationship to INTERSECTS().\n\nDISJOINT() is based on the original MySQL implementation and uses object\nbounding rectangles, while ST_DISJOINT() uses object shapes.\n\nURL: https://mariadb.com/kb/en/disjoint/
+[DOUBLE]
+declaration=M,D
+category=Data Types
+description=A normal-size (double-precision) floating-point number (see FLOAT for a\nsingle-precision floating-point number).\n\nAllowable values are:\n\n* -1.7976931348623157E+308 to -2.2250738585072014E-308\n* 0\n* 2.2250738585072014E-308 to 1.7976931348623157E+308\n\nThese are the theoretical limits, based on the IEEE standard. The actual range\nmight be slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following the\ndecimal point. If M and D are omitted, values are stored to the limits allowed\nby the hardware. A double-precision floating-point number is accurate to\napproximately 15 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nZEROFILL, if specified, pads the number with zeros, up to the total number of\ndigits specified by M.\n\nREAL and DOUBLE PRECISION are synonyms, unless the REAL_AS_FLOAT SQL mode is\nenabled, in which case REAL is a synonym for FLOAT rather than DOUBLE.\n\nSee Floating Point Accuracy for issues when using floating-point numbers.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DOUBLE(5,0) zerofill);\n\nINSERT INTO t1 VALUES (1),(2),(3),(4);\n\nSELECT * FROM t1;\n+-------+\n| d |\n+-------+\n| 00001 |\n| 00002 |\n| 00003 |\n| 00004 |\n+-------+\n\nURL: https://mariadb.com/kb/en/double/
+[ELT]
+declaration=N, str1[, str2, str3,...]
+category=String Functions
+description=Takes a numeric argument and a series of string arguments. Returns the string\nthat corresponds to the given numeric position. For instance, it returns str1\nif N is 1, str2 if N is 2, and so on. If the numeric argument is a FLOAT,\nMariaDB rounds it to the nearest INTEGER. If the numeric argument is less than\n1, greater than the total number of arguments, or not a number, ELT() returns\nNULL. It must have at least two arguments.\n\nIt is complementary to the FIELD() function.\n\nExamples\n--------\n\nSELECT ELT(1, 'ej', 'Heja', 'hej', 'foo');\n+------------------------------------+\n| ELT(1, 'ej', 'Heja', 'hej', 'foo') |\n+------------------------------------+\n| ej |\n+------------------------------------+\n\nSELECT ELT(4, 'ej', 'Heja', 'hej', 'foo');\n+------------------------------------+\n| ELT(4, 'ej', 'Heja', 'hej', 'foo') |\n+------------------------------------+\n| foo |\n+------------------------------------+\n\nURL: https://mariadb.com/kb/en/elt/
+[ENCODE]
+declaration=str,pass_str
+category=Encryption Functions
+description=ENCODE is not considered cryptographically secure, and should not be used for\npassword encryption.\n\nEncrypt str using pass_str as the password. To decrypt the result, use\nDECODE().\n\nThe result is a binary string of the same length as str.\n\nThe strength of the encryption is based on how good the random generator is.\n\nIt is not recommended to rely on the encryption performed by the ENCODE\nfunction. Using a salt value (changed when a password is updated) will improve\nmatters somewhat, but for storing passwords, consider a more cryptographically\nsecure function, such as SHA2().\n\nExamples\n--------\n\nENCODE('not so secret text', CONCAT('random_salt','password'))\n\nURL: https://mariadb.com/kb/en/encode/
+[ENCRYPT]
+declaration=str[,salt]
+category=Encryption Functions
+description=Encrypts a string using the Unix crypt() system call, returning an encrypted\nbinary string. The salt argument should be a string with at least two\ncharacters or the returned result will be NULL. If no salt argument is given,\na random value of sufficient length is used.\n\nIt is not recommended to use ENCRYPT() with utf16, utf32 or ucs2 multi-byte\ncharacter sets because the crypt() system call expects a string terminated\nwith a zero byte.\n\nNote that the underlying crypt() system call may have some limitations, such\nas ignoring all but the first eight characters.\n\nIf the have_crypt system variable is set to NO (because the crypt() system\ncall is not available), the ENCRYPT function will always return NULL.\n\nExamples\n--------\n\nSELECT ENCRYPT('encrypt me');\n+-----------------------+\n| ENCRYPT('encrypt me') |\n+-----------------------+\n| 4I5BsEx0lqTDk |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/encrypt/
+[ENUM]
+declaration='value1','value2',...
+category=Data Types
+description=An enumeration. A string object that can have only one value, chosen from the\nlist of values 'value1', 'value2', ..., NULL or the special '' error value. In\ntheory, an ENUM column can have a maximum of 65,535 distinct values; in\npractice, the real maximum depends on many factors. ENUM values are\nrepresented internally as integers.\n\nTrailing spaces are automatically stripped from ENUM values on table creation.\n\nENUMs require relatively little storage space compared to strings, either one\nor two bytes depending on the number of enumeration values.\n\nNULL and empty values\n---------------------\n\nAn ENUM can also contain NULL and empty values. If the ENUM column is declared\nto permit NULL values, NULL becomes a valid value, as well as the default\nvalue (see below). If strict SQL Mode is not enabled, and an invalid value is\ninserted into an ENUM, a special empty string, with an index value of zero\n(see Numeric index, below), is inserted, with a warning. This may be\nconfusing, because the empty string is also a possible value, and the only\ndifference if that is this case its index is not 0. Inserting will fail with\nan error if strict mode is active.\n\nIf a DEFAULT clause is missing, the default value will be:\n\n* NULL if the column is nullable;\n* otherwise, the first value in the enumeration.\n\nNumeric index\n-------------\n\nENUM values are indexed numerically in the order they are defined, and sorting\nwill be performed in this numeric order. We suggest not using ENUM to store\nnumerals, as there is little to no storage space benefit, and it is easy to\nconfuse the enum integer with the enum numeral value by leaving out the quotes.\n\nAn ENUM defined as ENUM('apple','orange','pear') would have the following\nindex values:\n\n+--------------------------------------+--------------------------------------+\n| Index | Value |\n+--------------------------------------+--------------------------------------+\n| NULL | NULL |\n+--------------------------------------+--------------------------------------+\n| 0 | '' |\n+--------------------------------------+--------------------------------------+\n| 1 | 'apple' |\n+--------------------------------------+--------------------------------------+\n| 2 | 'orange' |\n+--------------------------------------+--------------------------------------+\n ...
+[EQUALS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether g1 is spatially equal to g2.\n\nEQUALS() is based on the original MySQL implementation and uses object\nbounding rectangles, while ST_EQUALS() uses object shapes.\n\nFrom MariaDB 10.2.3, MBREQUALS is a synonym for Equals.\n\nURL: https://mariadb.com/kb/en/equals/
+[EXCEPT]
+declaration=SELECT c_name AS name, email FROM employees
+category=Data Manipulation
+description=Difference between UNION, EXCEPT and INTERSECT. INTERSECT ALL and EXCEPT ALL\nare available from MariaDB 10.5.0.\n\nCREATE TABLE seqs (i INT);\nINSERT INTO seqs VALUES (1),(2),(2),(3),(3),(4),(5),(6);\n\nSELECT i FROM seqs WHERE i <= 3 UNION SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n| 3 |\n| 3 |\n| 3 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 EXCEPT ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 2 |\n+------+\n ...
+[EXP]
+declaration=X
+category=Numeric Functions
+description=Returns the value of e (the base of natural logarithms) raised to the power of\nX. The inverse of this function is LOG() (using a single argument only) or\nLN().\n\nIf X is NULL, this function returns NULL.\n\nExamples\n--------\n\nSELECT EXP(2);\n+------------------+\n| EXP(2) |\n+------------------+\n| 7.38905609893065 |\n+------------------+\n\nSELECT EXP(-2);\n+--------------------+\n| EXP(-2) |\n+--------------------+\n| 0.1353352832366127 |\n+--------------------+\n\nSELECT EXP(0);\n+--------+\n| EXP(0) |\n+--------+\n| 1 |\n+--------+\n\nSELECT EXP(NULL);\n+-----------+\n| EXP(NULL) |\n+-----------+\n| NULL |\n+-----------+\n\nURL: https://mariadb.com/kb/en/exp/
+[EXPORT_SET]
+declaration=bits, on, off[, separator[, number_of_bits]]
+category=String Functions
+description=Takes a minimum of three arguments. Returns a string where each bit in the\ngiven bits argument is returned, with the string values given for on and off.\n\nBits are examined from right to left, (from low-order to high-order bits).\nStrings are added to the result from left to right, separated by a separator\nstring (defaults as ','). You can optionally limit the number of bits the\nEXPORT_SET() function examines using the number_of_bits option.\n\nIf any of the arguments are set as NULL, the function returns NULL.\n\nExamples\n--------\n\nSELECT EXPORT_SET(5,'Y','N',',',4);\n+-----------------------------+\n| EXPORT_SET(5,'Y','N',',',4) |\n+-----------------------------+\n| Y,N,Y,N |\n+-----------------------------+\n\nSELECT EXPORT_SET(6,'1','0',',',10);\n+------------------------------+\n| EXPORT_SET(6,'1','0',',',10) |\n+------------------------------+\n| 0,1,1,0,0,0,0,0,0,0 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/export_set/
+[EXTRACT]
+declaration=unit FROM date
+category=Date and Time Functions
+description=The EXTRACT() function extracts the required unit from the date. See Date and\nTime Units for a complete list of permitted units.\n\nIn MariaDB 10.0.7 and MariaDB 5.5.35, EXTRACT (HOUR FROM ...) was changed to\nreturn a value from 0 to 23, adhering to the SQL standard. Until MariaDB\n10.0.6 and MariaDB 5.5.34, and in all versions of MySQL at least as of MySQL\n5.7, it could return a value > 23. HOUR() is not a standard function, so\ncontinues to adhere to the old behaviour inherited from MySQL.\n\nExamples\n--------\n\nSELECT EXTRACT(YEAR FROM '2009-07-02');\n+---------------------------------+\n| EXTRACT(YEAR FROM '2009-07-02') |\n+---------------------------------+\n| 2009 |\n+---------------------------------+\n\nSELECT EXTRACT(YEAR_MONTH FROM '2009-07-02 01:02:03');\n+------------------------------------------------+\n| EXTRACT(YEAR_MONTH FROM '2009-07-02 01:02:03') |\n+------------------------------------------------+\n| 200907 |\n+------------------------------------------------+\n\nSELECT EXTRACT(DAY_MINUTE FROM '2009-07-02 01:02:03');\n+------------------------------------------------+\n| EXTRACT(DAY_MINUTE FROM '2009-07-02 01:02:03') |\n+------------------------------------------------+\n| 20102 |\n+------------------------------------------------+\n\nSELECT EXTRACT(MICROSECOND FROM '2003-01-02 10:30:00.000123');\n+--------------------------------------------------------+\n| EXTRACT(MICROSECOND FROM '2003-01-02 10:30:00.000123') |\n+--------------------------------------------------------+\n| 123 |\n+--------------------------------------------------------+\n\nFrom MariaDB 10.0.7 and MariaDB 5.5.35, EXTRACT (HOUR FROM...) returns a value\nfrom 0 to 23, as per the SQL standard. HOUR is not a standard function, so\ncontinues to adhere to the old behaviour inherited from MySQL.\n\nSELECT EXTRACT(HOUR FROM '26:30:00'), HOUR('26:30:00');\n+-------------------------------+------------------+\n| EXTRACT(HOUR FROM '26:30:00') | HOUR('26:30:00') |\n+-------------------------------+------------------+\n| 2 | 26 |\n+-------------------------------+------------------+\n\nURL: https://mariadb.com/kb/en/extract/
+[EXTRACTVALUE]
+declaration=xml_frag, xpath_expr
+category=String Functions
+description=The EXTRACTVALUE() function takes two string arguments: a fragment of XML\nmarkup and an XPath expression, (also known as a locator). It returns the text\n(That is, CDDATA), of the first text node which is a child of the element or\nelements matching the XPath expression.\n\nIn cases where a valid XPath expression does not match any text nodes in a\nvalid XML fragment, (including the implicit /text() expression), the\nEXTRACTVALUE() function returns an empty string.\n\nInvalid Arguments\n-----------------\n\nWhen either the XML fragment or the XPath expression is NULL, the\nEXTRACTVALUE() function returns NULL. When the XML fragment is invalid, it\nraises a warning Code 1525:\n\nWarning (Code 1525): Incorrect XML value: 'parse error at line 1 pos 11:\nunexpected END-OF-INPUT'\n\nWhen the XPath value is invalid, it generates an Error 1105:\n\nERROR 1105 (HY000): XPATH syntax error: ')'\n\nExplicit text() Expressions\n---------------------------\n\nThis function is the equivalent of performing a match using the XPath\nexpression after appending /text(). In other words:\n\nSELECT\n EXTRACTVALUE('example', '/cases/case')\n AS 'Base Example',\n EXTRACTVALUE('example', '/cases/case/text()')\n AS 'text() Example';\n+--------------+----------------+\n| Base Example | text() Example |\n+--------------+----------------+\n| example | example |\n+--------------+----------------+\n\nCount Matches\n-------------\n\nWhen EXTRACTVALUE() returns multiple matches, it returns the content of the\nfirst child text node of each matching element, in the matched order, as a\nsingle, space-delimited string.\n\nBy design, the EXTRACTVALUE() function makes no distinction between a match on\nan empty element and no match at all. If you need to determine whether no\nmatching element was found in the XML fragment or if an element was found that\n ...
+[FIELD]
+declaration=pattern, str1[,str2,...]
+category=String Functions
+description=Returns the index position of the string or number matching the given pattern.\nReturns 0 in the event that none of the arguments match the pattern. Raises an\nError 1582 if not given at least two arguments.\n\nWhen all arguments given to the FIELD() function are strings, they are treated\nas case-insensitive. When all the arguments are numbers, they are treated as\nnumbers. Otherwise, they are treated as doubles.\n\nIf the given pattern occurs more than once, the FIELD() function only returns\nthe index of the first instance. If the given pattern is NULL, the function\nreturns 0, as a NULL pattern always fails to match.\n\nThis function is complementary to the ELT() function.\n\nExamples\n--------\n\nSELECT FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo') \n AS 'Field Results';\n+---------------+\n| Field Results | \n+---------------+\n| 2 |\n+---------------+\n\nSELECT FIELD('fo', 'Hej', 'ej', 'Heja', 'hej', 'foo')\n AS 'Field Results';\n+---------------+\n| Field Results | \n+---------------+\n| 0 |\n+---------------+\n\nSELECT FIELD(1, 2, 3, 4, 5, 1) AS 'Field Results';\n+---------------+\n| Field Results |\n+---------------+\n| 5 |\n+---------------+\n\nSELECT FIELD(NULL, 2, 3) AS 'Field Results';\n+---------------+\n| Field Results |\n+---------------+\n| 0 |\n+---------------+\n\nSELECT FIELD('fail') AS 'Field Results';\nError 1582 (42000): Incorrect parameter count in call\nto native function 'field'\n\nURL: https://mariadb.com/kb/en/field/
+[FIND_IN_SET]
+declaration=pattern, strlist
+category=String Functions
+description=Returns the index position where the given pattern occurs in a string list.\nThe first argument is the pattern you want to search for. The second argument\nis a string containing comma-separated variables. If the second argument is of\nthe SET data-type, the function is optimized to use bit arithmetic.\n\nIf the pattern does not occur in the string list or if the string list is an\nempty string, the function returns 0. If either argument is NULL, the function\nreturns NULL. The function does not return the correct result if the pattern\ncontains a comma (",") character.\n\nExamples\n--------\n\nSELECT FIND_IN_SET('b','a,b,c,d') AS "Found Results";\n+---------------+\n| Found Results |\n+---------------+\n| 2 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/find_in_set/
+[FIRST_VALUE]
+declaration=expr
+category=Window Functions
+description=FIRST_VALUE returns the first result from an ordered set, or NULL if no such\nresult exists.\n\nExamples\n--------\n\nCREATE TABLE t1 (\n pk int primary key,\n a int,\n b int,\n c char(10),\n d decimal(10, 3),\n e real\n);\n\nINSERT INTO t1 VALUES\n( 1, 0, 1, 'one', 0.1, 0.001),\n( 2, 0, 2, 'two', 0.2, 0.002),\n( 3, 0, 3, 'three', 0.3, 0.003),\n( 4, 1, 2, 'three', 0.4, 0.004),\n( 5, 1, 1, 'two', 0.5, 0.005),\n( 6, 1, 1, 'one', 0.6, 0.006),\n( 7, 2, NULL, 'n_one', 0.5, 0.007),\n( 8, 2, 1, 'n_two', NULL, 0.008),\n( 9, 2, 2, NULL, 0.7, 0.009),\n(10, 2, 0, 'n_four', 0.8, 0.010),\n(11, 2, 10, NULL, 0.9, NULL);\n\nSELECT pk, FIRST_VALUE(pk) OVER (ORDER BY pk) AS first_asc,\n LAST_VALUE(pk) OVER (ORDER BY pk) AS last_asc,\n FIRST_VALUE(pk) OVER (ORDER BY pk DESC) AS first_desc,\n LAST_VALUE(pk) OVER (ORDER BY pk DESC) AS last_desc\nFROM t1\nORDER BY pk DESC;\n\n+----+-----------+----------+------------+-----------+\n| pk | first_asc | last_asc | first_desc | last_desc |\n+----+-----------+----------+------------+-----------+\n| 11 | 1 | 11 | 11 | 11 |\n| 10 | 1 | 10 | 11 | 10 |\n| 9 | 1 | 9 | 11 | 9 |\n| 8 | 1 | 8 | 11 | 8 |\n| 7 | 1 | 7 | 11 | 7 |\n| 6 | 1 | 6 | 11 | 6 |\n| 5 | 1 | 5 | 11 | 5 |\n| 4 | 1 | 4 | 11 | 4 |\n| 3 | 1 | 3 | 11 | 3 |\n| 2 | 1 | 2 | 11 | 2 |\n| 1 | 1 | 1 | 11 | 1 |\n+----+-----------+----------+------------+-----------+\n ...
+[FLOAT]
+declaration=M,D
+category=Data Types
+description=A small (single-precision) floating-point number (see DOUBLE for a\nregular-size floating point number). Allowable values are:\n\n* -3.402823466E+38 to -1.175494351E-38\n* 0\n* 1.175494351E-38 to 3.402823466E+38.\n\nThese are the theoretical limits, based on the IEEE standard. The actual range\nmight be slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following the\ndecimal point. If M and D are omitted, values are stored to the limits allowed\nby the hardware. A single-precision floating-point number is accurate to\napproximately 7 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nUsing FLOAT might give you some unexpected problems because all calculations\nin MariaDB are done with double precision. See Floating Point Accuracy.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nURL: https://mariadb.com/kb/en/float/
+[FLOOR]
+declaration=X
+category=Numeric Functions
+description=Returns the largest integer value not greater than X.\n\nExamples\n--------\n\nSELECT FLOOR(1.23);\n+-------------+\n| FLOOR(1.23) |\n+-------------+\n| 1 |\n+-------------+\n\nSELECT FLOOR(-1.23);\n+--------------+\n| FLOOR(-1.23) |\n+--------------+\n| -2 |\n+--------------+\n\nURL: https://mariadb.com/kb/en/floor/
+[FORMAT]
+declaration=num, decimal_position[, locale]
+category=String Functions
+description=Formats the given number for display as a string, adding separators to\nappropriate position and rounding the results to the given decimal position.\nFor instance, it would format 15233.345 to 15,233.35.\n\nIf the given decimal position is 0, it rounds to return no decimal point or\nfractional part. You can optionally specify a locale value to format numbers\nto the pattern appropriate for the given region.\n\nExamples\n--------\n\nSELECT FORMAT(1234567890.09876543210, 4) AS 'Format';\n+--------------------+\n| Format |\n+--------------------+\n| 1,234,567,890.0988 |\n+--------------------+\n\nSELECT FORMAT(1234567.89, 4) AS 'Format';\n+----------------+\n| Format |\n+----------------+\n| 1,234,567.8900 |\n+----------------+\n\nSELECT FORMAT(1234567.89, 0) AS 'Format';\n+-----------+\n| Format |\n+-----------+\n| 1,234,568 |\n+-----------+\n\nSELECT FORMAT(123456789,2,'rm_CH') AS 'Format';\n+----------------+\n| Format |\n+----------------+\n| 123'456'789,00 |\n+----------------+\n\nURL: https://mariadb.com/kb/en/format/
+[FORMAT_PICO_TIME]
+declaration=time_val
+category=Date and Time Functions
+description=Given a time in picoseconds, returns a human-readable time value and unit\nindicator. Resulting unit is dependent on the length of the argument, and can\nbe:\n\n* ps - picoseconds\n* ns - nanoseconds\n* us - microseconds\n* ms - milliseconds\n* s - seconds\n* min - minutes\n* h - hours\n* d - days\n\nWith the exception of results under one nanosecond, which are not rounded and\nare represented as whole numbers, the result is rounded to 2 decimal places,\nwith a minimum of 3 significant digits.\n\nReturns NULL if the argument is NULL.\n\nThis function is very similar to the Sys Schema FORMAT_TIME function, but with\nthe following differences:\n\n* Represents minutes as min rather than m.\n* Does not represent weeks.\n\nExamples\n--------\n\nSELECT\n FORMAT_PICO_TIME(43) AS ps,\n FORMAT_PICO_TIME(4321) AS ns,\n FORMAT_PICO_TIME(43211234) AS us,\n FORMAT_PICO_TIME(432112344321) AS ms,\n FORMAT_PICO_TIME(43211234432123) AS s,\n FORMAT_PICO_TIME(432112344321234) AS m,\n FORMAT_PICO_TIME(4321123443212345) AS h,\n FORMAT_PICO_TIME(432112344321234545) AS d;\n+--------+---------+----------+-----------+---------+----------+--------+------\n-+\n| ps | ns | us | ms | s | m | h | d \n |\n+--------+---------+----------+-----------+---------+----------+--------+------\n-+\n| 43 ps | 4.32 ns | 43.21 us | 432.11 ms | 43.21 s | 7.20 min | 1.20 h | 5.00\nd |\n+--------+---------+----------+-----------+---------+----------+--------+------\n-+\n\nURL: https://mariadb.com/kb/en/format_pico_time/
+[FOUND_ROWS]
+declaration=
+category=Information Functions
+description=A SELECT statement may include a LIMIT clause to restrict the number of rows\nthe server returns to the client. In some cases, it is desirable to know how\nmany rows the statement would have returned without the LIMIT, but without\nrunning the statement again. To obtain this row count, include a\nSQL_CALC_FOUND_ROWS option in the SELECT statement, and then invoke\nFOUND_ROWS() afterwards.\n\nYou can also use FOUND_ROWS() to obtain the number of rows returned by a\nSELECT which does not contain a LIMIT clause. In this case you don't need to\nuse the SQL_CALC_FOUND_ROWS option. This can be useful for example in a stored\nprocedure.\n\nAlso, this function works with some other statements which return a resultset,\nincluding SHOW, DESC and HELP. For DELETE ... RETURNING you should use\nROW_COUNT(). It also works as a prepared statement, or after executing a\nprepared statement.\n\nStatements which don't return any results don't affect FOUND_ROWS() - the\nprevious value will still be returned.\n\nWarning: When used after a CALL statement, this function returns the number of\nrows selected by the last query in the procedure, not by the whole procedure.\n\nStatements using the FOUND_ROWS() function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nSHOW ENGINES\G\n*************************** 1. row ***************************\n Engine: CSV\n Support: YES\n Comment: Stores tables as CSV files\nTransactions: NO\n XA: NO\n Savepoints: NO\n*************************** 2. row ***************************\n Engine: MRG_MyISAM\n Support: YES\n Comment: Collection of identical MyISAM tables\nTransactions: NO\n XA: NO\n Savepoints: NO\n\n...\n\n*************************** 8. row ***************************\n Engine: PERFORMANCE_SCHEMA\n Support: YES\n ...
+[FROM_BASE64]
+declaration=str
+category=String Functions
+description=Decodes the given base-64 encode string, returning the result as a binary\nstring. Returns NULL if the given string is NULL or if it's invalid.\n\nIt is the reverse of the TO_BASE64 function.\n\nThere are numerous methods to base-64 encode a string. MariaDB uses the\nfollowing:\n\n* It encodes alphabet value 64 as '+'.\n* It encodes alphabet value 63 as '/'.\n* It codes output in groups of four printable characters. Each three byte of\ndata encoded uses four characters. If the final group is incomplete, it pads\nthe difference with the '=' character.\n* It divides long output, adding a new line very 76 characters.\n* In decoding, it recognizes and ignores newlines, carriage returns, tabs and\nspace whitespace characters.\n\nSELECT TO_BASE64('Maria') AS 'Input';\n+-----------+\n| Input |\n+-----------+\n| TWFyaWE= |\n+-----------+\n\nSELECT FROM_BASE64('TWFyaWE=') AS 'Output';\n+--------+\n| Output |\n+--------+\n| Maria |\n+--------+\n\nURL: https://mariadb.com/kb/en/from_base64/
+[FROM_DAYS]
+declaration=N
+category=Date and Time Functions
+description=Given a day number N, returns a DATE value. The day count is based on the\nnumber of days from the start of the standard calendar (0000-00-00).\n\nThe function is not designed for use with dates before the advent of the\nGregorian calendar in October 1582. Results will not be reliable since it\ndoesn't account for the lost days when the calendar changed from the Julian\ncalendar.\n\nThis is the converse of the TO_DAYS() function.\n\nExamples\n--------\n\nSELECT FROM_DAYS(730669);\n+-------------------+\n| FROM_DAYS(730669) |\n+-------------------+\n| 2000-07-03 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/from_days/
+[FROM_UNIXTIME]
+declaration=unix_timestamp
+category=Date and Time Functions
+description=Returns a representation of the unix_timestamp argument as a value in\n'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS.uuuuuu format, depending on whether\nthe function is used in a string or numeric context. The value is expressed in\nthe current time zone. unix_timestamp is an internal timestamp value such as\nis produced by the UNIX_TIMESTAMP() function.\n\nIf format is given, the result is formatted according to the format string,\nwhich is used the same way as listed in the entry for the DATE_FORMAT()\nfunction.\n\nTimestamps in MariaDB have a maximum value of 2147483647, equivalent to\n2038-01-19 05:14:07. This is due to the underlying 32-bit limitation. Using\nthe function on a timestamp beyond this will result in NULL being returned.\nUse DATETIME as a storage type if you require dates beyond this.\n\nThe options that can be used by FROM_UNIXTIME(), as well as DATE_FORMAT() and\nSTR_TO_DATE(), are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix 'th', 'nd', 'st' or |\n| | 'rd''. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n+---------------------------+------------------------------------------------+\n ...
+[GEOMETRYCOLLECTION]
+declaration=g1,g2,...
+category=Geometry Constructors
+description=Constructs a WKB GeometryCollection. If any argument is not a well-formed WKB\nrepresentation of a geometry, the return value is NULL.\n\nExamples\n--------\n\nCREATE TABLE gis_geometrycollection (g GEOMETRYCOLLECTION);\nSHOW FIELDS FROM gis_geometrycollection;\nINSERT INTO gis_geometrycollection VALUES\n (GeomCollFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0,10\n10))')),\n (GeometryFromWKB(AsWKB(GeometryCollection(Point(44, 6),\nLineString(Point(3, 6), Point(7, 9)))))),\n (GeomFromText('GeometryCollection()')),\n (GeomFromText('GeometryCollection EMPTY'));\n\nURL: https://mariadb.com/kb/en/geometrycollection/
+[GET_FORMAT]
+declaration={DATE|DATETIME|TIME}, {'EUR'|'USA'|'JIS'|'ISO'|'INTERNAL'}
+category=Date and Time Functions
+description=Returns a format string. This function is useful in combination with the\nDATE_FORMAT() and the STR_TO_DATE() functions.\n\nPossible result formats are:\n\n+--------------------------------------+--------------------------------------+\n| Function Call | Result Format |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATE,'EUR') | '%d.%m.%Y' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATE,'USA') | '%m.%d.%Y' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATE,'JIS') | '%Y-%m-%d' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATE,'ISO') | '%Y-%m-%d' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATE,'INTERNAL') | '%Y%m%d' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATETIME,'EUR') | '%Y-%m-%d %H.%i.%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATETIME,'USA') | '%Y-%m-%d %H.%i.%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATETIME,'JIS') | '%Y-%m-%d %H:%i:%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATETIME,'ISO') | '%Y-%m-%d %H:%i:%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(DATETIME,'INTERNAL') | '%Y%m%d%H%i%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(TIME,'EUR') | '%H.%i.%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(TIME,'USA') | '%h:%i:%s %p' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(TIME,'JIS') | '%H:%i:%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(TIME,'ISO') | '%H:%i:%s' |\n+--------------------------------------+--------------------------------------+\n| GET_FORMAT(TIME,'INTERNAL') | '%H%i%s' |\n+--------------------------------------+--------------------------------------+\n\nExamples\n--------\n\nObtaining the string matching to the standard European date format:\n\nSELECT GET_FORMAT(DATE, 'EUR');\n+-------------------------+\n| GET_FORMAT(DATE, 'EUR') |\n+-------------------------+\n| %d.%m.%Y |\n+-------------------------+\n ...
+[GET_LOCK]
+declaration=str,timeout
+category=Miscellaneous Functions
+description=Tries to obtain a lock with a name given by the string str, using a timeout of\ntimeout seconds. Returns 1 if the lock was obtained successfully, 0 if the\nattempt timed out (for example, because another client has previously locked\nthe name), or NULL if an error occurred (such as running out of memory or the\nthread was killed with mariadb-admin kill).\n\nA lock is released with RELEASE_LOCK(), when the connection terminates (either\nnormally or abnormally). A connection can hold multiple locks at the same\ntime, so a lock that is no longer needed needs to be explicitly released.\n\nThe IS_FREE_LOCK function returns whether a specified lock a free or not, and\nthe IS_USED_LOCK whether the function is in use or not.\n\nLocks obtained with GET_LOCK() do not interact with transactions. That is,\ncommitting a transaction does not release any such locks obtained during the\ntransaction.\n\nIt is also possible to recursively set the same lock. If a lock with the same\nname is set n times, it needs to be released n times as well.\n\nstr is case insensitive for GET_LOCK() and related functions. If str is an\nempty string or NULL, GET_LOCK() returns NULL and does nothing. timeout\nsupports microseconds.\n\nIf the metadata_lock_info plugin is installed, locks acquired with this\nfunction are visible in the Information Schema METADATA_LOCK_INFO table.\n\nThis function can be used to implement application locks or to simulate record\nlocks. Names are locked on a server-wide basis. If a name has been locked by\none client, GET_LOCK() blocks any request by another client for a lock with\nthe same name. This allows clients that agree on a given lock name to use the\nname to perform cooperative advisory locking. But be aware that it also allows\na client that is not among the set of cooperating clients to lock a name,\neither inadvertently or deliberately, and thus prevent any of the cooperating\nclients from locking that name. One way to reduce the likelihood of this is to\nuse lock names that are database-specific or application-specific. For\nexample, use lock names of the form db_name.str or app_name.str.\n\nStatements using the GET_LOCK function are not safe for statement-based\nreplication.\n\nThe patch to permit multiple locks was contributed by Konstantin "Kostja"\nOsipov (MDEV-3917).\n\nExamples\n--------\n\nSELECT GET_LOCK('lock1',10);\n+----------------------+\n| GET_LOCK('lock1',10) |\n ...
+[GLENGTH]
+declaration=ls
+category=LineString Properties
+description=Returns as a double-precision number the length of the LineString value ls in\nits associated spatial reference.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT GLength(GeomFromText(@ls));\n+----------------------------+\n| GLength(GeomFromText(@ls)) |\n+----------------------------+\n| 2.82842712474619 |\n+----------------------------+\n\nURL: https://mariadb.com/kb/en/glength/
+[GREATEST]
+declaration=value1,value2,...
+category=Comparison Operators
+description=With two or more arguments, returns the largest (maximum-valued) argument. The\narguments are compared using the same rules as for LEAST().\n\nExamples\n--------\n\nSELECT GREATEST(2,0);\n+---------------+\n| GREATEST(2,0) |\n+---------------+\n| 2 |\n+---------------+\n\nSELECT GREATEST(34.0,3.0,5.0,767.0);\n+------------------------------+\n| GREATEST(34.0,3.0,5.0,767.0) |\n+------------------------------+\n| 767.0 |\n+------------------------------+\n\nSELECT GREATEST('B','A','C');\n+-----------------------+\n| GREATEST('B','A','C') |\n+-----------------------+\n| C |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/greatest/
+[GROUP_CONCAT]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=This function returns a string result with the concatenated non-NULL values\nfrom a group. If any expr in GROUP_CONCAT evaluates to NULL, that tuple is not\npresent in the list returned by GROUP_CONCAT.\n\nIt returns NULL if all arguments are NULL, or there are no matching rows.\n\nThe maximum returned length in bytes is determined by the group_concat_max_len\nserver system variable, which defaults to 1M.\n\nIf group_concat_max_len <= 512, the return type is VARBINARY or VARCHAR;\notherwise, the return type is BLOB or TEXT. The choice between binary or\nnon-binary types depends from the input.\n\nThe full syntax is as follows:\n\nGROUP_CONCAT([DISTINCT] expr [,expr ...]\n [ORDER BY {unsigned_integer | col_name | expr}\n [ASC | DESC] [,col_name ...]]\n [SEPARATOR str_val]\n [LIMIT {[offset,] row_count | row_count OFFSET offset}])\n\nDISTINCT eliminates duplicate values from the output string.\n\nORDER BY determines the order of returned values.\n\nSEPARATOR specifies a separator between the values. The default separator is a\ncomma (,). It is possible to avoid using a separator by specifying an empty\nstring.\n\nLIMIT\n-----\n\nThe LIMIT clause can be used with GROUP_CONCAT. This was not possible prior to\nMariaDB 10.3.3.\n\nExamples\n--------\n\nSELECT student_name,\n GROUP_CONCAT(test_score)\n FROM student\n GROUP BY student_name;\n\nGet a readable list of MariaDB users from the mysql.user table:\n\nSELECT GROUP_CONCAT(DISTINCT User ORDER BY User SEPARATOR '\n')\n FROM mysql.user;\n\nIn the former example, DISTINCT is used because the same user may occur more\nthan once. The new line (\n) used as a SEPARATOR makes the results easier to\n ...
+[HEX]
+declaration=N_or_S
+category=String Functions
+description=If N_or_S is a number, returns a string representation of the hexadecimal\nvalue of N, where N is a longlong (BIGINT) number. This is equivalent to\nCONV(N,10,16).\n\nIf N_or_S is a string, returns a hexadecimal string representation of N_or_S\nwhere each byte of each character in N_or_S is converted to two hexadecimal\ndigits. If N_or_S is NULL, returns NULL. The inverse of this operation is\nperformed by the UNHEX() function.\n\nMariaDB starting with 10.5.0\n----------------------------\nHEX() with an INET6 argument returns a hexadecimal representation of the\nunderlying 16-byte binary string.\n\nExamples\n--------\n\nSELECT HEX(255);\n+----------+\n| HEX(255) |\n+----------+\n| FF |\n+----------+\n\nSELECT 0x4D617269614442;\n+------------------+\n| 0x4D617269614442 |\n+------------------+\n| MariaDB |\n+------------------+\n\nSELECT HEX('MariaDB');\n+----------------+\n| HEX('MariaDB') |\n+----------------+\n| 4D617269614442 |\n+----------------+\n\nFrom MariaDB 10.5.0:\n\nSELECT HEX(CAST('2001:db8::ff00:42:8329' AS INET6));\n+----------------------------------------------+\n| HEX(CAST('2001:db8::ff00:42:8329' AS INET6)) |\n+----------------------------------------------+\n| 20010DB8000000000000FF0000428329 |\n+----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/hex/
+[HOUR]
+declaration=time
+category=Date and Time Functions
+description=Returns the hour for time. The range of the return value is 0 to 23 for\ntime-of-day values. However, the range of TIME values actually is much larger,\nso HOUR can return values greater than 23.\n\nThe return value is always positive, even if a negative TIME value is provided.\n\nExamples\n--------\n\nSELECT HOUR('10:05:03');\n+------------------+\n| HOUR('10:05:03') |\n+------------------+\n| 10 |\n+------------------+\n\nSELECT HOUR('272:59:59');\n+-------------------+\n| HOUR('272:59:59') |\n+-------------------+\n| 272 |\n+-------------------+\n\nDifference between EXTRACT (HOUR FROM ...) (>= MariaDB 10.0.7 and MariaDB\n5.5.35) and HOUR:\n\nSELECT EXTRACT(HOUR FROM '26:30:00'), HOUR('26:30:00');\n+-------------------------------+------------------+\n| EXTRACT(HOUR FROM '26:30:00') | HOUR('26:30:00') |\n+-------------------------------+------------------+\n| 2 | 26 |\n+-------------------------------+------------------+\n\nURL: https://mariadb.com/kb/en/hour/
+[IFNULL]
+declaration=expr1,expr2
+category=Control Flow Functions
+description=If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2.\nIFNULL() returns a numeric or string value, depending on the context in which\nit is used.\n\nFrom MariaDB 10.3, NVL() is an alias for IFNULL().\n\nExamples\n--------\n\nSELECT IFNULL(1,0); \n+-------------+\n| IFNULL(1,0) |\n+-------------+\n| 1 |\n+-------------+\n\nSELECT IFNULL(NULL,10);\n+-----------------+\n| IFNULL(NULL,10) |\n+-----------------+\n| 10 |\n+-----------------+\n\nSELECT IFNULL(1/0,10);\n+----------------+\n| IFNULL(1/0,10) |\n+----------------+\n| 10.0000 |\n+----------------+\n\nSELECT IFNULL(1/0,'yes');\n+-------------------+\n| IFNULL(1/0,'yes') |\n+-------------------+\n| yes |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/ifnull/
+[IN]
+declaration=value,...
+category=Comparison Operators
+description=Returns 1 if expr is equal to any of the values in the IN list, else returns\n0. If all values are constants, they are evaluated according to the type of\nexpr and sorted. The search for the item then is done using a binary search.\nThis means IN is very quick if the IN value list consists entirely of\nconstants. Otherwise, type conversion takes place according to the rules\ndescribed at Type Conversion, but applied to all the arguments.\n\nIf expr is NULL, IN always returns NULL. If at least one of the values in the\nlist is NULL, and one of the comparisons is true, the result is 1. If at least\none of the values in the list is NULL and none of the comparisons is true, the\nresult is NULL.\n\nExamples\n--------\n\nSELECT 2 IN (0,3,5,7);\n+----------------+\n| 2 IN (0,3,5,7) |\n+----------------+\n| 0 |\n+----------------+\n\nSELECT 'wefwf' IN ('wee','wefwf','weg');\n+----------------------------------+\n| 'wefwf' IN ('wee','wefwf','weg') |\n+----------------------------------+\n| 1 |\n+----------------------------------+\n\nType conversion:\n\nSELECT 1 IN ('1', '2', '3');\n+----------------------+\n| 1 IN ('1', '2', '3') |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT NULL IN (1, 2, 3);\n+-------------------+\n| NULL IN (1, 2, 3) |\n+-------------------+\n| NULL |\n+-------------------+\n\nSELECT 1 IN (1, 2, NULL);\n+-------------------+\n| 1 IN (1, 2, NULL) |\n+-------------------+\n| 1 |\n ...
+[INET6_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address as a string, returns a binary string\nthat represents the numeric value of the address.\n\nNo trailing zone ID's or traling network masks are permitted. For IPv4\naddresses, or IPv6 addresses with IPv4 address parts, no classful addresses or\ntrailing port numbers are permitted and octal numbers are not supported.\n\nThe returned binary string will be VARBINARY(16) or VARBINARY(4) for IPv6 and\nIPv4 addresses respectively.\n\nReturns NULL if the argument is not understood.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, INET6_ATON can take INET6 as an argument.\n\nExamples\n--------\n\nSELECT HEX(INET6_ATON('10.0.1.1'));\n+-----------------------------+\n| HEX(INET6_ATON('10.0.1.1')) |\n+-----------------------------+\n| 0A000101 |\n+-----------------------------+\n\nSELECT HEX(INET6_ATON('48f3::d432:1431:ba23:846f'));\n+----------------------------------------------+\n| HEX(INET6_ATON('48f3::d432:1431:ba23:846f')) |\n+----------------------------------------------+\n| 48F3000000000000D4321431BA23846F |\n+----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/inet6_aton/
+[INET6_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address as a numeric binary string, returns the\naddress as a nonbinary string in the connection character set.\n\nThe return string is lowercase, and is platform independent, since it does not\nuse functions specific to the operating system. It has a maximum length of 39\ncharacters.\n\nReturns NULL if the argument is not understood.\n\nExamples\n--------\n\nSELECT INET6_NTOA(UNHEX('0A000101'));\n+-------------------------------+\n| INET6_NTOA(UNHEX('0A000101')) |\n+-------------------------------+\n| 10.0.1.1 |\n+-------------------------------+\n\nSELECT INET6_NTOA(UNHEX('48F3000000000000D4321431BA23846F'));\n+-------------------------------------------------------+\n| INET6_NTOA(UNHEX('48F3000000000000D4321431BA23846F')) |\n+-------------------------------------------------------+\n| 48f3::d432:1431:ba23:846f |\n+-------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/inet6_ntoa/
+[INET_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given the dotted-quad representation of an IPv4 network address as a string,\nreturns an integer that represents the numeric value of the address. Addresses\nmay be 4- or 8-byte addresses.\n\nReturns NULL if the argument is not understood.\n\nExamples\n--------\n\nSELECT INET_ATON('192.168.1.1');\n+--------------------------+\n| INET_ATON('192.168.1.1') |\n+--------------------------+\n| 3232235777 |\n+--------------------------+\n\nThis is calculated as follows: 192 x 2563 + 168 x 256 2 + 1 x 256 + 1\n\nURL: https://mariadb.com/kb/en/inet_aton/
+[INET_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given a numeric IPv4 network address in network byte order (4 or 8 byte),\nreturns the dotted-quad representation of the address as a string.\n\nExamples\n--------\n\nSELECT INET_NTOA(3232235777);\n+-----------------------+\n| INET_NTOA(3232235777) |\n+-----------------------+\n| 192.168.1.1 |\n+-----------------------+\n\n192.168.1.1 corresponds to 3232235777 since 192 x 2563 + 168 x 256 2 + 1 x 256\n+ 1 = 3232235777\n\nURL: https://mariadb.com/kb/en/inet_ntoa/
+[INSTR]
+declaration=str,substr
+category=String Functions
+description=Returns the position of the first occurrence of substring substr in string\nstr. This is the same as the two-argument form of LOCATE(), except that the\norder of the arguments is reversed.\n\nINSTR() performs a case-insensitive search.\n\nIf any argument is NULL, returns NULL.\n\nExamples\n--------\n\nSELECT INSTR('foobarbar', 'bar');\n+---------------------------+\n| INSTR('foobarbar', 'bar') |\n+---------------------------+\n| 4 |\n+---------------------------+\n\nSELECT INSTR('My', 'Maria');\n+----------------------+\n| INSTR('My', 'Maria') |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/instr/
+[INT]
+declaration=M
+category=Data Types
+description=A normal-size integer. When marked UNSIGNED, it ranges from 0 to 4294967295,\notherwise its range is -2147483648 to 2147483647 (SIGNED is the default). If a\ncolumn has been set to ZEROFILL, all values will be prepended by zeros so that\nthe INT value contains a number of M digits. INTEGER is a synonym for INT.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nINT4 is a synonym for INT.\n\nFor details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE ints (a INT,b INT UNSIGNED,c INT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO ints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column 'b' at row 1\n\nINSERT INTO ints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column 'c' at row 1\n\nINSERT INTO ints VALUES (-10,10,10);\n\nINSERT INTO ints VALUES (2147483648,2147483648,2147483648);\nERROR 1264 (22003): Out of range value for column 'a' at row 1\n\nINSERT INTO ints VALUES (2147483647,2147483648,2147483648);\n\nSELECT * FROM ints;\n+------------+------------+------------+\n| a | b | c |\n+------------+------------+------------+\n| -10 | 10 | 0000000010 |\n| 2147483647 | 2147483648 | 2147483648 |\n+------------+------------+------------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO ints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.10 sec)\nWarning (Code 1264): Out of range value for column 'b' at row 1\nWarning (Code 1264): Out of range value for column 'c' at row 1\n\nINSERT INTO ints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\nWarning (Code 1264): Out of range value for column 'c' at row 1\n ...
+[INTERSECT]
+declaration=as well as EXCEPT
+category=Data Manipulation
+description=MariaDB 10.3.\n\nAll behavior for naming columns, ORDER BY and LIMIT is the same as for UNION.\n\nINTERSECT implicitly supposes a DISTINCT operation.\n\nThe result of an intersect is the intersection of right and left SELECT\nresults, i.e. only records that are present in both result sets will be\nincluded in the result of the operation.\n\nINTERSECT has higher precedence than UNION and EXCEPT (unless running running\nin Oracle mode, in which case all three have the same precedence). If possible\nit will be executed linearly but if not it will be translated to a subquery in\nthe FROM clause:\n\n(select a,b from t1)\nunion\n(select c,d from t2)\nintersect\n(select e,f from t3)\nunion\n(select 4,4);\n\nwill be translated to:\n\n(select a,b from t1)\nunion\nselect c,d from\n ((select c,d from t2)\n intersect\n (select e,f from t3)) dummy_subselect\nunion\n(select 4,4)\n\nMariaDB starting with 10.4.0\n----------------------------\n\nParentheses\n-----------\n\nFrom MariaDB 10.4.0, parentheses can be used to specify precedence. Before\nthis, a syntax error would be returned.\n\nMariaDB starting with 10.5.0\n----------------------------\n\nALL/DISTINCT\n------------\n\nINTERSECT ALL and INTERSECT DISTINCT were introduced in MariaDB 10.5.0. The\n ...
+[INTERSECTS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 spatially intersects geometry\ng2.\n\nINTERSECTS() is based on the original MySQL implementation and uses object\nbounding rectangles, while ST_INTERSECTS() uses object shapes.\n\nINTERSECTS() tests the opposite relationship to DISJOINT().\n\nURL: https://mariadb.com/kb/en/intersects/
+[INTERVAL]
+declaration=N,N1,N2,N3,...
+category=Comparison Operators
+description=Returns the index of the last argument that is less than the first argument or\nis NULL.\n\nReturns 0 if N < N1, 1 if N < N2, 2 if N < N3 and so on or -1 if N is NULL.\nAll arguments are treated as integers. It is required that N1 < N2 < N3 < ...\n< Nn for this function to work correctly. This is because a fast binary search\nis used.\n\nExamples\n--------\n\nSELECT INTERVAL(23, 1, 15, 17, 30, 44, 200);\n+--------------------------------------+\n| INTERVAL(23, 1, 15, 17, 30, 44, 200) |\n+--------------------------------------+\n| 3 |\n+--------------------------------------+\n\nSELECT INTERVAL(10, 1, 10, 100, 1000);\n+--------------------------------+\n| INTERVAL(10, 1, 10, 100, 1000) |\n+--------------------------------+\n| 2 |\n+--------------------------------+\n\nSELECT INTERVAL(22, 23, 30, 44, 200);\n+-------------------------------+\n| INTERVAL(22, 23, 30, 44, 200) |\n+-------------------------------+\n| 0 |\n+-------------------------------+\n\nSELECT INTERVAL(10, 2, NULL);\n+-----------------------+\n| INTERVAL(10, 2, NULL) |\n+-----------------------+\n| 2 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/interval/
+[ISNULL]
+declaration=expr
+category=Comparison Operators
+description=If expr is NULL, ISNULL() returns 1, otherwise it returns 0.\n\nSee also NULL Values in MariaDB.\n\nExamples\n--------\n\nSELECT ISNULL(1+1);\n+-------------+\n| ISNULL(1+1) |\n+-------------+\n| 0 |\n+-------------+\n\nSELECT ISNULL(1/0);\n+-------------+\n| ISNULL(1/0) |\n+-------------+\n| 1 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/isnull/
+[IS_FREE_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Checks whether the lock named str is free to use (that is, not locked).\nReturns 1 if the lock is free (no one is using the lock), 0 if the lock is in\nuse, and NULL if an error occurs (such as an incorrect argument, like an empty\nstring or NULL). str is case insensitive.\n\nIf the metadata_lock_info plugin is installed, the Information Schema\nmetadata_lock_info table contains information about locks of this kind (as\nwell as metadata locks).\n\nStatements using the IS_FREE_LOCK function are not safe for statement-based\nreplication.\n\nURL: https://mariadb.com/kb/en/is_free_lock/
+[IS_IPV4]
+declaration=expr
+category=Miscellaneous Functions
+description=If the expression is a valid IPv4 address, returns 1, otherwise returns 0.\n\nIS_IPV4() is stricter than INET_ATON(), but as strict as INET6_ATON(), in\ndetermining the validity of an IPv4 address. This implies that if IS_IPV4\nreturns 1, the same expression will always return a non-NULL result when\npassed to INET_ATON(), but that the reverse may not apply.\n\nExamples\n--------\n\nSELECT IS_IPV4('1110.0.1.1');\n+-----------------------+\n| IS_IPV4('1110.0.1.1') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nSELECT IS_IPV4('48f3::d432:1431:ba23:846f');\n+--------------------------------------+\n| IS_IPV4('48f3::d432:1431:ba23:846f') |\n+--------------------------------------+\n| 0 |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4/
+[IS_IPV4_COMPAT]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if a given numeric binary string IPv6 address, such as returned by\nINET6_ATON(), is IPv4-compatible, otherwise returns 0.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, when the argument is not INET6, automatic implicit CAST\nto INET6 is applied. As a consequence, IS_IPV4_COMPAT now understands\narguments in both text representation and binary(16) representation. Before\nMariaDB 10.5.0, the function understood only binary(16) representation.\n\nExamples\n--------\n\nSELECT IS_IPV4_COMPAT(INET6_ATON('::10.0.1.1'));\n+------------------------------------------+\n| IS_IPV4_COMPAT(INET6_ATON('::10.0.1.1')) |\n+------------------------------------------+\n| 1 |\n+------------------------------------------+\n\nSELECT IS_IPV4_COMPAT(INET6_ATON('::48f3::d432:1431:ba23:846f'));\n+-----------------------------------------------------------+\n| IS_IPV4_COMPAT(INET6_ATON('::48f3::d432:1431:ba23:846f')) |\n+-----------------------------------------------------------+\n| 0 |\n+-----------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4_compat/
+[IS_IPV4_MAPPED]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if a given a numeric binary string IPv6 address, such as returned by\nINET6_ATON(), is a valid IPv4-mapped address, otherwise returns 0.\n\nMariaDB starting with 10.5.0\n----------------------------\nFrom MariaDB 10.5.0, when the argument is not INET6, automatic implicit CAST\nto INET6 is applied. As a consequence, IS_IPV4_MAPPED now understands\narguments in both text representation and binary(16) representation. Before\nMariaDB 10.5.0, the function understood only binary(16) representation.\n\nExamples\n--------\n\nSELECT IS_IPV4_MAPPED(INET6_ATON('::10.0.1.1'));\n+------------------------------------------+\n| IS_IPV4_MAPPED(INET6_ATON('::10.0.1.1')) |\n+------------------------------------------+\n| 0 |\n+------------------------------------------+\n\nSELECT IS_IPV4_MAPPED(INET6_ATON('::ffff:10.0.1.1'));\n+-----------------------------------------------+\n| IS_IPV4_MAPPED(INET6_ATON('::ffff:10.0.1.1')) |\n+-----------------------------------------------+\n| 1 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv4_mapped/
+[IS_IPV6]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if the expression is a valid IPv6 address specified as a string,\notherwise returns 0. Does not consider IPv4 addresses to be valid IPv6\naddresses.\n\nExamples\n--------\n\nSELECT IS_IPV6('48f3::d432:1431:ba23:846f');\n+--------------------------------------+\n| IS_IPV6('48f3::d432:1431:ba23:846f') |\n+--------------------------------------+\n| 1 |\n+--------------------------------------+\n1 row in set (0.02 sec)\n\nSELECT IS_IPV6('10.0.1.1');\n+---------------------+\n| IS_IPV6('10.0.1.1') |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/is_ipv6/
+[IS_USED_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Checks whether the lock named str is in use (that is, locked). If so, it\nreturns the connection identifier of the client that holds the lock.\nOtherwise, it returns NULL. str is case insensitive.\n\nIf the metadata_lock_info plugin is installed, the Information Schema\nmetadata_lock_info table contains information about locks of this kind (as\nwell as metadata locks).\n\nStatements using the IS_USED_LOCK function are not safe for statement-based\nreplication.\n\nURL: https://mariadb.com/kb/en/is_used_lock/
+[JSON_ARRAY]
+declaration=[value[, value2] ...]
+category=JSON Functions
+description=Returns a JSON array containing the listed values. The list can be empty.\n\nExample\n-------\n\nSELECT Json_Array(56, 3.1416, 'My name is "Foo"', NULL);\n+--------------------------------------------------+\n| Json_Array(56, 3.1416, 'My name is "Foo"', NULL) |\n+--------------------------------------------------+\n| [56, 3.1416, "My name is \"Foo\"", null] |\n+--------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_array/
+[JSON_ARRAYAGG]
+declaration=column_or_expression
+category=JSON Functions
+description=JSON_ARRAYAGG returns a JSON array containing an element for each value in a\ngiven set of JSON or SQL values. It acts on a column or an expression that\nevaluates to a single value.\n\nThe maximum returned length in bytes is determined by the group_concat_max_len\nserver system variable.\n\nReturns NULL in the case of an error, or if the result contains no rows.\n\nJSON_ARRAYAGG cannot currently be used as a window function.\n\nThe full syntax is as follows:\n\nJSON_ARRAYAGG([DISTINCT] expr\n [ORDER BY {unsigned_integer | col_name | expr}\n [ASC | DESC] [,col_name ...]]\n [LIMIT {[offset,] row_count | row_count OFFSET offset}])\n\nExamples\n--------\n\nCREATE TABLE t1 (a INT, b INT);\n\nINSERT INTO t1 VALUES (1, 1),(2, 1), (1, 1),(2, 1), (3, 2),(2, 2),(2, 2),(2,\n2);\n\nSELECT JSON_ARRAYAGG(a), JSON_ARRAYAGG(b) FROM t1;\n+-------------------+-------------------+\n| JSON_ARRAYAGG(a) | JSON_ARRAYAGG(b) |\n+-------------------+-------------------+\n| [1,2,1,2,3,2,2,2] | [1,1,1,1,2,2,2,2] |\n+-------------------+-------------------+\n\nSELECT JSON_ARRAYAGG(a), JSON_ARRAYAGG(b) FROM t1 GROUP BY b;\n+------------------+------------------+\n| JSON_ARRAYAGG(a) | JSON_ARRAYAGG(b) |\n+------------------+------------------+\n| [1,2,1,2] | [1,1,1,1] |\n| [3,2,2,2] | [2,2,2,2] |\n+------------------+------------------+\n\nURL: https://mariadb.com/kb/en/json_arrayagg/
+[JSON_ARRAY_APPEND]
+declaration=json_doc, path, value[, path, value] ...
+category=JSON Functions
+description=Appends values to the end of the specified arrays within a JSON document,\nreturning the result, or NULL if any of the arguments are NULL.\n\nEvaluation is performed from left to right, with the resulting document from\nthe previous pair becoming the new value against which the next pair is\nevaluated.\n\nIf the json_doc is not a valid JSON document, or if any of the paths are not\nvalid, or contain a * or ** wildcard, an error is returned.\n\nExamples\n--------\n\nSET @json = '[1, 2, [3, 4]]';\n\nSELECT JSON_ARRAY_APPEND(@json, '$[0]', 5)\n+-------------------------------------+\n| JSON_ARRAY_APPEND(@json, '$[0]', 5) |\n+-------------------------------------+\n| [[1, 5], 2, [3, 4]] |\n+-------------------------------------+\n\nSELECT JSON_ARRAY_APPEND(@json, '$[1]', 6);\n+-------------------------------------+\n| JSON_ARRAY_APPEND(@json, '$[1]', 6) |\n+-------------------------------------+\n| [1, [2, 6], [3, 4]] |\n+-------------------------------------+\n\nSELECT JSON_ARRAY_APPEND(@json, '$[1]', 6, '$[2]', 7);\n+------------------------------------------------+\n| JSON_ARRAY_APPEND(@json, '$[1]', 6, '$[2]', 7) |\n+------------------------------------------------+\n| [1, [2, 6], [3, 4, 7]] |\n+------------------------------------------------+\n\nSELECT JSON_ARRAY_APPEND(@json, '$', 5);\n+----------------------------------+\n| JSON_ARRAY_APPEND(@json, '$', 5) |\n+----------------------------------+\n| [1, 2, [3, 4], 5] |\n+----------------------------------+\n\nSET @json = '{"A": 1, "B": [2], "C": [3, 4]}';\n\nSELECT JSON_ARRAY_APPEND(@json, '$.B', 5);\n+------------------------------------+\n| JSON_ARRAY_APPEND(@json, '$.B', 5) |\n+------------------------------------+\n| {"A": 1, "B": [2, 5], "C": [3, 4]} |\n+------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_array_append/
+[JSON_ARRAY_INSERT]
+declaration=json_doc, path, value[, path, value] ...
+category=JSON Functions
+description=Inserts a value into a JSON document, returning the modified document, or NULL\nif any of the arguments are NULL.\n\nEvaluation is performed from left to right, with the resulting document from\nthe previous pair becoming the new value against which the next pair is\nevaluated.\n\nIf the json_doc is not a valid JSON document, or if any of the paths are not\nvalid, or contain a * or ** wildcard, an error is returned.\n\nExamples\n--------\n\nSET @json = '[1, 2, [3, 4]]';\n\nSELECT JSON_ARRAY_INSERT(@json, '$[0]', 5);\n+-------------------------------------+\n| JSON_ARRAY_INSERT(@json, '$[0]', 5) |\n+-------------------------------------+\n| [5, 1, 2, [3, 4]] |\n+-------------------------------------+\n\nSELECT JSON_ARRAY_INSERT(@json, '$[1]', 6);\n+-------------------------------------+\n| JSON_ARRAY_INSERT(@json, '$[1]', 6) |\n+-------------------------------------+\n| [1, 6, 2, [3, 4]] |\n+-------------------------------------+\n\nSELECT JSON_ARRAY_INSERT(@json, '$[1]', 6, '$[2]', 7);\n+------------------------------------------------+\n| JSON_ARRAY_INSERT(@json, '$[1]', 6, '$[2]', 7) |\n+------------------------------------------------+\n| [1, 6, 7, 2, [3, 4]] |\n+------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_array_insert/
+[JSON_ARRAY_INTERSECT]
+declaration=arr1, arr2
+category=JSON Functions
+description=Finds intersection between two json arrays and returns an array of items found\nin both array.\n\nExamples\n--------\n\nSET @json1= '[1,2,3]';\nSET @json2= '[1,2,4]';\n\nSELECT json_array_intersect(@json1, @json2); \n+--------------------------------------+\n| json_array_intersect(@json1, @json2) |\n+--------------------------------------+\n| [1, 2] |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_array_intersect/
+[JSON_COMPACT]
+declaration=json_doc
+category=JSON Functions
+description=Removes all unnecessary spaces so the json document is as short as possible.\n\nExample\n-------\n\nSET @j = '{ "A": 1, "B": [2, 3]}';\n\nSELECT JSON_COMPACT(@j), @j;\n+-------------------+------------------------+\n| JSON_COMPACT(@j) | @j |\n+-------------------+------------------------+\n| {"A":1,"B":[2,3]} | { "A": 1, "B": [2, 3]} |\n+-------------------+------------------------+\n\nURL: https://mariadb.com/kb/en/json_compact/
+[JSON_CONTAINS]
+declaration=json_doc, val[, path]
+category=JSON Functions
+description=Returns whether or not the specified value is found in the given JSON document\nor, optionally, at the specified path within the document. Returns 1 if it\ndoes, 0 if not and NULL if any of the arguments are null. An error occurs if\nthe document or path is not valid, or contains the * or ** wildcards.\n\nExamples\n--------\n\nSET @json = '{"A": 0, "B": {"C": 1}, "D": 2}';\n\nSELECT JSON_CONTAINS(@json, '2', '$.A');\n+----------------------------------+\n| JSON_CONTAINS(@json, '2', '$.A') |\n+----------------------------------+\n| 0 |\n+----------------------------------+\n\nSELECT JSON_CONTAINS(@json, '2', '$.D');\n+----------------------------------+\n| JSON_CONTAINS(@json, '2', '$.D') |\n+----------------------------------+\n| 1 |\n+----------------------------------+\n\nSELECT JSON_CONTAINS(@json, '{"C": 1}', '$.A');\n+-----------------------------------------+\n| JSON_CONTAINS(@json, '{"C": 1}', '$.A') |\n+-----------------------------------------+\n| 0 |\n+-----------------------------------------+\n\nSELECT JSON_CONTAINS(@json, '{"C": 1}', '$.B');\n+-----------------------------------------+\n| JSON_CONTAINS(@json, '{"C": 1}', '$.B') |\n+-----------------------------------------+\n| 1 |\n+-----------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_contains/
+[JSON_CONTAINS_PATH]
+declaration=json_doc, return_arg, path[, path] ...
+category=JSON Functions
+description=Indicates whether the given JSON document contains data at the specified path\nor paths. Returns 1 if it does, 0 if not and NULL if any of the arguments are\nnull.\n\nThe return_arg can be one or all:\n\n* one - Returns 1 if at least one path exists within the JSON document. \n* all - Returns 1 only if all paths exist within the JSON document.\n\nExamples\n--------\n\nSET @json = '{"A": 1, "B": [2], "C": [3, 4]}';\n\nSELECT JSON_CONTAINS_PATH(@json, 'one', '$.A', '$.D');\n+------------------------------------------------+\n| JSON_CONTAINS_PATH(@json, 'one', '$.A', '$.D') |\n+------------------------------------------------+\n| 1 |\n+------------------------------------------------+\n1 row in set (0.00 sec)\n\nSELECT JSON_CONTAINS_PATH(@json, 'all', '$.A', '$.D');\n+------------------------------------------------+\n| JSON_CONTAINS_PATH(@json, 'all', '$.A', '$.D') |\n+------------------------------------------------+\n| 0 |\n+------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_contains_path/
+[JSON_DEPTH]
+declaration=json_doc
+category=JSON Functions
+description=Returns the maximum depth of the given JSON document, or NULL if the argument\nis null. An error will occur if the argument is an invalid JSON document.\n\n* Scalar values or empty arrays or objects have a depth of 1.\n* Arrays or objects that are not empty but contain only elements or member\nvalues of depth 1 will have a depth of 2.\n* In other cases, the depth will be greater than 2.\n\nExamples\n--------\n\nSELECT JSON_DEPTH('[]'), JSON_DEPTH('true'), JSON_DEPTH('{}');\n+------------------+--------------------+------------------+\n| JSON_DEPTH('[]') | JSON_DEPTH('true') | JSON_DEPTH('{}') |\n+------------------+--------------------+------------------+\n| 1 | 1 | 1 |\n+------------------+--------------------+------------------+\n\nSELECT JSON_DEPTH('[1, 2, 3]'), JSON_DEPTH('[[], {}, []]');\n+-------------------------+----------------------------+\n| JSON_DEPTH('[1, 2, 3]') | JSON_DEPTH('[[], {}, []]') |\n+-------------------------+----------------------------+\n| 2 | 2 |\n+-------------------------+----------------------------+\n\nSELECT JSON_DEPTH('[1, 2, [3, 4, 5, 6], 7]');\n+---------------------------------------+\n| JSON_DEPTH('[1, 2, [3, 4, 5, 6], 7]') |\n+---------------------------------------+\n| 3 |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_depth/
+[JSON_DETAILED]
+declaration=json_doc[, tab_size]
+category=JSON Functions
+description=Represents JSON in the most understandable way emphasizing nested structures.\n\nJSON_PRETTY was added as an alias for JSON_DETAILED in MariaDB 10.10.3,\nMariaDB 10.9.5, MariaDB 10.8.7, MariaDB 10.7.8, MariaDB 10.6.12, MariaDB\n10.5.19 and MariaDB 10.4.28.\n\nExample\n-------\n\nSET @j = '{ "A":1,"B":[2,3]}';\n\nSELECT @j;\n+--------------------+\n| @j |\n+--------------------+\n| { "A":1,"B":[2,3]} |\n+--------------------+\n\nSELECT JSON_DETAILED(@j);\n+------------------------------------------------------------+\n| JSON_DETAILED(@j) |\n+------------------------------------------------------------+\n| {\n "A": 1,\n "B":\n [\n 2,\n 3\n ]\n} |\n+------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_detailed/
+[JSON_EQUALS]
+declaration=json1, json2
+category=JSON Functions
+description=Checks if there is equality between two json objects. Returns 1 if it there\nis, 0 if not, or NULL if any of the arguments are null.\n\nExamples\n--------\n\nSELECT JSON_EQUALS('{"a" :[1, 2, 3],"b":[4]}', '{"b":[4],"a":[1, 2, 3.0]}');\n+------------------------------------------------------------------------+\n| JSON_EQUALS('{"a" :[1, 2, 3],"b":[4]}', '{"b":[4],"a":[1, 2, 3.0]}') |\n+------------------------------------------------------------------------+\n| 1 |\n+------------------------------------------------------------------------+\n\nSELECT JSON_EQUALS('{"a":[1, 2, 3]}', '{"a":[1, 2, 3.01]}');\n+------------------------------------------------------+\n| JSON_EQUALS('{"a":[1, 2, 3]}', '{"a":[1, 2, 3.01]}') |\n+------------------------------------------------------+\n| 0 |\n+------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_equals/
+[JSON_EXISTS]
+declaration='{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2"
+category=JSON Functions
+description=+------------------------------------------------------------+\n| JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2") |\n+------------------------------------------------------------+\n| 1 |\n+------------------------------------------------------------+\n\nSELECT JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key3");\n+------------------------------------------------------------+\n| JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key3") |\n+------------------------------------------------------------+\n| 0 |\n+------------------------------------------------------------+\n\nSELECT JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]");\n+---------------------------------------------------------------+\n| JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]") |\n+---------------------------------------------------------------+\n| 1 |\n+---------------------------------------------------------------+\n\nSELECT JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]");\n+----------------------------------------------------------------+\n| JSON_EXISTS('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]") |\n+----------------------------------------------------------------+\n| 0 |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_exists/
+[JSON_EXTRACT]
+declaration=json_doc, path[, path] ...
+category=JSON Functions
+description=Extracts data from a JSON document. The extracted data is selected from the\nparts matching the path arguments. Returns all matched values; either as a\nsingle matched value, or, if the arguments could return multiple values, a\nresult autowrapped as an array in the matching order.\n\nReturns NULL if no paths match or if any of the arguments are NULL.\n\nAn error will occur if any path argument is not a valid path, or if the\njson_doc argument is not a valid JSON document.\n\nThe path expression be a JSONPath expression as supported by MariaDB\n\nExamples\n--------\n\nSET @json = '[1, 2, [3, 4]]';\n\nSELECT JSON_EXTRACT(@json, '$[1]');\n+-----------------------------+\n| JSON_EXTRACT(@json, '$[1]') |\n+-----------------------------+\n| 2 |\n+-----------------------------+\n\nSELECT JSON_EXTRACT(@json, '$[2]');\n+-----------------------------+\n| JSON_EXTRACT(@json, '$[2]') |\n+-----------------------------+\n| [3, 4] |\n+-----------------------------+\n\nSELECT JSON_EXTRACT(@json, '$[2][1]');\n+--------------------------------+\n| JSON_EXTRACT(@json, '$[2][1]') |\n+--------------------------------+\n| 4 |\n+--------------------------------+\n\nURL: https://mariadb.com/kb/en/json_extract/
+[JSON_INSERT]
+declaration=json_doc, path, val[, path, val] ...
+category=JSON Functions
+description=Inserts data into a JSON document, returning the resulting document or NULL if\neither of the json_doc or path arguments are null.\n\nAn error will occur if the JSON document is invalid, or if any of the paths\nare invalid or contain a * or ** wildcard.\n\nJSON_INSERT can only insert data while JSON_REPLACE can only update. JSON_SET\ncan update or insert data.\n\nExamples\n--------\n\nSET @json = '{ "A": 0, "B": [1, 2]}';\n\nSELECT JSON_INSERT(@json, '$.C', '[3, 4]');\n+--------------------------------------+\n| JSON_INSERT(@json, '$.C', '[3, 4]') |\n+--------------------------------------+\n| { "A": 0, "B": [1, 2], "C":"[3, 4]"} |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_insert/
+[JSON_KEYS]
+declaration=json_doc[, path]
+category=JSON Functions
+description=Returns the keys as a JSON array from the top-level value of a JSON object or,\nif the optional path argument is provided, the top-level keys from the path.\n\nExcludes keys from nested sub-objects in the top level value. The resulting\narray will be empty if the selected object is empty.\n\nReturns NULL if any of the arguments are null, a given path does not locate an\nobject, or if the json_doc argument is not an object.\n\nAn error will occur if JSON document is invalid, the path is invalid or if the\npath contains a * or ** wildcard.\n\nExamples\n--------\n\nSELECT JSON_KEYS('{"A": 1, "B": {"C": 2}}');\n+--------------------------------------+\n| JSON_KEYS('{"A": 1, "B": {"C": 2}}') |\n+--------------------------------------+\n| ["A", "B"] |\n+--------------------------------------+\n\nSELECT JSON_KEYS('{"A": 1, "B": 2, "C": {"D": 3}}', '$.C');\n+-----------------------------------------------------+\n| JSON_KEYS('{"A": 1, "B": 2, "C": {"D": 3}}', '$.C') |\n+-----------------------------------------------------+\n| ["D"] |\n+-----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_keys/
+[JSON_LENGTH]
+declaration=json_doc[, path]
+category=JSON Functions
+description=Returns the length of a JSON document, or, if the optional path argument is\ngiven, the length of the value within the document specified by the path.\n\nReturns NULL if any of the arguments argument are null or the path argument\ndoes not identify a value in the document.\n\nAn error will occur if the JSON document is invalid, the path is invalid or if\nthe path contains a * or ** wildcard.\n\nLength will be determined as follow:\n\n* A scalar's length is always 1.\n* If an array, the number of elements in the array.\n* If an object, the number of members in the object.\n\nThe length of nested arrays or objects are not counted.\n\nExamples\n--------\n\nURL: https://mariadb.com/kb/en/json_length/
+[JSON_LOOSE]
+declaration=json_doc
+category=JSON Functions
+description=Adds spaces to a JSON document to make it look more readable.\n\nExample\n-------\n\nSET @j = '{ "A":1,"B":[2,3]}';\n\nSELECT JSON_LOOSE(@j), @j;\n+-----------------------+--------------------+\n| JSON_LOOSE(@j) | @j |\n+-----------------------+--------------------+\n| {"A": 1, "B": [2, 3]} | { "A":1,"B":[2,3]} |\n+-----------------------+--------------------+\n\nURL: https://mariadb.com/kb/en/json_loose/
+[JSON_MERGE]
+declaration=json_doc, json_doc[, json_doc] ...
+category=JSON Functions
+description=Merges the given JSON documents.\n\nReturns the merged result,or NULL if any argument is NULL.\n\nAn error occurs if any of the arguments are not valid JSON documents.\n\nJSON_MERGE has been deprecated since MariaDB 10.2.25, MariaDB 10.3.16 and\nMariaDB 10.4.5. JSON_MERGE_PATCH is an RFC 7396-compliant replacement, and\nJSON_MERGE_PRESERVE is a synonym.\n\nExample\n-------\n\nSET @json1 = '[1, 2]';\nSET @json2 = '[3, 4]';\n\nSELECT JSON_MERGE(@json1,@json2);\n+---------------------------+\n| JSON_MERGE(@json1,@json2) |\n+---------------------------+\n| [1, 2, 3, 4] |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/json_merge/
+[JSON_MERGE_PATCH]
+declaration=json_doc, json_doc[, json_doc] ...
+category=JSON Functions
+description=Merges the given JSON documents, returning the merged result, or NULL if any\nargument is NULL.\n\nJSON_MERGE_PATCH is an RFC 7396-compliant replacement for JSON_MERGE, which\nhas been deprecated.\n\nUnlike JSON_MERGE_PRESERVE, members with duplicate keys are not preserved.\n\nExample\n-------\n\nSET @json1 = '[1, 2]';\nSET @json2 = '[2, 3]';\nSELECT JSON_MERGE_PATCH(@json1,@json2),JSON_MERGE_PRESERVE(@json1,@json2);\n+---------------------------------+------------------------------------+\n| JSON_MERGE_PATCH(@json1,@json2) | JSON_MERGE_PRESERVE(@json1,@json2) |\n+---------------------------------+------------------------------------+\n| [2, 3] | [1, 2, 2, 3] |\n+---------------------------------+------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_merge_patch/
+[JSON_MERGE_PRESERVE]
+declaration=json_doc, json_doc[, json_doc] ...
+category=JSON Functions
+description=Merges the given JSON documents, returning the merged result, or NULL if any\nargument is NULL.\n\nJSON_MERGE_PRESERVE was introduced as a synonym for JSON_MERGE, which has been\ndeprecated.\n\nUnlike JSON_MERGE_PATCH, members with duplicate keys are preserved.\n\nExample\n-------\n\nSET @json1 = '[1, 2]';\nSET @json2 = '[2, 3]';\nSELECT JSON_MERGE_PATCH(@json1,@json2),JSON_MERGE_PRESERVE(@json1,@json2);\n+---------------------------------+------------------------------------+\n| JSON_MERGE_PATCH(@json1,@json2) | JSON_MERGE_PRESERVE(@json1,@json2) |\n+---------------------------------+------------------------------------+\n| [2, 3] | [1, 2, 2, 3] |\n+---------------------------------+------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_merge_preserve/
+[JSON_NORMALIZE]
+declaration=json
+category=JSON Functions
+description=Recursively sorts keys and removes spaces, allowing comparison of json\ndocuments for equality.\n\nExamples\n--------\n\nWe may wish our application to use the database to enforce a unique constraint\non the JSON contents, and we can do so using the JSON_NORMALIZE function in\ncombination with a unique key.\n\nFor example, if we have a table with a JSON column:\n\nCREATE TABLE t1 (\n id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,\n val JSON,\n /* other columns here */\n PRIMARY KEY (id)\n);\n\nAdd a unique constraint using JSON_NORMALIZE like this:\n\nALTER TABLE t1\n ADD COLUMN jnorm JSON AS (JSON_NORMALIZE(val)) VIRTUAL,\n ADD UNIQUE KEY (jnorm);\n\nWe can test this by first inserting a row as normal:\n\nINSERT INTO t1 (val) VALUES ('{"name":"alice","color":"blue"}');\n\nAnd then seeing what happens with a different string which would produce the\nsame JSON object:\n\nINSERT INTO t1 (val) VALUES ('{ "color": "blue", "name": "alice" }');\nERROR 1062 (23000): Duplicate entry '{"color":"blue","name":"alice"}' for key\n'jnorm'\n\nURL: https://mariadb.com/kb/en/json_normalize/
+[JSON_OBJECT]
+declaration=[key, value[, key, value] ...]
+category=JSON Functions
+description=Returns a JSON object containing the given key/value pairs. The key/value list\ncan be empty.\n\nAn error will occur if there are an odd number of arguments, or any key name\nis NULL.\n\nExample\n-------\n\nSELECT JSON_OBJECT("id", 1, "name", "Monty");\n+---------------------------------------+\n| JSON_OBJECT("id", 1, "name", "Monty") |\n+---------------------------------------+\n| {"id": 1, "name": "Monty"} |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_object/
+[JSON_OBJECTAGG]
+declaration=key, value
+category=JSON Functions
+description=JSON_OBJECTAGG returns a JSON object containing key-value pairs. It takes two\nexpressions that evaluate to a single value, or two column names, as\narguments, the first used as a key, and the second as a value.\n\nThe maximum returned length in bytes is determined by the group_concat_max_len\nserver system variable.\n\nReturns NULL in the case of an error, or if the result contains no rows.\n\nJSON_OBJECTAGG cannot currently be used as a window function.\n\nExamples\n--------\n\nselect * from t1;\n+------+-------+\n| a | b |\n+------+-------+\n| 1 | Hello |\n| 1 | World |\n| 2 | This |\n+------+-------+\n\nSELECT JSON_OBJECTAGG(a, b) FROM t1;\n+----------------------------------------+\n| JSON_OBJECTAGG(a, b) |\n+----------------------------------------+\n| {"1":"Hello", "1":"World", "2":"This"} |\n+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_objectagg/
+[JSON_OBJECT_FILTER_KEYS]
+declaration=obj, array_keys
+category=JSON Functions
+description=JSON_OBJECT_FILTER_KEYS returns a JSON object with keys from the object that\nare also present in the array as string. It is used when one wants to get\nkey-value pair such that the keys are common but the values may not be common.\n\nExample\n-------\n\nSET @obj1= '{ "a": 1, "b": 2, "c": 3}';\nSET @obj2= '{"b" : 10, "c": 20, "d": 30}';\nSELECT JSON_OBJECT_FILTER_KEYS (@obj1, JSON_ARRAY_INTERSECT(JSON_KEYS(@obj1),\nJSON_KEYS(@obj2)));\n+------------------------------------------------------------------------------\n------------+\n| JSON_OBJECT_FILTER_KEYS (@obj1, JSON_ARRAY_INTERSECT(JSON_KEYS(@obj1),\nJSON_KEYS(@obj2))) |\n+------------------------------------------------------------------------------\n------------+\n| {"b": 2, "c": 3} \n |\n+------------------------------------------------------------------------------\n------------+\n\nURL: https://mariadb.com/kb/en/json_object_filter_keys/
+[JSON_OBJECT_TO_ARRAY]
+declaration=Obj
+category=JSON Functions
+description=It is used to convert all JSON objects found in a JSON document to JSON arrays\nwhere each item in the outer array represents a single key-value pair from the\nobject. It is used when we want not just common keys, but also common values.\nIt can be used in conjunction with JSON_ARRAY_INTERSECT().\n\nExamples\n--------\n\nSET @obj1= '{ "a": [1, 2, 3], "b": { "key1":"val1", "key2": {"key3":"val3"}\n}}';\n\nSELECT JSON_OBJECT_TO_ARRAY(@obj1);\n+-----------------------------------------------------------------------+\n| JSON_OBJECT_TO_ARRAY(@obj1) |\n+-----------------------------------------------------------------------+\n| [["a", [1, 2, 3]], ["b", {"key1": "val1", "key2": {"key3": "val3"}}]] |\n+-----------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_object_to_array/
+[JSON_OVERLAPS]
+declaration=json_doc1, json_doc2
+category=JSON Functions
+description=JSON_OVERLAPS() compares two json documents and returns true if they have at\nleast one common key-value pair between two objects, array element common\nbetween two arrays, or array element common with scalar if one of the\narguments is a scalar and other is an array. If two json documents are\nscalars, it returns true if they have same type and value.\n\nIf none of the above conditions are satisfied then it returns false.\n\nExamples\n--------\n\nSELECT JSON_OVERLAPS('false', 'false');\n+---------------------------------+\n| JSON_OVERLAPS('false', 'false') |\n+---------------------------------+\n| 1 |\n+---------------------------------+\n\nSELECT JSON_OVERLAPS('true', '["abc", 1, 2, true, false]');\n+----------------------------------------------------+\n| JSON_OVERLAPS('true','["abc", 1, 2, true, false]') |\n+----------------------------------------------------+\n| 1 |\n+----------------------------------------------------+\n\nSELECT JSON_OVERLAPS('{"A": 1, "B": {"C":2}}', '{"A": 2, "B": {"C":2}}') AS\nis_overlap;\n+---------------------+\n| is_overlap |\n+---------------------+\n| 1 |\n+---------------------+\n\nPartial match is considered as no-match.\n\nExamples\n--------\n\nSELECT JSON_OVERLAPS('[1, 2, true, false, null]', '[3, 4, [1]]') AS is_overlap;\n+--------------------- +\n| is_overlap |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/json_overlaps/
+[JSON_QUERY]
+declaration=json_doc, path
+category=JSON Functions
+description=Given a JSON document, returns an object or array specified by the path.\nReturns NULL if not given a valid JSON document, or if there is no match.\n\nExamples\n--------\n\nselect json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1');\n+-----------------------------------------------------+\n| json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1') |\n+-----------------------------------------------------+\n| {"a":1, "b":[1,2]} |\n+-----------------------------------------------------+\n\nselect json_query('{"key1":123, "key1": [1,2,3]}', '$.key1');\n+-------------------------------------------------------+\n| json_query('{"key1":123, "key1": [1,2,3]}', '$.key1') |\n+-------------------------------------------------------+\n| [1,2,3] |\n+-------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_query/
+[JSON_QUOTE]
+declaration=json_value
+category=JSON Functions
+description=Quotes a string as a JSON value, usually for producing valid JSON string\nliterals for inclusion in JSON documents. Wraps the string with double quote\ncharacters and escapes interior quotes and other special characters, returning\na utf8mb4 string.\n\nReturns NULL if the argument is NULL.\n\nExamples\n--------\n\nSELECT JSON_QUOTE('A'), JSON_QUOTE("B"), JSON_QUOTE('"C"');\n+-----------------+-----------------+-------------------+\n| JSON_QUOTE('A') | JSON_QUOTE("B") | JSON_QUOTE('"C"') |\n+-----------------+-----------------+-------------------+\n| "A" | "B" | "\"C\"" |\n+-----------------+-----------------+-------------------+\n\nURL: https://mariadb.com/kb/en/json_quote/
+[JSON_REMOVE]
+declaration=json_doc, path[, path] ...
+category=JSON Functions
+description=Removes data from a JSON document returning the result, or NULL if any of the\narguments are null. If the element does not exist in the document, no changes\nare made.\n\nThe function returns NULL and throws a warning if the JSON document is\ninvalid, the path is invalid, contains a range, or contains a * or ** wildcard.\n\nPath arguments are evaluated from left to right, with the result from the\nearlier evaluation being used as the value for the next.\n\nExamples\n--------\n\nSELECT JSON_REMOVE('{"A": 1, "B": 2, "C": {"D": 3}}', '$.C');\n+-------------------------------------------------------+\n| JSON_REMOVE('{"A": 1, "B": 2, "C": {"D": 3}}', '$.C') |\n+-------------------------------------------------------+\n| {"A": 1, "B": 2} |\n+-------------------------------------------------------+\n\nSELECT JSON_REMOVE('["A", "B", ["C", "D"], "E"]', '$[1]');\n+----------------------------------------------------+\n| JSON_REMOVE('["A", "B", ["C", "D"], "E"]', '$[1]') |\n+----------------------------------------------------+\n| ["A", ["C", "D"], "E"] |\n+----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_remove/
+[JSON_REPLACE]
+declaration=json_doc, path, val[, path, val] ...
+category=JSON Functions
+description=Replaces existing values in a JSON document, returning the result, or NULL if\nany of the arguments are NULL.\n\nAn error will occur if the JSON document is invalid, the path is invalid or if\nthe path contains a * or ** wildcard.\n\nPaths and values are evaluated from left to right, with the result from the\nearlier evaluation being used as the value for the next.\n\nJSON_REPLACE can only update data, while JSON_INSERT can only insert. JSON_SET\ncan update or insert data.\n\nExamples\n--------\n\nSELECT JSON_REPLACE('{ "A": 1, "B": [2, 3]}', '$.B[1]', 4);\n+-----------------------------------------------------+\n| JSON_REPLACE('{ "A": 1, "B": [2, 3]}', '$.B[1]', 4) |\n+-----------------------------------------------------+\n| { "A": 1, "B": [2, 4]} |\n+-----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_replace/
+[JSON_SCHEMA_VALID]
+declaration=schema, json
+category=JSON Functions
+description=JSON_SCHEMA_VALID allows MariaDB to support JSON schema validation. If a given\njson is valid against a schema it returns true. When JSON does not validate\nagainst the schema, it does not return a message about which keyword it failed\nagainst and only returns false.\n\nThe function supports JSON Schema Draft 2020 with a few exceptions:\n\n* External resources are not supported\n* Hyper schema keywords are not supported\n* Formats like date, email etc are treated as annotations.\n\nExamples\n--------\n\nTo create validation rules for json field\n\nCREATE TABLE obj_table(val_obj JSON CHECK(JSON_SCHEMA_VALID('{\n "type":"object",\n "properties": {\n "number1":{\n "type":"number",\n "maximum":5,\n "const":4\n },\n "string1":{\n "type":"string",\n "maxLength":5,\n "minLength":3\n },\n "object1":{\n "type":"object",\n "properties":{\n "key1": {"type":"string"},\n "key2":{"type":"array"},\n "key3":{"type":"number", "minimum":3}\n },\n "dependentRequired": { "key1":["key3"] }\n }\n },\n "required":["number1","object1"]\n }', val_obj)));\n\nINSERT INTO obj_table VALUES(\n '{"number1":4, "string1":"abcd",\n "object1":{"key1":"val1", "key2":[1,2,3, "string1"], "key3":4}}'\n);\n\nINSERT INTO obj_table VALUES(\n '{"number1":3, "string1":"abcd",\n "object1":{"key1":"val1", "key2":[1,2,3, "string1"], "key3":4}}'\n ...
+[JSON_SEARCH]
+declaration=json_doc, return_arg, search_str[, escape_char[, path] ...]
+category=JSON Functions
+description=Returns the path to the given string within a JSON document, or NULL if any of\njson_doc, search_str or a path argument is NULL; if the search string is not\nfound, or if no path exists within the document.\n\nA warning will occur if the JSON document is not valid, any of the path\narguments are not valid, if return_arg is neither one nor all, or if the\nescape character is not a constant. NULL will be returned.\n\nreturn_arg can be one of two values:\n\n* 'one: Terminates after finding the first match, so will return one path\nstring. If there is more than one match, it is undefined which is considered\nfirst.\n* all: Returns all matching path strings, without duplicates. Multiple strings\nare autowrapped as an array. The order is undefined.\n\nExamples\n--------\n\nSET @json = '["A", [{"B": "1"}], {"C":"AB"}, {"D":"BC"}]';\n\nSELECT JSON_SEARCH(@json, 'one', 'AB');\n+---------------------------------+\n| JSON_SEARCH(@json, 'one', 'AB') |\n+---------------------------------+\n| "$[2].C" |\n+---------------------------------+\n\nURL: https://mariadb.com/kb/en/json_search/
+[JSON_SET]
+declaration=json_doc, path, val[, path, val] ...
+category=JSON Functions
+description=Updates or inserts data into a JSON document, returning the result, or NULL if\nany of the arguments are NULL or the optional path fails to find an object.\n\nAn error will occur if the JSON document is invalid, the path is invalid or if\nthe path contains a * or wildcard.\n\nJSON_SET can update or insert data, while JSON_REPLACE can only update, and\nJSON_INSERT only insert.\n\nExamples\n--------\n\nSELECT JSON_SET(Priv, '$.locked', 'true') FROM mysql.global_priv\n\nURL: https://mariadb.com/kb/en/json_set/
+[JSON_TABLE]
+declaration=json_doc, context_path COLUMNS (column_list
+category=JSON Functions
+description=JSON_TABLE can be used in contexts where a table reference can be used; in the\nFROM clause of a SELECT statement, and in multi-table UPDATE/DELETE statements.\n\njson_doc is the JSON document to extract data from. In the simplest case, it\nis a string literal containing JSON. In more complex cases it can be an\narbitrary expression returning JSON. The expression may have references to\ncolumns of other tables. However, one can only refer to tables that precede\nthis JSON_TABLE invocation. For RIGHT JOIN, it is assumed that its outer side\nprecedes the inner. All tables in outer selects are also considered preceding.\n\ncontext_path is a JSON Path expression pointing to a collection of nodes in\njson_doc that will be used as the source of rows.\n\nThe COLUMNS clause declares the names and types of the columns that JSON_TABLE\nreturns, as well as how the values of the columns are produced.\n\nColumn Definitions\n------------------\n\nThe following types of columns are supported:\n\nPath Columns\n------------\n\nname type PATH path_str [on_empty] [on_error]\n\nLocates the JSON node pointed to by path_str and returns its value. The\npath_str is evaluated using the current row source node as the context node.\n\nset @json='\n[\n {"name":"Laptop", "color":"black", "price":"1000"},\n {"name":"Jeans", "color":"blue"}\n]';\n\nselect * from json_table(@json, '$[*]' \n columns(\n name varchar(10) path '$.name',\n color varchar(10) path '$.color',\n price decimal(8,2) path '$.price' )\n) as jt;\n+--------+-------+---------+\n| name | color | price |\n+--------+-------+---------+\n| Laptop | black | 1000.00 |\n| Jeans | blue | NULL |\n+--------+-------+---------+\n\nThe on_empty and on_error clauses specify the actions to be performed when the\nvalue was not found or there was an error condition. See the ON EMPTY and ON\n ...
+[JSON_TYPE]
+declaration=json_val
+category=JSON Functions
+description=Returns the type of a JSON value (as a string), or NULL if the argument is\nnull.\n\nAn error will occur if the argument is an invalid JSON value.\n\nThe following is a complete list of the possible return types:\n\n+-----------------------------------+-----------------+-----------------------+\n| Return type | Value | Example |\n+-----------------------------------+-----------------+-----------------------+\n| ARRAY | JSON array | [1, 2, {"key": |\n| | | "value"}] |\n+-----------------------------------+-----------------+-----------------------+\n| OBJECT | JSON object | {"key":"value"} |\n+-----------------------------------+-----------------+-----------------------+\n| BOOLEAN | JSON | true, false |\n| | true/false | |\n| | literals | |\n+-----------------------------------+-----------------+-----------------------+\n| DOUBLE | A number with | 1.2 |\n| | at least one | |\n| | floating point | |\n| | decimal. | |\n+-----------------------------------+-----------------+-----------------------+\n| INTEGER | A number | 1 |\n| | without a | |\n| | floating point | |\n| | decimal. | |\n+-----------------------------------+-----------------+-----------------------+\n| NULL | JSON null | null |\n| | literal (this | |\n| | is returned as | |\n| | a string, not | |\n| | to be confused | |\n| | with the SQL | |\n| | NULL value!) | |\n+-----------------------------------+-----------------+-----------------------+\n| STRING | JSON String | "a sample string" |\n+-----------------------------------+-----------------+-----------------------+\n\nExamples\n--------\n\nSELECT JSON_TYPE('{"A": 1, "B": 2, "C": 3}');\n+---------------------------------------+\n| JSON_TYPE('{"A": 1, "B": 2, "C": 3}') |\n+---------------------------------------+\n| OBJECT |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_type/
+[JSON_UNQUOTE]
+declaration=val
+category=JSON Functions
+description=Unquotes a JSON value, returning a string, or NULL if the argument is null.\n\nAn error will occur if the given value begins and ends with double quotes and\nis an invalid JSON string literal.\n\nIf the given value is not a JSON string, value is passed through unmodified.\n\nCertain character sequences have special meanings within a string. Usually, a\nbackslash is ignored, but the escape sequences in the table below are\nrecognised by MariaDB, unless the SQL Mode is set to NO_BACKSLASH_ESCAPES SQL.\n\n+-----------------------------------------------+-----------------------------+\n| Escape sequence | Character |\n+-----------------------------------------------+-----------------------------+\n| \" | Double quote (") |\n+-----------------------------------------------+-----------------------------+\n| \b | Backslash |\n+-----------------------------------------------+-----------------------------+\n| \f | Formfeed |\n+-----------------------------------------------+-----------------------------+\n| \n | Newline (linefeed) |\n+-----------------------------------------------+-----------------------------+\n| \r | Carriage return |\n+-----------------------------------------------+-----------------------------+\n| \t | Tab |\n+-----------------------------------------------+-----------------------------+\n| \\ | Backslash (\) |\n+-----------------------------------------------+-----------------------------+\n| \uXXXX | UTF-8 bytes for Unicode |\n| | value XXXX |\n+-----------------------------------------------+-----------------------------+\n\nExamples\n--------\n\nSELECT JSON_UNQUOTE('"Monty"');\n+-------------------------+\n| JSON_UNQUOTE('"Monty"') |\n+-------------------------+\n| Monty |\n+-------------------------+\n\nWith the default SQL Mode:\n\nSELECT JSON_UNQUOTE('Si\bng\ting');\n+-----------------------------+\n| JSON_UNQUOTE('Si\bng\ting') |\n+-----------------------------+\n| Sng ing |\n+-----------------------------+\n ...
+[JSON_VALID]
+declaration=value
+category=JSON Functions
+description=Indicates whether the given value is a valid JSON document or not. Returns 1\nif valid, 0 if not, and NULL if the argument is NULL.\n\nFrom MariaDB 10.4.3, the JSON_VALID function is automatically used as a CHECK\nconstraint for the JSON data type alias in order to ensure that a valid json\ndocument is inserted.\n\nExamples\n--------\n\nSELECT JSON_VALID('{"id": 1, "name": "Monty"}');\n+------------------------------------------+\n| JSON_VALID('{"id": 1, "name": "Monty"}') |\n+------------------------------------------+\n| 1 |\n+------------------------------------------+\n\nSELECT JSON_VALID('{"id": 1, "name": "Monty", "oddfield"}');\n+------------------------------------------------------+\n| JSON_VALID('{"id": 1, "name": "Monty", "oddfield"}') |\n+------------------------------------------------------+\n| 0 |\n+------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/json_valid/
+[JSON_VALUE]
+declaration=json_doc, path
+category=JSON Functions
+description=Given a JSON document, returns the scalar specified by the path. Returns NULL\nif not given a valid JSON document, or if there is no match.\n\nExamples\n--------\n\nselect json_value('{"key1":123}', '$.key1');\n+--------------------------------------+\n| json_value('{"key1":123}', '$.key1') |\n+--------------------------------------+\n| 123 |\n+--------------------------------------+\n\nselect json_value('{"key1": [1,2,3], "key1":123}', '$.key1');\n+-------------------------------------------------------+\n| json_value('{"key1": [1,2,3], "key1":123}', '$.key1') |\n+-------------------------------------------------------+\n| 123 |\n+-------------------------------------------------------+\n\nIn the SET statement below, two escape characters are needed, as a single\nescape character would be applied by the SQL parser in the SET statement, and\nthe escaped character would not form part of the saved value.\n\nSET @json = '{"key1":"60\\" Table", "key2":"1"}';\n\nSELECT JSON_VALUE(@json,'$.key1') AS Name , json_value(@json,'$.key2') as ID;\n+-----------+------+\n| Name | ID |\n+-----------+------+\n| 60" Table | 1 |\n+-----------+------+\n\nURL: https://mariadb.com/kb/en/json_value/
+[KDF]
+declaration=
+category=Encryption Functions
+description=KDF is a key derivation function, similar to OpenSSL's EVP_KDF_derive(). The\npurpose of a KDF is to be slow, so if the calculated value is lost/stolen, the\noriginal key_str is not achievable easily with modern GPU. KDFs are therefore\nan ideal replacement for password hashes. KDFs can also pad out a password\nsecret to the number of bits used in encryption algorithms.\n\nFor generating good encryption keys for AES_ENCRYPT a less expensive function,\nbut cryptographically secure function like RANDOM_BYTES is recommended..\n\n* kdf_name is "hkdf" or "pbkdf2_hmac" (default)\n* width (in bits) can be any number divisible by 8, by default it's taken from\n@@block_encryption_mode\n* iterations must be positive, and is 1000 by default\n\nNote that OpenSSL 1.0 doesn't support HKDF, so in this case NULL is returned.\nThis OpenSSL version is still used in SLES 12 and CentOS 7.\n\nExamples\n--------\n\nselect hex(kdf('foo', 'bar', 'infa', 'hkdf')); \n+----------------------------------------+\n| hex(kdf('foo', 'bar', 'infa', 'hkdf')) |\n+----------------------------------------+\n| 612875F859CFB4EE0DFEFF9F2A18E836 |\n+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/kdf/
+[LAG]
+declaration=expr[, offset]
+category=Window Functions
+description=The LAG function accesses data from a previous row according to the ORDER BY\nclause without the need for a self-join. The specific row is determined by the\noffset (default 1), which specifies the number of rows behind the current row\nto use. An offset of 0 is the current row.\n\nExamples\n--------\n\nCREATE TABLE t1 (pk int primary key, a int, b int, c char(10), d decimal(10,\n3), e real);\n\nINSERT INTO t1 VALUES\n ( 1, 0, 1, 'one', 0.1, 0.001),\n ( 2, 0, 2, 'two', 0.2, 0.002),\n ( 3, 0, 3, 'three', 0.3, 0.003),\n ( 4, 1, 2, 'three', 0.4, 0.004),\n ( 5, 1, 1, 'two', 0.5, 0.005),\n ( 6, 1, 1, 'one', 0.6, 0.006),\n ( 7, 2, NULL, 'n_one', 0.5, 0.007),\n ( 8, 2, 1, 'n_two', NULL, 0.008),\n ( 9, 2, 2, NULL, 0.7, 0.009),\n (10, 2, 0, 'n_four', 0.8, 0.010),\n (11, 2, 10, NULL, 0.9, NULL);\n\nSELECT pk, LAG(pk) OVER (ORDER BY pk) AS l,\n LAG(pk,1) OVER (ORDER BY pk) AS l1,\n LAG(pk,2) OVER (ORDER BY pk) AS l2,\n LAG(pk,0) OVER (ORDER BY pk) AS l0,\n LAG(pk,-1) OVER (ORDER BY pk) AS lm1,\n LAG(pk,-2) OVER (ORDER BY pk) AS lm2\nFROM t1;\n+----+------+------+------+------+------+------+\n| pk | l | l1 | l2 | l0 | lm1 | lm2 |\n+----+------+------+------+------+------+------+\n| 1 | NULL | NULL | NULL | 1 | 2 | 3 |\n| 2 | 1 | 1 | NULL | 2 | 3 | 4 |\n| 3 | 2 | 2 | 1 | 3 | 4 | 5 |\n| 4 | 3 | 3 | 2 | 4 | 5 | 6 |\n| 5 | 4 | 4 | 3 | 5 | 6 | 7 |\n| 6 | 5 | 5 | 4 | 6 | 7 | 8 |\n| 7 | 6 | 6 | 5 | 7 | 8 | 9 |\n| 8 | 7 | 7 | 6 | 8 | 9 | 10 |\n| 9 | 8 | 8 | 7 | 9 | 10 | 11 |\n| 10 | 9 | 9 | 8 | 10 | 11 | NULL |\n| 11 | 10 | 10 | 9 | 11 | NULL | NULL |\n+----+------+------+------+------+------+------+\n\nURL: https://mariadb.com/kb/en/lag/
+[LAST_DAY]
+declaration=date
+category=Date and Time Functions
+description=Takes a date or datetime value and returns the corresponding value for the\nlast day of the month. Returns NULL if the argument is invalid.\n\nExamples\n--------\n\nSELECT LAST_DAY('2003-02-05');\n+------------------------+\n| LAST_DAY('2003-02-05') |\n+------------------------+\n| 2003-02-28 |\n+------------------------+\n\nSELECT LAST_DAY('2004-02-05');\n+------------------------+\n| LAST_DAY('2004-02-05') |\n+------------------------+\n| 2004-02-29 |\n+------------------------+\n\nSELECT LAST_DAY('2004-01-01 01:01:01');\n+---------------------------------+\n| LAST_DAY('2004-01-01 01:01:01') |\n+---------------------------------+\n| 2004-01-31 |\n+---------------------------------+\n\nSELECT LAST_DAY('2003-03-32');\n+------------------------+\n| LAST_DAY('2003-03-32') |\n+------------------------+\n| NULL |\n+------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nWarning (Code 1292): Incorrect datetime value: '2003-03-32'\n\nURL: https://mariadb.com/kb/en/last_day/
+[LAST_INSERT_ID]
+declaration=
+category=Information Functions
+description=LAST_INSERT_ID() (no arguments) returns the first automatically generated\nvalue successfully inserted for an AUTO_INCREMENT column as a result of the\nmost recently executed INSERT statement. The value of LAST_INSERT_ID() remains\nunchanged if no rows are successfully inserted.\n\nIf one gives an argument to LAST_INSERT_ID(), then it will return the value of\nthe expression and the next call to LAST_INSERT_ID() will return the same\nvalue. The value will also be sent to the client and can be accessed by the\nmysql_insert_id function.\n\nFor example, after inserting a row that generates an AUTO_INCREMENT value, you\ncan get the value like this:\n\nSELECT LAST_INSERT_ID();\n+------------------+\n| LAST_INSERT_ID() |\n+------------------+\n| 9 |\n+------------------+\n\nYou can also use LAST_INSERT_ID() to delete the last inserted row:\n\nDELETE FROM product WHERE id = LAST_INSERT_ID();\n\nIf no rows were successfully inserted, LAST_INSERT_ID() returns 0.\n\nThe value of LAST_INSERT_ID() will be consistent across all versions if all\nrows in the INSERT or UPDATE statement were successful.\n\nThe currently executing statement does not affect the value of\nLAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value with one\nstatement, and then refer to LAST_INSERT_ID() in a multiple-row INSERT\nstatement that inserts rows into a table with its own AUTO_INCREMENT column.\nThe value of LAST_INSERT_ID() will remain stable in the second statement; its\nvalue for the second and later rows is not affected by the earlier row\ninsertions. (However, if you mix references to LAST_INSERT_ID() and\nLAST_INSERT_ID(expr), the effect is undefined.)\n\nIf the previous statement returned an error, the value of LAST_INSERT_ID() is\nundefined. For transactional tables, if the statement is rolled back due to an\nerror, the value of LAST_INSERT_ID() is left undefined. For manual ROLLBACK,\nthe value of LAST_INSERT_ID() is not restored to that before the transaction;\nit remains as it was at the point of the ROLLBACK.\n\nWithin the body of a stored routine (procedure or function) or a trigger, the\nvalue of LAST_INSERT_ID() changes the same way as for statements executed\noutside the body of these kinds of objects. The effect of a stored routine or\ntrigger upon the value of LAST_INSERT_ID() that is seen by following\nstatements depends on the kind of routine:\n\n ...
+[LAST_VALUE]
+declaration=expr,[expr,...]
+category=Information Functions
+description=LAST_VALUE() evaluates all expressions and returns the last.\n\nThis is useful together with setting user variables to a value with\n@var:=expr, for example when you want to get data of rows updated/deleted\nwithout having to do two queries against the table.\n\nLAST_VALUE can be used as a window function.\n\nReturns NULL if no last value exists.\n\nExamples\n--------\n\nCREATE TABLE t1 (a int, b int);\nINSERT INTO t1 VALUES(1,10),(2,20);\nDELETE FROM t1 WHERE a=1 AND last_value(@a:=a,@b:=b,1);\nSELECT @a,@b;\n+------+------+\n| @a | @b |\n+------+------+\n| 1 | 10 |\n+------+------+\n\nAs a window function:\n\nCREATE TABLE t1 (\n pk int primary key,\n a int,\n b int,\n c char(10),\n d decimal(10, 3),\n e real\n);\n\nINSERT INTO t1 VALUES\n( 1, 0, 1, 'one', 0.1, 0.001),\n( 2, 0, 2, 'two', 0.2, 0.002),\n( 3, 0, 3, 'three', 0.3, 0.003),\n( 4, 1, 2, 'three', 0.4, 0.004),\n( 5, 1, 1, 'two', 0.5, 0.005),\n( 6, 1, 1, 'one', 0.6, 0.006),\n( 7, 2, NULL, 'n_one', 0.5, 0.007),\n( 8, 2, 1, 'n_two', NULL, 0.008),\n( 9, 2, 2, NULL, 0.7, 0.009),\n(10, 2, 0, 'n_four', 0.8, 0.010),\n(11, 2, 10, NULL, 0.9, NULL);\n\nSELECT pk, FIRST_VALUE(pk) OVER (ORDER BY pk) AS first_asc,\n LAST_VALUE(pk) OVER (ORDER BY pk) AS last_asc,\n FIRST_VALUE(pk) OVER (ORDER BY pk DESC) AS first_desc,\n ...
+[LCASE]
+declaration=str
+category=String Functions
+description=LCASE() is a synonym for LOWER().\n\nURL: https://mariadb.com/kb/en/lcase/
+[LEAD]
+declaration=expr[, offset]
+category=Window Functions
+description=The LEAD function accesses data from a following row in the same result set\nwithout the need for a self-join. The specific row is determined by the offset\n(default 1), which specifies the number of rows ahead the current row to use.\nAn offset of 0 is the current row.\n\nExample\n-------\n\nCREATE TABLE t1 (pk int primary key, a int, b int, c char(10), d decimal(10,\n3), e real);\n\nINSERT INTO t1 VALUES\n ( 1, 0, 1, 'one', 0.1, 0.001),\n ( 2, 0, 2, 'two', 0.2, 0.002),\n ( 3, 0, 3, 'three', 0.3, 0.003),\n ( 4, 1, 2, 'three', 0.4, 0.004),\n ( 5, 1, 1, 'two', 0.5, 0.005),\n ( 6, 1, 1, 'one', 0.6, 0.006),\n ( 7, 2, NULL, 'n_one', 0.5, 0.007),\n ( 8, 2, 1, 'n_two', NULL, 0.008),\n ( 9, 2, 2, NULL, 0.7, 0.009),\n (10, 2, 0, 'n_four', 0.8, 0.010),\n (11, 2, 10, NULL, 0.9, NULL);\n\nSELECT pk, LEAD(pk) OVER (ORDER BY pk) AS l,\n LEAD(pk,1) OVER (ORDER BY pk) AS l1,\n LEAD(pk,2) OVER (ORDER BY pk) AS l2,\n LEAD(pk,0) OVER (ORDER BY pk) AS l0,\n LEAD(pk,-1) OVER (ORDER BY pk) AS lm1,\n LEAD(pk,-2) OVER (ORDER BY pk) AS lm2\nFROM t1;\n+----+------+------+------+------+------+------+\n| pk | l | l1 | l2 | l0 | lm1 | lm2 |\n+----+------+------+------+------+------+------+\n| 1 | 2 | 2 | 3 | 1 | NULL | NULL |\n| 2 | 3 | 3 | 4 | 2 | 1 | NULL |\n| 3 | 4 | 4 | 5 | 3 | 2 | 1 |\n| 4 | 5 | 5 | 6 | 4 | 3 | 2 |\n| 5 | 6 | 6 | 7 | 5 | 4 | 3 |\n| 6 | 7 | 7 | 8 | 6 | 5 | 4 |\n| 7 | 8 | 8 | 9 | 7 | 6 | 5 |\n| 8 | 9 | 9 | 10 | 8 | 7 | 6 |\n| 9 | 10 | 10 | 11 | 9 | 8 | 7 |\n| 10 | 11 | 11 | NULL | 10 | 9 | 8 |\n| 11 | NULL | NULL | NULL | 11 | 10 | 9 |\n+----+------+------+------+------+------+------+\n\nURL: https://mariadb.com/kb/en/lead/
+[LEAST]
+declaration=value1,value2,...
+category=Comparison Operators
+description=With two or more arguments, returns the smallest (minimum-valued) argument.\nThe arguments are compared using the following rules:\n\n* If the return value is used in an INTEGER context or all arguments are\ninteger-valued, they are compared as integers.\n* If the return value is used in a REAL context or all arguments are\nreal-valued, they are compared as reals.\n* If any argument is a case-sensitive string, the arguments are compared as\ncase-sensitive strings.\n* In all other cases, the arguments are compared as case-insensitive strings.\n\nLEAST() returns NULL if any argument is NULL.\n\nExamples\n--------\n\nSELECT LEAST(2,0);\n+------------+\n| LEAST(2,0) |\n+------------+\n| 0 |\n+------------+\n\nSELECT LEAST(34.0,3.0,5.0,767.0);\n+---------------------------+\n| LEAST(34.0,3.0,5.0,767.0) |\n+---------------------------+\n| 3.0 |\n+---------------------------+\n\nSELECT LEAST('B','A','C');\n+--------------------+\n| LEAST('B','A','C') |\n+--------------------+\n| A |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/least/
+[LEFT]
+declaration=str,len
+category=String Functions
+description=Returns the leftmost len characters from the string str, or NULL if any\nargument is NULL.\n\nExamples\n--------\n\nSELECT LEFT('MariaDB', 5);\n+--------------------+\n| LEFT('MariaDB', 5) |\n+--------------------+\n| Maria |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/left/
+[LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str.\n\nIn the default mode, when Oracle mode from MariaDB 10.3 is not set, the length\nis measured in bytes. In this case, a multi-byte character counts as multiple\nbytes. This means that for a string containing five two-byte characters,\nLENGTH() returns 10, whereas CHAR_LENGTH() returns 5.\n\nWhen running Oracle mode from MariaDB 10.3, the length is measured in\ncharacters, and LENGTH is a synonym for CHAR_LENGTH().\n\nIf str is not a string value, it is converted into a string. If str is NULL,\nthe function returns NULL.\n\nExamples\n--------\n\nSELECT LENGTH('MariaDB');\n+-------------------+\n| LENGTH('MariaDB') |\n+-------------------+\n| 7 |\n+-------------------+\n\nWhen Oracle mode from MariaDB 10.3 is not set:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 2 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nIn Oracle mode from MariaDB 10.3:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 1 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nURL: https://mariadb.com/kb/en/length/
+[LENGTHB]
+declaration=str
+category=String Functions
+description=LENGTHB() returns the length of the given string, in bytes. When Oracle mode\nis not set, this is a synonym for LENGTH.\n\nA multi-byte character counts as multiple bytes. This means that for a string\ncontaining five two-byte characters, LENGTHB() returns 10, whereas\nCHAR_LENGTH() returns 5.\n\nIf str is not a string value, it is converted into a string. If str is NULL,\nthe function returns NULL.\n\nExamples\n--------\n\nWhen Oracle mode from MariaDB 10.3 is not set:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 2 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nIn Oracle mode from MariaDB 10.3:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 1 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nURL: https://mariadb.com/kb/en/lengthb/
+[LIMIT]
+declaration=or ORDER BY
+category=Data Manipulation
+description=multi-table UPDATE statement. This restriction was lifted in MariaDB 10.3.2.\n\nGROUP_CONCAT\n------------\n\nStarting from MariaDB 10.3.3, it is possible to use LIMIT with GROUP_CONCAT().\n\nExamples\n--------\n\nCREATE TABLE members (name VARCHAR(20));\nINSERT INTO members VALUES('Jagdish'),('Kenny'),('Rokurou'),('Immaculada');\n\nSELECT * FROM members;\n+------------+\n| name |\n+------------+\n| Jagdish |\n| Kenny |\n| Rokurou |\n| Immaculada |\n+------------+\n\nSelect the first two names (no ordering specified):\n\nSELECT * FROM members LIMIT 2;\n+---------+\n| name |\n+---------+\n| Jagdish |\n| Kenny |\n+---------+\n\nAll the names in alphabetical order:\n\nSELECT * FROM members ORDER BY name;\n+------------+\n| name |\n+------------+\n| Immaculada |\n| Jagdish |\n| Kenny |\n| Rokurou |\n+------------+\n\nThe first two names, ordered alphabetically:\n\nSELECT * FROM members ORDER BY name LIMIT 2;\n+------------+\n| name |\n ...
+[LINESTRING]
+declaration=pt1,pt2,...
+category=Geometry Constructors
+description=Constructs a WKB LineString value from a number of WKB Point arguments. If any\nargument is not a WKB Point, the return value is NULL. If the number of Point\narguments is less than two, the return value is NULL.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT AsText(EndPoint(GeomFromText(@ls)));\n+-------------------------------------+\n| AsText(EndPoint(GeomFromText(@ls))) |\n+-------------------------------------+\n| POINT(3 3) |\n+-------------------------------------+\n\nCREATE TABLE gis_line (g LINESTRING);\nINSERT INTO gis_line VALUES\n (LineFromText('LINESTRING(0 0,0 10,10 0)')),\n (LineStringFromText('LINESTRING(10 10,20 10,20 20,10 20,10 10)')),\n (LineStringFromWKB(AsWKB(LineString(Point(10, 10), Point(40, 10)))));\n\nURL: https://mariadb.com/kb/en/linestring/
+[LN]
+declaration=X
+category=Numeric Functions
+description=Returns the natural logarithm of X; that is, the base-e logarithm of X. If X\nis less than or equal to 0, or NULL, then NULL is returned.\n\nThe inverse of this function is EXP().\n\nExamples\n--------\n\nSELECT LN(2);\n+-------------------+\n| LN(2) |\n+-------------------+\n| 0.693147180559945 |\n+-------------------+\n\nSELECT LN(-2);\n+--------+\n| LN(-2) |\n+--------+\n| NULL |\n+--------+\n\nURL: https://mariadb.com/kb/en/ln/
+[LOAD_FILE]
+declaration=file_name
+category=String Functions
+description=Reads the file and returns the file contents as a string. To use this\nfunction, the file must be located on the server host, you must specify the\nfull path name to the file, and you must have the FILE privilege. The file\nmust be readable by all and it must be less than the size, in bytes, of the\nmax_allowed_packet system variable. If the secure_file_priv system variable is\nset to a non-empty directory name, the file to be loaded must be located in\nthat directory.\n\nIf the file does not exist or cannot be read because one of the preceding\nconditions is not satisfied, the function returns NULL.\n\nSince MariaDB 5.1, the character_set_filesystem system variable has controlled\ninterpretation of file names that are given as literal strings.\n\nStatements using the LOAD_FILE() function are not safe for statement based\nreplication. This is because the slave will execute the LOAD_FILE() command\nitself. If the file doesn't exist on the slave, the function will return NULL.\n\nExamples\n--------\n\nUPDATE t SET blob_col=LOAD_FILE('/tmp/picture') WHERE id=1;\n\nURL: https://mariadb.com/kb/en/load_file/
+[LOCALTIME]
+declaration=[precision]
+category=Date and Time Functions
+description=LOCALTIME and LOCALTIME() are synonyms for NOW().\n\nURL: https://mariadb.com/kb/en/localtime/
+[LOCALTIMESTAMP]
+declaration=[precision]
+category=Date and Time Functions
+description=LOCALTIMESTAMP and LOCALTIMESTAMP() are synonyms for NOW().\n\nURL: https://mariadb.com/kb/en/localtimestamp/
+[LOCATE]
+declaration=substr,str
+category=String Functions
+description=The first syntax returns the position of the first occurrence of substring\nsubstr in string str. The second syntax returns the position of the first\noccurrence of substring substr in string str, starting at position pos.\nReturns 0 if substr is not in str.\n\nLOCATE() performs a case-insensitive search.\n\nIf any argument is NULL, returns NULL.\n\nINSTR() is the same as the two-argument form of LOCATE(), except that the\norder of the arguments is reversed.\n\nExamples\n--------\n\nSELECT LOCATE('bar', 'foobarbar');\n+----------------------------+\n| LOCATE('bar', 'foobarbar') |\n+----------------------------+\n| 4 |\n+----------------------------+\n\nSELECT LOCATE('My', 'Maria');\n+-----------------------+\n| LOCATE('My', 'Maria') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nSELECT LOCATE('bar', 'foobarbar', 5);\n+-------------------------------+\n| LOCATE('bar', 'foobarbar', 5) |\n+-------------------------------+\n| 7 |\n+-------------------------------+\n\nURL: https://mariadb.com/kb/en/locate/
+[LOG]
+declaration=X
+category=Numeric Functions
+description=If called with one parameter, this function returns the natural logarithm of\nX. If X is less than or equal to 0, then NULL is returned.\n\nIf called with two parameters, it returns the logarithm of X to the base B. If\nB is <= 1 or X <= 0, the function returns NULL.\n\nIf any argument is NULL, the function returns NULL.\n\nThe inverse of this function (when called with a single argument) is the EXP()\nfunction.\n\nExamples\n--------\n\nLOG(X):\n\nSELECT LOG(2);\n+-------------------+\n| LOG(2) |\n+-------------------+\n| 0.693147180559945 |\n+-------------------+\n\nSELECT LOG(-2);\n+---------+\n| LOG(-2) |\n+---------+\n| NULL |\n+---------+\n\nLOG(B,X)\n\nSELECT LOG(2,16);\n+-----------+\n| LOG(2,16) |\n+-----------+\n| 4 |\n+-----------+\n\nSELECT LOG(3,27);\n+-----------+\n| LOG(3,27) |\n+-----------+\n| 3 |\n+-----------+\n\nSELECT LOG(3,1);\n+----------+\n| LOG(3,1) |\n+----------+\n ...
+[LOG10]
+declaration=X
+category=Numeric Functions
+description=Returns the base-10 logarithm of X.\n\nExamples\n--------\n\nSELECT LOG10(2);\n+-------------------+\n| LOG10(2) |\n+-------------------+\n| 0.301029995663981 |\n+-------------------+\n\nSELECT LOG10(100);\n+------------+\n| LOG10(100) |\n+------------+\n| 2 |\n+------------+\n\nSELECT LOG10(-100);\n+-------------+\n| LOG10(-100) |\n+-------------+\n| NULL |\n+-------------+\n\nURL: https://mariadb.com/kb/en/log10/
+[LOG2]
+declaration=X
+category=Numeric Functions
+description=Returns the base-2 logarithm of X.\n\nExamples\n--------\n\nSELECT LOG2(4398046511104);\n+---------------------+\n| LOG2(4398046511104) |\n+---------------------+\n| 42 |\n+---------------------+\n\nSELECT LOG2(65536);\n+-------------+\n| LOG2(65536) |\n+-------------+\n| 16 |\n+-------------+\n\nSELECT LOG2(-100);\n+------------+\n| LOG2(-100) |\n+------------+\n| NULL |\n+------------+\n\nURL: https://mariadb.com/kb/en/log2/
+[LOWER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to lowercase according to\nthe current character set mapping. The default is latin1 (cp1252 West\nEuropean).\n\nLCASE is a synonym for LOWER\n\nExamples\n--------\n\nSELECT LOWER('QUADRATICALLY');\n+------------------------+\n| LOWER('QUADRATICALLY') |\n+------------------------+\n| quadratically |\n+------------------------+\n\nLOWER() (and UPPER()) are ineffective when applied to binary strings (BINARY,\nVARBINARY, BLOB). To perform lettercase conversion, CONVERT the string to a\nnon-binary string:\n\nSET @str = BINARY 'North Carolina';\n\nSELECT LOWER(@str), LOWER(CONVERT(@str USING latin1));\n+----------------+-----------------------------------+\n| LOWER(@str) | LOWER(CONVERT(@str USING latin1)) |\n+----------------+-----------------------------------+\n| North Carolina | north carolina |\n+----------------+-----------------------------------+\n\nURL: https://mariadb.com/kb/en/lower/
+[LPAD]
+declaration=str, len [,padstr]
+category=String Functions
+description=Returns the string str, left-padded with the string padstr to a length of len\ncharacters. If str is longer than len, the return value is shortened to len\ncharacters. If padstr is omitted, the LPAD function pads spaces.\n\nPrior to MariaDB 10.3.1, the padstr parameter was mandatory.\n\nReturns NULL if given a NULL argument. If the result is empty (zero length),\nreturns either an empty string or, from MariaDB 10.3.6 with SQL_MODE=Oracle,\nNULL.\n\nThe Oracle mode version of the function can be accessed outside of Oracle mode\nby using LPAD_ORACLE as the function name.\n\nExamples\n--------\n\nSELECT LPAD('hello',10,'.');\n+----------------------+\n| LPAD('hello',10,'.') |\n+----------------------+\n| .....hello |\n+----------------------+\n\nSELECT LPAD('hello',2,'.');\n+---------------------+\n| LPAD('hello',2,'.') |\n+---------------------+\n| he |\n+---------------------+\n\nFrom MariaDB 10.3.1, with the pad string defaulting to space.\n\nSELECT LPAD('hello',10);\n+------------------+\n| LPAD('hello',10) |\n+------------------+\n| hello |\n+------------------+\n\nOracle mode version from MariaDB 10.3.6:\n\nSELECT LPAD('',0),LPAD_ORACLE('',0);\n+------------+-------------------+\n| LPAD('',0) | LPAD_ORACLE('',0) |\n+------------+-------------------+\n| | NULL |\n+------------+-------------------+\n\nURL: https://mariadb.com/kb/en/lpad/
+[LTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with leading space characters removed.\n\nReturns NULL if given a NULL argument. If the result is empty, returns either\nan empty string, or, from MariaDB 10.3.6 with SQL_MODE=Oracle, NULL.\n\nThe Oracle mode version of the function can be accessed outside of Oracle mode\nby using LTRIM_ORACLE as the function name.\n\nExamples\n--------\n\nSELECT QUOTE(LTRIM(' MariaDB '));\n+-------------------------------+\n| QUOTE(LTRIM(' MariaDB ')) |\n+-------------------------------+\n| 'MariaDB ' |\n+-------------------------------+\n\nOracle mode version from MariaDB 10.3.6:\n\nSELECT LTRIM(''),LTRIM_ORACLE('');\n+-----------+------------------+\n| LTRIM('') | LTRIM_ORACLE('') |\n+-----------+------------------+\n| | NULL |\n+-----------+------------------+\n\nURL: https://mariadb.com/kb/en/ltrim/
+[MAKEDATE]
+declaration=year,dayofyear
+category=Date and Time Functions
+description=Returns a date, given year and day-of-year values. dayofyear must be greater\nthan 0 or the result is NULL.\n\nExamples\n--------\n\nSELECT MAKEDATE(2011,31), MAKEDATE(2011,32);\n+-------------------+-------------------+\n| MAKEDATE(2011,31) | MAKEDATE(2011,32) |\n+-------------------+-------------------+\n| 2011-01-31 | 2011-02-01 |\n+-------------------+-------------------+\n\nSELECT MAKEDATE(2011,365), MAKEDATE(2014,365);\n+--------------------+--------------------+\n| MAKEDATE(2011,365) | MAKEDATE(2014,365) |\n+--------------------+--------------------+\n| 2011-12-31 | 2014-12-31 |\n+--------------------+--------------------+\n\nSELECT MAKEDATE(2011,0);\n+------------------+\n| MAKEDATE(2011,0) |\n+------------------+\n| NULL |\n+------------------+\n\nURL: https://mariadb.com/kb/en/makedate/
+[MAKETIME]
+declaration=hour,minute,second
+category=Date and Time Functions
+description=Returns a time value calculated from the hour, minute, and second arguments.\n\nIf minute or second are out of the range 0 to 60, NULL is returned. The hour\ncan be in the range -838 to 838, outside of which the value is truncated with\na warning.\n\nExamples\n--------\n\nSELECT MAKETIME(13,57,33);\n+--------------------+\n| MAKETIME(13,57,33) |\n+--------------------+\n| 13:57:33 |\n+--------------------+\n\nSELECT MAKETIME(-13,57,33);\n+---------------------+\n| MAKETIME(-13,57,33) |\n+---------------------+\n| -13:57:33 |\n+---------------------+\n\nSELECT MAKETIME(13,67,33);\n+--------------------+\n| MAKETIME(13,67,33) |\n+--------------------+\n| NULL |\n+--------------------+\n\nSELECT MAKETIME(-1000,57,33);\n+-----------------------+\n| MAKETIME(-1000,57,33) |\n+-----------------------+\n| -838:59:59 |\n+-----------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-----------------------------------------------+\n| Level | Code | Message |\n+---------+------+-----------------------------------------------+\n| Warning | 1292 | Truncated incorrect time value: '-1000:57:33' |\n+---------+------+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/maketime/
+[MAKE_SET]
+declaration=bits,str1,str2,...
+category=String Functions
+description=Returns a set value (a string containing substrings separated by ","\ncharacters) consisting of the strings that have the corresponding bit in bits\nset. str1 corresponds to bit 0, str2 to bit 1, and so on. NULL values in str1,\nstr2, ... are not appended to the result.\n\nExamples\n--------\n\nSELECT MAKE_SET(1,'a','b','c');\n+-------------------------+\n| MAKE_SET(1,'a','b','c') |\n+-------------------------+\n| a |\n+-------------------------+\n\nSELECT MAKE_SET(1 | 4,'hello','nice','world');\n+----------------------------------------+\n| MAKE_SET(1 | 4,'hello','nice','world') |\n+----------------------------------------+\n| hello,world |\n+----------------------------------------+\n\nSELECT MAKE_SET(1 | 4,'hello','nice',NULL,'world');\n+---------------------------------------------+\n| MAKE_SET(1 | 4,'hello','nice',NULL,'world') |\n+---------------------------------------------+\n| hello |\n+---------------------------------------------+\n\nSELECT QUOTE(MAKE_SET(0,'a','b','c'));\n+--------------------------------+\n| QUOTE(MAKE_SET(0,'a','b','c')) |\n+--------------------------------+\n| '' |\n+--------------------------------+\n\nURL: https://mariadb.com/kb/en/make_set/
+[MASTER_GTID_WAIT]
+declaration=gtid-list[, timeout
+category=Miscellaneous Functions
+description=This function takes a string containing a comma-separated list of global\ntransaction id's (similar to the value of, for example, gtid_binlog_pos). It\nwaits until the value of gtid_slave_pos has the same or higher seq_no within\nall replication domains specified in the gtid-list; in other words, it waits\nuntil the slave has reached the specified GTID position.\n\nAn optional second argument gives a timeout in seconds. If the timeout expires\nbefore the specified GTID position is reached, then the function returns -1.\nPassing NULL or a negative number for the timeout means no timeout, and the\nfunction will wait indefinitely.\n\nIf the wait completes without a timeout, 0 is returned. Passing NULL for the\ngtid-list makes the function return NULL immediately, without waiting.\n\nThe gtid-list may be the empty string, in which case MASTER_GTID_WAIT()\nreturns immediately. If the gtid-list contains fewer domains than\ngtid_slave_pos, then only those domains are waited upon. If gtid-list contains\na domain that is not present in @@gtid_slave_pos, then MASTER_GTID_WAIT() will\nwait until an event containing such domain_id arrives on the slave (or until\ntimed out or killed).\n\nMASTER_GTID_WAIT() can be useful to ensure that a slave has caught up to a\nmaster. Simply take the value of gtid_binlog_pos on the master, and use it in\na MASTER_GTID_WAIT() call on the slave; when the call completes, the slave\nwill have caught up with that master position.\n\nMASTER_GTID_WAIT() can also be used in client applications together with the\nlast_gtid session variable. This is useful in a read-scaleout replication\nsetup, where the application writes to a single master but divides the reads\nout to a number of slaves to distribute the load. In such a setup, there is a\nrisk that an application could first do an update on the master, and then a\nbit later do a read on a slave, and if the slave is not fast enough, the data\nread from the slave might not include the update just made, possibly confusing\nthe application and/or the end-user. One way to avoid this is to request the\nvalue of last_gtid on the master just after the update. Then before doing the\nread on the slave, do a MASTER_GTID_WAIT() on the value obtained from the\nmaster; this will ensure that the read is not performed until the slave has\nreplicated sufficiently far for the update to have become visible.\n\nNote that MASTER_GTID_WAIT() can be used even if the slave is configured not\nto use GTID for connections (CHANGE MASTER TO master_use_gtid=no). This is\nbecause from MariaDB 10, GTIDs are always logged on the master server, and\nalways recorded on the slave servers.\n\nDifferences to MASTER_POS_WAIT()\n--------------------------------\n\n* MASTER_GTID_WAIT() is global; it waits for any master connection to reach\n the specified GTID position. MASTER_POS_WAIT() works only against a\n specific connection. This also means that while MASTER_POS_WAIT() aborts if\n ...
+[MASTER_POS_WAIT]
+declaration=log_name,log_pos[,timeout,["connection_name"]]
+category=Miscellaneous Functions
+description=This function is useful in replication for controlling primary/replica\nsynchronization. It blocks until the replica has read and applied all updates\nup to the specified position (log_name,log_pos) in the primary log. The return\nvalue is the number of log events the replica had to wait for to advance to\nthe specified position. The function returns NULL if the replica SQL thread is\nnot started, the replica's primary information is not initialized, the\narguments are incorrect, or an error occurs. It returns -1 if the timeout has\nbeen exceeded. If the replica SQL thread stops while MASTER_POS_WAIT() is\nwaiting, the function returns NULL. If the replica is past the specified\nposition, the function returns immediately.\n\nIf a timeout value is specified, MASTER_POS_WAIT() stops waiting when timeout\nseconds have elapsed. timeout must be greater than 0; a zero or negative\ntimeout means no timeout.\n\nThe connection_name is used when you are using multi-source-replication. If\nyou don't specify it, it's set to the value of the default_master_connection\nsystem variable.\n\nStatements using the MASTER_POS_WAIT() function are not safe for\nstatement-based replication.\n\nURL: https://mariadb.com/kb/en/master_pos_wait/
+[MAX]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the largest, or maximum, value of expr. MAX() can also take a string\nargument in which case it returns the maximum string value. The DISTINCT\nkeyword can be used to find the maximum of the distinct values of expr,\nhowever, this produces the same result as omitting DISTINCT.\n\nNote that SET and ENUM fields are currently compared by their string value\nrather than their relative position in the set, so MAX() may produce a\ndifferent highest result than ORDER BY DESC.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nMAX() can be used as a window function.\n\nMAX() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87), ('Tatiana', 'Tuning', 83);\n\nSELECT name, MAX(score) FROM student GROUP BY name;\n+---------+------------+\n| name | MAX(score) |\n+---------+------------+\n| Chun | 75 |\n| Esben | 43 |\n| Kaolin | 88 |\n| Tatiana | 87 |\n+---------+------------+\n\nMAX string:\n\nSELECT MAX(name) FROM student;\n+-----------+\n| MAX(name) |\n+-----------+\n| Tatiana |\n+-----------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\nmismatched data:\n\nSELECT name,test,MAX(SCORE) FROM student;\n+------+------+------------+\n ...
+[MBRContains]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangle of g1\ncontains the Minimum Bounding Rectangle of g2. This tests the opposite\nrelationship as MBRWithin().\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\n\nSET @g2 = GeomFromText('Point(1 1)');\n\nSELECT MBRContains(@g1,@g2), MBRContains(@g2,@g1);\n+----------------------+----------------------+\n| MBRContains(@g1,@g2) | MBRContains(@g2,@g1) |\n+----------------------+----------------------+\n| 1 | 0 |\n+----------------------+----------------------+\n\nURL: https://mariadb.com/kb/en/mbrcontains/
+[MBRDisjoint]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 are disjoint. Two geometries are disjoint if they do not\nintersect, that is touch or overlap.\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((4 4,4 7,7 7,7 4,4 4))');\nSELECTmbrdisjoint(@g1,@g2);\n+----------------------+\n| mbrdisjoint(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbrdisjoint(@g1,@g2);\n+----------------------+\n| mbrdisjoint(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/mbrdisjoint/
+[MBREqual]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 are the same.\n\nExamples\n--------\n\nSET @g1=GEOMFROMTEXT('LINESTRING(0 0, 1 2)');\nSET @g2=GEOMFROMTEXT('POLYGON((0 0, 0 2, 1 2, 1 0, 0 0))');\nSELECT MbrEqual(@g1,@g2);\n+-------------------+\n| MbrEqual(@g1,@g2) |\n+-------------------+\n| 1 |\n+-------------------+\n\nSET @g1=GEOMFROMTEXT('LINESTRING(0 0, 1 3)');\nSET @g2=GEOMFROMTEXT('POLYGON((0 0, 0 2, 1 4, 1 0, 0 0))');\nSELECT MbrEqual(@g1,@g2);\n+-------------------+\n| MbrEqual(@g1,@g2) |\n+-------------------+\n| 0 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/mbrequal/
+[MBRIntersects]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 intersect.\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbrintersects(@g1,@g2);\n+------------------------+\n| mbrintersects(@g1,@g2) |\n+------------------------+\n| 1 |\n+------------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((4 4,4 7,7 7,7 4,4 4))');\nSELECT mbrintersects(@g1,@g2);\n+------------------------+\n| mbrintersects(@g1,@g2) |\n+------------------------+\n| 0 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/mbrintersects/
+[MBROverlaps]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 overlap. The term spatially overlaps is used if two\ngeometries intersect and their intersection results in a geometry of the same\ndimension but not equal to either of the given geometries.\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((4 4,4 7,7 7,7 4,4 4))');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 4,4 4,4 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbroverlaps(@g1,@g2);\n+----------------------+\n| mbroverlaps(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/mbroverlaps/
+[MBRTouches]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangles of the two\ngeometries g1 and g2 touch. Two geometries spatially touch if the interiors of\nthe geometries do not intersect, but the boundary of one of the geometries\nintersects either the boundary or the interior of the other.\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((4 4,4 7,7 7,7 4,4 4))');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 1 |\n+---------------------+\n\nSET @g1 = GeomFromText('Polygon((0 0,0 4,4 4,4 0,0 0))');\nSET @g2 = GeomFromText('Polygon((3 3,3 6,6 6,6 3,3 3))');\nSELECT mbrtouches(@g1,@g2);\n+---------------------+\n| mbrtouches(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/mbrtouches/
+[MBRWithin]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the Minimum Bounding Rectangle of g1 is\nwithin the Minimum Bounding Rectangle of g2. This tests the opposite\nrelationship as MBRContains().\n\nExamples\n--------\n\nSET @g1 = GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))');\nSET @g2 = GeomFromText('Polygon((0 0,0 5,5 5,5 0,0 0))');\nSELECT MBRWithin(@g1,@g2), MBRWithin(@g2,@g1);\n+--------------------+--------------------+\n| MBRWithin(@g1,@g2) | MBRWithin(@g2,@g1) |\n+--------------------+--------------------+\n| 1 | 0 |\n+--------------------+--------------------+\n\nURL: https://mariadb.com/kb/en/mbrwithin/
+[MD5]
+declaration=str
+category=Encryption Functions
+description=Calculates an MD5 128-bit checksum for the string.\n\nThe return value is a 32-hex digit string, and as of MariaDB 5.5, is a\nnonbinary string in the connection character set and collation, determined by\nthe values of the character_set_connection and collation_connection system\nvariables. Before 5.5, the return value was a binary string.\n\nNULL is returned if the argument was NULL.\n\nExamples\n--------\n\nSELECT MD5('testing');\n+----------------------------------+\n| MD5('testing') |\n+----------------------------------+\n| ae2b1fca515949e5d54fb22b8ed95575 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/md5/
+[MEDIUMINT]
+declaration=M
+category=Data Types
+description=A medium-sized integer. The signed range is -8388608 to 8388607. The unsigned\nrange is 0 to 16777215.\n\nZEROFILL pads the integer with zeroes and assumes UNSIGNED (even if UNSIGNED\nis not specified).\n\nINT3 is a synonym for MEDIUMINT.\n\nFor details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE mediumints (a MEDIUMINT,b MEDIUMINT UNSIGNED,c MEDIUMINT\nZEROFILL);\n\nDESCRIBE mediumints;\n+-------+--------------------------------+------+-----+---------+-------+\n| Field | Type | Null | Key | Default | Extra |\n+-------+--------------------------------+------+-----+---------+-------+\n| a | mediumint(9) | YES | | NULL | |\n| b | mediumint(8) unsigned | YES | | NULL | |\n| c | mediumint(8) unsigned zerofill | YES | | NULL | |\n+-------+--------------------------------+------+-----+---------+-------+\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO mediumints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column 'b' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column 'c' at row 1\n\nINSERT INTO mediumints VALUES (-10,10,10);\n\nINSERT INTO mediumints VALUES (8388608,8388608,8388608);\nERROR 1264 (22003): Out of range value for column 'a' at row 1\n\nINSERT INTO mediumints VALUES (8388607,8388608,8388608);\n\nSELECT * FROM mediumints;\n+---------+---------+----------+\n| a | b | c |\n+---------+---------+----------+\n| -10 | 10 | 00000010 |\n| 8388607 | 8388608 | 08388608 |\n+---------+---------+----------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\n ...
+[MICROSECOND]
+declaration=expr
+category=Date and Time Functions
+description=Returns the microseconds from the time or datetime expression expr as a number\nin the range from 0 to 999999.\n\nIf expr is a time with no microseconds, zero is returned, while if expr is a\ndate with no time, zero with a warning is returned.\n\nExamples\n--------\n\nSELECT MICROSECOND('12:00:00.123456');\n+--------------------------------+\n| MICROSECOND('12:00:00.123456') |\n+--------------------------------+\n| 123456 |\n+--------------------------------+\n\nSELECT MICROSECOND('2009-12-31 23:59:59.000010');\n+-------------------------------------------+\n| MICROSECOND('2009-12-31 23:59:59.000010') |\n+-------------------------------------------+\n| 10 |\n+-------------------------------------------+\n\nSELECT MICROSECOND('2013-08-07 12:13:14');\n+------------------------------------+\n| MICROSECOND('2013-08-07 12:13:14') |\n+------------------------------------+\n| 0 |\n+------------------------------------+\n\nSELECT MICROSECOND('2013-08-07');\n+---------------------------+\n| MICROSECOND('2013-08-07') |\n+---------------------------+\n| 0 |\n+---------------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+----------------------------------------------+\n| Level | Code | Message |\n+---------+------+----------------------------------------------+\n| Warning | 1292 | Truncated incorrect time value: '2013-08-07' |\n+---------+------+----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/microsecond/
+[MID]
+declaration=str,pos,len
+category=String Functions
+description=MID(str,pos,len) is a synonym for SUBSTRING(str,pos,len).\n\nExamples\n--------\n\nSELECT MID('abcd',4,1);\n+-----------------+\n| MID('abcd',4,1) |\n+-----------------+\n| d |\n+-----------------+\n\nSELECT MID('abcd',2,2);\n+-----------------+\n| MID('abcd',2,2) |\n+-----------------+\n| bc |\n+-----------------+\n\nA negative starting position:\n\nSELECT MID('abcd',-2,4);\n+------------------+\n| MID('abcd',-2,4) |\n+------------------+\n| cd |\n+------------------+\n\nURL: https://mariadb.com/kb/en/mid/
+[MIN]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the minimum value of expr. MIN() may take a string argument, in which\ncase it returns the minimum string value. The DISTINCT keyword can be used to\nfind the minimum of the distinct values of expr, however, this produces the\nsame result as omitting DISTINCT.\n\nNote that SET and ENUM fields are currently compared by their string value\nrather than their relative position in the set, so MIN() may produce a\ndifferent lowest result than ORDER BY ASC.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nMIN() can be used as a window function.\n\nMIN() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE student (name CHAR(10), test CHAR(10), score TINYINT);\n\nINSERT INTO student VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87), ('Tatiana', 'Tuning', 83);\n\nSELECT name, MIN(score) FROM student GROUP BY name;\n+---------+------------+\n| name | MIN(score) |\n+---------+------------+\n| Chun | 73 |\n| Esben | 31 |\n| Kaolin | 56 |\n| Tatiana | 83 |\n+---------+------------+\n\nMIN() with a string:\n\nSELECT MIN(name) FROM student;\n+-----------+\n| MIN(name) |\n+-----------+\n| Chun |\n+-----------+\n\nBe careful to avoid this common mistake, not grouping correctly and returning\nmismatched data:\n\nSELECT name,test,MIN(score) FROM student;\n+------+------+------------+\n ...
+[MINUTE]
+declaration=time
+category=Date and Time Functions
+description=Returns the minute for time, in the range 0 to 59.\n\nExamples\n--------\n\nSELECT MINUTE('2013-08-03 11:04:03');\n+-------------------------------+\n| MINUTE('2013-08-03 11:04:03') |\n+-------------------------------+\n| 4 |\n+-------------------------------+\n\nSELECT MINUTE ('23:12:50');\n+---------------------+\n| MINUTE ('23:12:50') |\n+---------------------+\n| 12 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/minute/
+[MLineFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MULTILINESTRING value using its WKT representation and SRID.\n\nMLineFromText() and MultiLineStringFromText() are synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_multi_line (g MULTILINESTRING);\nSHOW FIELDS FROM gis_multi_line;\nINSERT INTO gis_multi_line VALUES\n (MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16\n23,16 48))')),\n (MLineFromText('MULTILINESTRING((10 48,10 21,10 0))')),\n (MLineFromWKB(AsWKB(MultiLineString(\n LineString(Point(1, 2), Point(3, 5)),\n LineString(Point(2, 5), Point(5, 8), Point(21, 7))))));\n\nURL: https://mariadb.com/kb/en/mlinefromtext/
+[MLineFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MULTILINESTRING value using its WKB representation and SRID.\n\nMLineFromWKB() and MultiLineStringFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(MLineFromText('MULTILINESTRING((10 48,10 21,10 0),(16\n0,16 23,16 48))'));\n\nSELECT ST_AsText(MLineFromWKB(@g));\n+--------------------------------------------------------+\n| ST_AsText(MLineFromWKB(@g)) |\n+--------------------------------------------------------+\n| MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48)) |\n+--------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/mlinefromwkb/
+[MOD]
+declaration=N,M
+category=Numeric Functions
+description=Modulo operation. Returns the remainder of N divided by M. See also Modulo\nOperator.\n\nIf the ERROR_ON_DIVISION_BY_ZERO SQL_MODE is used, any number modulus zero\nproduces an error. Otherwise, it returns NULL.\n\nThe integer part of a division can be obtained using DIV.\n\nExamples\n--------\n\nSELECT 1042 % 50;\n+-----------+\n| 1042 % 50 |\n+-----------+\n| 42 |\n+-----------+\n\nSELECT MOD(234, 10);\n+--------------+\n| MOD(234, 10) |\n+--------------+\n| 4 |\n+--------------+\n\nSELECT 253 % 7;\n+---------+\n| 253 % 7 |\n+---------+\n| 1 |\n+---------+\n\nSELECT MOD(29,9);\n+-----------+\n| MOD(29,9) |\n+-----------+\n| 2 |\n+-----------+\n\nSELECT 29 MOD 9;\n+----------+\n| 29 MOD 9 |\n+----------+\n| 2 |\n+----------+\n\nURL: https://mariadb.com/kb/en/mod/
+[MONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the month for date in the range 1 to 12 for January to December, or 0\nfor dates such as '0000-00-00' or '2008-00-00' that have a zero month part.\n\nExamples\n--------\n\nSELECT MONTH('2019-01-03');\n+---------------------+\n| MONTH('2019-01-03') |\n+---------------------+\n| 1 |\n+---------------------+\n\nSELECT MONTH('2019-00-03');\n+---------------------+\n| MONTH('2019-00-03') |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/month/
+[MONTHNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the full name of the month for date. The language used for the name is\ncontrolled by the value of the lc_time_names system variable. See server\nlocale for more on the supported locales.\n\nExamples\n--------\n\nSELECT MONTHNAME('2019-02-03');\n+-------------------------+\n| MONTHNAME('2019-02-03') |\n+-------------------------+\n| February |\n+-------------------------+\n\nChanging the locale:\n\nSET lc_time_names = 'fr_CA';\n\nSELECT MONTHNAME('2019-05-21');\n+-------------------------+\n| MONTHNAME('2019-05-21') |\n+-------------------------+\n| mai |\n+-------------------------+\n\nURL: https://mariadb.com/kb/en/monthname/
+[MPointFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MULTIPOINT value using its WKT representation and SRID.\n\nMPointFromText() and MultiPointFromText() are synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_multi_point (g MULTIPOINT);\nSHOW FIELDS FROM gis_multi_point;\nINSERT INTO gis_multi_point VALUES\n (MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)')),\n (MPointFromText('MULTIPOINT(1 1,11 11,11 21,21 21)')),\n (MPointFromWKB(AsWKB(MultiPoint(Point(3, 6), Point(4, 10)))));\n\nURL: https://mariadb.com/kb/en/mpointfromtext/
+[MPointFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MULTIPOINT value using its WKB representation and SRID.\n\nMPointFromWKB() and MultiPointFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(MPointFromText('MultiPoint( 1 1, 2 2, 5 3, 7 2, 9 3, 8 4,\n6 6, 6 9, 4 9, 1 5 )'));\n\nSELECT ST_AsText(MPointFromWKB(@g));\n+-----------------------------------------------------+\n| ST_AsText(MPointFromWKB(@g)) |\n+-----------------------------------------------------+\n| MULTIPOINT(1 1,2 2,5 3,7 2,9 3,8 4,6 6,6 9,4 9,1 5) |\n+-----------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/mpointfromwkb/
+[MPolyFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MULTIPOLYGON value using its WKT representation and SRID.\n\nMPolyFromText() and MultiPolygonFromText() are synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_multi_polygon (g MULTIPOLYGON);\nSHOW FIELDS FROM gis_multi_polygon;\nINSERT INTO gis_multi_polygon VALUES\n (MultiPolygonFromText('MULTIPOLYGON(\n ((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),\n ((59 18,67 18,67 13,59 13,59 18)))')),\n (MPolyFromText('MULTIPOLYGON(\n ((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),\n ((59 18,67 18,67 13,59 13,59 18)))')),\n (MPolyFromWKB(AsWKB(MultiPolygon(Polygon(\n LineString(Point(0, 3), Point(3, 3), Point(3, 0), Point(0, 3)))))));\n\nURL: https://mariadb.com/kb/en/mpolyfromtext/
+[MPolyFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MULTIPOLYGON value using its WKB representation and SRID.\n\nMPolyFromWKB() and MultiPolygonFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(MPointFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28\n26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))'));\n\nSELECT ST_AsText(MPolyFromWKB(@g))\G\n*************************** 1. row ***************************\nST_AsText(MPolyFromWKB(@g)): MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52\n18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))\n\nURL: https://mariadb.com/kb/en/mpolyfromwkb/
+[MULTILINESTRING]
+declaration=ls1,ls2,...
+category=Geometry Constructors
+description=Constructs a WKB MultiLineString value using WKB LineString arguments. If any\nargument is not a WKB LineString, the return value is NULL.\n\nExample\n-------\n\nCREATE TABLE gis_multi_line (g MULTILINESTRING);\nINSERT INTO gis_multi_line VALUES\n (MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16\n48))')),\n (MLineFromText('MULTILINESTRING((10 48,10 21,10 0))')),\n (MLineFromWKB(AsWKB(MultiLineString(LineString(Point(1, 2), \n Point(3, 5)), LineString(Point(2, 5),Point(5, 8),Point(21, 7))))));\n\nURL: https://mariadb.com/kb/en/multilinestring/
+[MULTIPOINT]
+declaration=pt1,pt2,...
+category=Geometry Constructors
+description=Constructs a WKB MultiPoint value using WKB Point arguments. If any argument\nis not a WKB Point, the return value is NULL.\n\nExamples\n--------\n\nSET @g = ST_GEOMFROMTEXT('MultiPoint( 1 1, 2 2, 5 3, 7 2, 9 3, 8 4, 6 6, 6 9,\n4 9, 1 5 )');\n\nCREATE TABLE gis_multi_point (g MULTIPOINT);\nINSERT INTO gis_multi_point VALUES\n (MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)')),\n (MPointFromText('MULTIPOINT(1 1,11 11,11 21,21 21)')),\n (MPointFromWKB(AsWKB(MultiPoint(Point(3, 6), Point(4, 10)))));\n\nURL: https://mariadb.com/kb/en/multipoint/
+[MULTIPOLYGON]
+declaration=poly1,poly2,...
+category=Geometry Constructors
+description=Constructs a WKB MultiPolygon value from a set of WKB Polygon arguments. If\nany argument is not a WKB Polygon, the return value is NULL.\n\nExample\n-------\n\nCREATE TABLE gis_multi_polygon (g MULTIPOLYGON);\nINSERT INTO gis_multi_polygon VALUES\n (MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52\n18,66 23,73 9,48 6,52 18)),\n ((59 18,67 18,67 13,59 13,59 18)))')),\n (MPolyFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66\n23,73 9,48 6,52 18)),\n ((59 18,67 18,67 13,59 13,59 18)))')),\n (MPolyFromWKB(AsWKB(MultiPolygon(Polygon(LineString(\n Point(0, 3), Point(3, 3), Point(3, 0), Point(0, 3)))))));\n\nURL: https://mariadb.com/kb/en/multipolygon/
+[NAME_CONST]
+declaration=name,value
+category=Miscellaneous Functions
+description=Returns the given value. When used to produce a result set column,\nNAME_CONST() causes the column to have the given name. The arguments should be\nconstants.\n\nThis function is used internally when replicating stored procedures. It makes\nlittle sense to use it explicitly in SQL statements, and it was not supposed\nto be used like that.\n\nSELECT NAME_CONST('myname', 14);\n+--------+\n| myname |\n+--------+\n| 14 |\n+--------+\n\nURL: https://mariadb.com/kb/en/name_const/
+[NATURAL_SORT_KEY]
+declaration=str
+category=String Functions
+description=The NATURAL_SORT_KEY function is used for sorting that is closer to natural\nsorting. Strings are sorted in alphabetical order, while numbers are treated\nin a way such that, for example, 10 is greater than 2, whereas in other forms\nof sorting, 2 would be greater than 10, just like z is greater than ya.\n\nThere are multiple natural sort implementations, differing in the way they\nhandle leading zeroes, fractions, i18n, negatives, decimals and so on.\n\nMariaDB's implementation ignores leading zeroes when performing the sort.\n\nYou can use also use NATURAL_SORT_KEY with generated columns. The value is not\nstored permanently in the table. When using a generated column, the virtual\ncolumn must be longer than the base column to cater for embedded numbers in\nthe string and MDEV-24582.\n\nExamples\n--------\n\nStrings and Numbers\n-------------------\n\nCREATE TABLE t1 (c TEXT);\n\nINSERT INTO t1 VALUES ('b1'),('a2'),('a11'),('a1');\n\nSELECT c FROM t1;\n+------+\n| c |\n+------+\n| b1 |\n| a2 |\n| a11 |\n| a1 |\n+------+\n\nSELECT c FROM t1 ORDER BY c;\n+------+\n| c |\n+------+\n| a1 |\n| a11 |\n| a2 |\n| b1 |\n+------+\n\nUnsorted, regular sort and natural sort:\n\nTRUNCATE t1;\n\nINSERT INTO t1 VALUES \n ...
+[NOW]
+declaration=[precision]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context. The value is expressed in the current time zone.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nNOW() (or its synonyms) can be used as the default value for TIMESTAMP columns\nas well as, since MariaDB 10.0.1, DATETIME columns. Before MariaDB 10.0.1, it\nwas only possible for a single TIMESTAMP column per table to contain the\nCURRENT_TIMESTAMP as its default.\n\nWhen displayed in the INFORMATION_SCHEMA.COLUMNS table, a default CURRENT\nTIMESTAMP is displayed as CURRENT_TIMESTAMP up until MariaDB 10.2.2, and as\ncurrent_timestamp() from MariaDB 10.2.3, due to to MariaDB 10.2 accepting\nexpressions in the DEFAULT clause.\n\nChanging the timestamp system variable with a SET timestamp statement affects\nthe value returned by NOW(), but not by SYSDATE().\n\nExamples\n--------\n\nSELECT NOW();\n+---------------------+\n| NOW() |\n+---------------------+\n| 2010-03-27 13:13:25 |\n+---------------------+\n\nSELECT NOW() + 0;\n+-----------------------+\n| NOW() + 0 |\n+-----------------------+\n| 20100327131329.000000 |\n+-----------------------+\n\nWith precision:\n\nSELECT CURRENT_TIMESTAMP(2);\n+------------------------+\n| CURRENT_TIMESTAMP(2) |\n+------------------------+\n| 2018-07-10 09:47:26.24 |\n+------------------------+\n\nUsed as a default TIMESTAMP:\n\nCREATE TABLE t (createdTS TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);\n\n ...
+[NTH_VALUE]
+declaration=expr[, num_row]
+category=Window Functions
+description=The NTH_VALUE function returns the value evaluated at row number num_row of\nthe window frame, starting from 1, or NULL if the row does not exist.\n\nURL: https://mariadb.com/kb/en/nth_value/
+[NTILE]
+declaration=expr
+category=Window Functions
+description=NTILE() is a window function that returns an integer indicating which group a\ngiven row falls into. The number of groups is specified in the argument\n(expr), starting at one. Ordered rows in the partition are divided into the\nspecified number of groups with as equal a size as possible.\n\nExamples\n--------\n\ncreate table t1 (\n pk int primary key,\n a int,\n b int\n );\n\ninsert into t1 values\n (11 , 0, 10),\n (12 , 0, 10),\n (13 , 1, 10),\n (14 , 1, 10),\n (18 , 2, 10),\n (15 , 2, 20),\n (16 , 2, 20),\n (17 , 2, 20),\n (19 , 4, 20),\n (20 , 4, 20);\n\nselect pk, a, b,\n ntile(1) over (order by pk)\n from t1;\n+----+------+------+-----------------------------+\n| pk | a | b | ntile(1) over (order by pk) |\n+----+------+------+-----------------------------+\n| 11 | 0 | 10 | 1 |\n| 12 | 0 | 10 | 1 |\n| 13 | 1 | 10 | 1 |\n| 14 | 1 | 10 | 1 |\n| 15 | 2 | 20 | 1 |\n| 16 | 2 | 20 | 1 |\n| 17 | 2 | 20 | 1 |\n| 18 | 2 | 10 | 1 |\n| 19 | 4 | 20 | 1 |\n| 20 | 4 | 20 | 1 |\n+----+------+------+-----------------------------+\n\nselect pk, a, b,\n ntile(4) over (order by pk)\n from t1;\n+----+------+------+-----------------------------+\n| pk | a | b | ntile(4) over (order by pk) |\n+----+------+------+-----------------------------+\n ...
+[NULLIF]
+declaration=expr1,expr2
+category=Control Flow Functions
+description=Returns NULL if expr1 = expr2 is true, otherwise returns expr1. This is the\nsame as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.\n\nExamples\n--------\n\nSELECT NULLIF(1,1);\n+-------------+\n| NULLIF(1,1) |\n+-------------+\n| NULL |\n+-------------+\n\nSELECT NULLIF(1,2);\n+-------------+\n| NULLIF(1,2) |\n+-------------+\n| 1 |\n+-------------+\n\nURL: https://mariadb.com/kb/en/nullif/
+[NVL2]
+declaration=expr1,expr2,expr3
+category=Control Flow Functions
+description=The NVL2 function returns a value based on whether a specified expression is\nNULL or not. If expr1 is not NULL, then NVL2 returns expr2. If expr1 is NULL,\nthen NVL2 returns expr3.\n\nExamples\n--------\n\nSELECT NVL2(NULL,1,2);\n+----------------+\n| NVL2(NULL,1,2) |\n+----------------+\n| 2 |\n+----------------+\n\nSELECT NVL2('x',1,2);\n+---------------+\n| NVL2('x',1,2) |\n+---------------+\n| 1 |\n+---------------+\n\nURL: https://mariadb.com/kb/en/nvl2/
+[OCT]
+declaration=N
+category=Numeric Functions
+description=Returns a string representation of the octal value of N, where N is a longlong\n(BIGINT) number. This is equivalent to CONV(N,10,8). Returns NULL if N is NULL.\n\nExamples\n--------\n\nSELECT OCT(34);\n+---------+\n| OCT(34) |\n+---------+\n| 42 |\n+---------+\n\nSELECT OCT(12);\n+---------+\n| OCT(12) |\n+---------+\n| 14 |\n+---------+\n\nURL: https://mariadb.com/kb/en/oct/
+[OCTET_LENGTH]
+declaration=str
+category=String Functions
+description=OCTET_LENGTH() returns the length of the given string, in octets (bytes). This\nis a synonym for LENGTHB(), and, when Oracle mode from MariaDB 10.3 is not\nset, a synonym for LENGTH().\n\nA multi-byte character counts as multiple bytes. This means that for a string\ncontaining five two-byte characters, OCTET_LENGTH() returns 10, whereas\nCHAR_LENGTH() returns 5.\n\nIf str is not a string value, it is converted into a string. If str is NULL,\nthe function returns NULL.\n\nExamples\n--------\n\nWhen Oracle mode from MariaDB 10.3 is not set:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 2 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nIn Oracle mode from MariaDB 10.3:\n\nSELECT CHAR_LENGTH('π'), LENGTH('π'), LENGTHB('π'), OCTET_LENGTH('π');\n+-------------------+--------------+---------------+--------------------+\n| CHAR_LENGTH('π') | LENGTH('π') | LENGTHB('π') | OCTET_LENGTH('π') |\n+-------------------+--------------+---------------+--------------------+\n| 1 | 1 | 2 | 2 |\n+-------------------+--------------+---------------+--------------------+\n\nURL: https://mariadb.com/kb/en/octet_length/
+[OLD_PASSWORD]
+declaration=str
+category=Encryption Functions
+description=OLD_PASSWORD() was added to MySQL when the implementation of PASSWORD() was\nchanged to improve security. OLD_PASSWORD() returns the value of the old\n(pre-MySQL 4.1) implementation of PASSWORD() as a string, and is intended to\npermit you to reset passwords for any pre-4.1 clients that need to connect to\na more recent MySQL server version, or any version of MariaDB, without locking\nthem out.\n\nAs of MariaDB 5.5, the return value is a nonbinary string in the connection\ncharacter set and collation, determined by the values of the\ncharacter_set_connection and collation_connection system variables. Before\n5.5, the return value was a binary string.\n\nThe return value is 16 bytes in length, or NULL if the argument was NULL.\n\nURL: https://mariadb.com/kb/en/old_password/
+[ORD]
+declaration=str
+category=String Functions
+description=If the leftmost character of the string str is a multi-byte character, returns\nthe code for that character, calculated from the numeric values of its\nconstituent bytes using this formula:\n\n(1st byte code)\n+ (2nd byte code x 256)\n+ (3rd byte code x 256 x 256) ...\n\nIf the leftmost character is not a multi-byte character, ORD() returns the\nsame value as the ASCII() function.\n\nExamples\n--------\n\nSELECT ORD('2');\n+----------+\n| ORD('2') |\n+----------+\n| 50 |\n+----------+\n\nURL: https://mariadb.com/kb/en/ord/
+[OVERLAPS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether g1 spatially overlaps g2. The term\nspatially overlaps is used if two geometries intersect and their intersection\nresults in a geometry of the same dimension but not equal to either of the\ngiven geometries.\n\nOVERLAPS() is based on the original MySQL implementation and uses object\nbounding rectangles, while ST_OVERLAPS() uses object shapes.\n\nURL: https://mariadb.com/kb/en/overlaps/
+[PASSWORD]
+declaration=str
+category=Encryption Functions
+description=The PASSWORD() function is used for hashing passwords for use in\nauthentication by the MariaDB server. It is not intended for use in other\napplications.\n\nCalculates and returns a hashed password string from the plaintext password\nstr. Returns an empty string (>= MariaDB 10.0.4) if the argument was NULL.\n\nThe return value is a nonbinary string in the connection character set and\ncollation, determined by the values of the character_set_connection and\ncollation_connection system variables.\n\nThis is the function that is used for hashing MariaDB passwords for storage in\nthe Password column of the user table (see privileges), usually used with the\nSET PASSWORD statement. It is not intended for use in other applications.\n\nUntil MariaDB 10.3, the return value is 41-bytes in length, and the first\ncharacter is always '*'. From MariaDB 10.4, the function takes into account\nthe authentication plugin where applicable (A CREATE USER or SET PASSWORD\nstatement). For example, when used in conjunction with a user authenticated by\nthe ed25519 plugin, the statement will create a longer hash:\n\nCREATE USER edtest@localhost IDENTIFIED VIA ed25519 USING PASSWORD('secret');\n\nCREATE USER edtest2@localhost IDENTIFIED BY 'secret';\n\nSELECT CONCAT(user, '@', host, ' => ', JSON_DETAILED(priv)) FROM\nmysql.global_priv\n WHERE user LIKE 'edtest%'\G\n*************************** 1. row ***************************\nCONCAT(user, '@', host, ' => ', JSON_DETAILED(priv)): edtest@localhost => {\n...\n "plugin": "ed25519",\n "authentication_string": "ZIgUREUg5PVgQ6LskhXmO+eZLS0nC8be6HPjYWR4YJY",\n...\n}\n*************************** 2. row ***************************\nCONCAT(user, '@', host, ' => ', JSON_DETAILED(priv)): edtest2@localhost => {\n...\n "plugin": "mysql_native_password",\n "authentication_string": "*14E65567ABDB5135D0CFD9A70B3032C179A49EE7",\n...\n}\n\nThe behavior of this function is affected by the value of the old_passwords\nsystem variable. If this is set to 1 (0 is default), MariaDB reverts to using\nthe mysql_old_password authentication plugin by default for newly created\nusers and passwords.\n\nExamples\n--------\n ...
+[PERCENTILE_CONT]
+declaration=
+category=Window Functions
+description=PERCENTILE_CONT() (standing for continuous percentile) is a window function\nwhich returns a value which corresponds to the given fraction in the sort\norder. If required, it will interpolate between adjacent input items.\n\nEssentially, the following process is followed to find the value to return:\n\n* Get the number of rows in the partition, denoted by N\n* RN = p*(N-1), where p denotes the argument to the PERCENTILE_CONT function\n* calculate the FRN(floor row number) and CRN(column row number for the group(\nFRN= floor(RN) and CRN = ceil(RN))\n* look up rows FRN and CRN\n* If (CRN = FRN = RN) then the result is (value of expression from row at RN)\n* Otherwise the result is\n* (CRN - RN) * (value of expression for row at FRN) +\n* (RN - FRN) * (value of expression for row at CRN)\n\nThe MEDIAN function is a specific case of PERCENTILE_CONT, equivalent to\nPERCENTILE_CONT(0.5).\n\nExamples\n--------\n\nCREATE TABLE book_rating (name CHAR(30), star_rating TINYINT);\n\nINSERT INTO book_rating VALUES ('Lord of the Ladybirds', 5);\nINSERT INTO book_rating VALUES ('Lord of the Ladybirds', 3);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 1);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 2);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 5);\n\nSELECT name, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY star_rating) \n OVER (PARTITION BY name) AS pc\n FROM book_rating;\n+-----------------------+--------------+\n| name | pc |\n+-----------------------+--------------+\n| Lord of the Ladybirds | 4.0000000000 |\n| Lord of the Ladybirds | 4.0000000000 |\n| Lady of the Flies | 2.0000000000 |\n| Lady of the Flies | 2.0000000000 |\n| Lady of the Flies | 2.0000000000 |\n+-----------------------+--------------+\n\nSELECT name, PERCENTILE_CONT(1) WITHIN GROUP (ORDER BY star_rating) \n OVER (PARTITION BY name) AS pc\n FROM book_rating;\n+-----------------------+--------------+\n| name | pc |\n+-----------------------+--------------+\n| Lord of the Ladybirds | 5.0000000000 |\n ...
+[PERCENTILE_DISC]
+declaration=
+category=Window Functions
+description=PERCENTILE_DISC() (standing for discrete percentile) is a window function\nwhich returns the first value in the set whose ordered position is the same or\nmore than the specified fraction.\n\nEssentially, the following process is followed to find the value to return:\n\n* Get the number of rows in the partition.\n* Walk through the partition, in order, until finding the the first row with\nCUME_DIST() >= function_argument.\n\nExamples\n--------\n\nCREATE TABLE book_rating (name CHAR(30), star_rating TINYINT);\n\nINSERT INTO book_rating VALUES ('Lord of the Ladybirds', 5);\nINSERT INTO book_rating VALUES ('Lord of the Ladybirds', 3);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 1);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 2);\nINSERT INTO book_rating VALUES ('Lady of the Flies', 5);\n\nSELECT name, PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY star_rating)\n OVER (PARTITION BY name) AS pc FROM book_rating;\n+-----------------------+------+\n| name | pc |\n+-----------------------+------+\n| Lord of the Ladybirds | 3 |\n| Lord of the Ladybirds | 3 |\n| Lady of the Flies | 2 |\n| Lady of the Flies | 2 |\n| Lady of the Flies | 2 |\n+-----------------------+------+\n5 rows in set (0.000 sec)\n\nSELECT name, PERCENTILE_DISC(0) WITHIN GROUP (ORDER BY star_rating) \n OVER (PARTITION BY name) AS pc FROM book_rating;\n+-----------------------+------+\n| name | pc |\n+-----------------------+------+\n| Lord of the Ladybirds | 3 |\n| Lord of the Ladybirds | 3 |\n| Lady of the Flies | 1 |\n| Lady of the Flies | 1 |\n| Lady of the Flies | 1 |\n+-----------------------+------+\n5 rows in set (0.000 sec)\n\nSELECT name, PERCENTILE_DISC(1) WITHIN GROUP (ORDER BY star_rating) \n OVER (PARTITION BY name) AS pc FROM book_rating;\n+-----------------------+------+\n ...
+[PERCENT_RANK]
+declaration=
+category=Window Functions
+description=PERCENT_RANK() is a window function that returns the relative percent rank of\na given row. The following formula is used to calculate the percent rank:\n\n(rank - 1) / (number of rows in the window or partition - 1)\n\nExamples\n--------\n\ncreate table t1 (\n pk int primary key,\n a int,\n b int\n);\n\ninsert into t1 values\n( 1 , 0, 10),\n( 2 , 0, 10),\n( 3 , 1, 10),\n( 4 , 1, 10),\n( 8 , 2, 10),\n( 5 , 2, 20),\n( 6 , 2, 20),\n( 7 , 2, 20),\n( 9 , 4, 20),\n(10 , 4, 20);\n\nselect pk, a, b,\n rank() over (order by a) as rank,\n percent_rank() over (order by a) as pct_rank,\n cume_dist() over (order by a) as cume_dist\nfrom t1;\n+----+------+------+------+--------------+--------------+\n| pk | a | b | rank | pct_rank | cume_dist |\n+----+------+------+------+--------------+--------------+\n| 1 | 0 | 10 | 1 | 0.0000000000 | 0.2000000000 |\n| 2 | 0 | 10 | 1 | 0.0000000000 | 0.2000000000 |\n| 3 | 1 | 10 | 3 | 0.2222222222 | 0.4000000000 |\n| 4 | 1 | 10 | 3 | 0.2222222222 | 0.4000000000 |\n| 5 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 6 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 7 | 2 | 20 | 5 | 0.4444444444 | 0.8000000000 |\n| 8 | 2 | 10 | 5 | 0.4444444444 | 0.8000000000 |\n| 9 | 4 | 20 | 9 | 0.8888888889 | 1.0000000000 |\n| 10 | 4 | 20 | 9 | 0.8888888889 | 1.0000000000 |\n+----+------+------+------+--------------+--------------+\n\nselect pk, a, b,\n percent_rank() over (order by pk) as pct_rank,\n cume_dist() over (order by pk) as cume_dist\nfrom t1 order by pk;\n ...
+[PERIOD_ADD]
+declaration=P,N
+category=Date and Time Functions
+description=Adds N months to period P. P is in the format YYMM or YYYYMM, and is not a\ndate value. If P contains a two-digit year, values from 00 to 69 are converted\nto from 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nReturns a value in the format YYYYMM.\n\nExamples\n--------\n\nSELECT PERIOD_ADD(200801,2);\n+----------------------+\n| PERIOD_ADD(200801,2) |\n+----------------------+\n| 200803 |\n+----------------------+\n\nSELECT PERIOD_ADD(6910,2);\n+--------------------+\n| PERIOD_ADD(6910,2) |\n+--------------------+\n| 206912 |\n+--------------------+\n\nSELECT PERIOD_ADD(7010,2);\n+--------------------+\n| PERIOD_ADD(7010,2) |\n+--------------------+\n| 197012 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/period_add/
+[PERIOD_DIFF]
+declaration=P1,P2
+category=Date and Time Functions
+description=Returns the number of months between periods P1 and P2. P1 and P2 can be in\nthe format YYMM or YYYYMM, and are not date values.\n\nIf P1 or P2 contains a two-digit year, values from 00 to 69 are converted to\nfrom 2000 to 2069, while values from 70 are converted to 1970 upwards.\n\nExamples\n--------\n\nSELECT PERIOD_DIFF(200802,200703);\n+----------------------------+\n| PERIOD_DIFF(200802,200703) |\n+----------------------------+\n| 11 |\n+----------------------------+\n\nSELECT PERIOD_DIFF(6902,6803);\n+------------------------+\n| PERIOD_DIFF(6902,6803) |\n+------------------------+\n| 11 |\n+------------------------+\n\nSELECT PERIOD_DIFF(7002,6803);\n+------------------------+\n| PERIOD_DIFF(7002,6803) |\n+------------------------+\n| -1177 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/period_diff/
+[PI]
+declaration=
+category=Numeric Functions
+description=Returns the value of π (pi). The default number of decimal places displayed is\nsix, but MariaDB uses the full double-precision value internally.\n\nExamples\n--------\n\nSELECT PI();\n+----------+\n| PI() |\n+----------+\n| 3.141593 |\n+----------+\n\nSELECT PI()+0.0000000000000000000000;\n+-------------------------------+\n| PI()+0.0000000000000000000000 |\n+-------------------------------+\n| 3.1415926535897931159980 |\n+-------------------------------+\n\nURL: https://mariadb.com/kb/en/pi/
+[POINT]
+declaration=x,y
+category=Geometry Constructors
+description=Constructs a WKB Point using the given coordinates.\n\nExamples\n--------\n\nSET @g = ST_GEOMFROMTEXT('Point(1 1)');\n\nCREATE TABLE gis_point (g POINT);\nINSERT INTO gis_point VALUES\n (PointFromText('POINT(10 10)')),\n (PointFromText('POINT(20 10)')),\n (PointFromText('POINT(20 20)')),\n (PointFromWKB(AsWKB(PointFromText('POINT(10 20)'))));\n\nURL: https://mariadb.com/kb/en/point/
+[POLYGON]
+declaration=ls1,ls2,...
+category=Geometry Constructors
+description=Constructs a WKB Polygon value from a number of WKB LineString arguments. If\nany argument does not represent the WKB of a LinearRing (that is, not a closed\nand simple LineString) the return value is NULL.\n\nNote that according to the OpenGIS standard, a POLYGON should have exactly one\nExteriorRing and all other rings should lie within that ExteriorRing and thus\nbe the InteriorRings. Practically, however, some systems, including MariaDB's,\npermit polygons to have several 'ExteriorRings'. In the case of there being\nmultiple, non-overlapping exterior rings ST_NUMINTERIORRINGS() will return 1.\n\nExamples\n--------\n\nSET @g = ST_GEOMFROMTEXT('POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1 1))');\n\nCREATE TABLE gis_polygon (g POLYGON);\nINSERT INTO gis_polygon VALUES\n (PolygonFromText('POLYGON((10 10,20 10,20 20,10 20,10 10))')),\n (PolyFromText('POLYGON((0 0,50 0,50 50,0 50,0 0), (10 10,20 10,20 20,10\n20,10 10))')),\n (PolyFromWKB(AsWKB(Polygon(LineString(Point(0, 0), Point(30, 0), Point(30,\n30), Point(0, 0))))));\n\nNon-overlapping 'polygon':\n\nSELECT ST_NumInteriorRings(ST_PolyFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),\n (-1 -1,-5 -1,-5 -5,-1 -5,-1 -1))')) AS NumInteriorRings;\n+------------------+\n| NumInteriorRings |\n+------------------+\n| 1 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/polygon/
+[POSITION]
+declaration=substr IN str
+category=String Functions
+description=POSITION(substr IN str) is a synonym for LOCATE(substr,str).\n\nIt's part of ODBC 3.0.\n\nURL: https://mariadb.com/kb/en/position/
+[POW]
+declaration=X,Y
+category=Numeric Functions
+description=Returns the value of X raised to the power of Y.\n\nPOWER() is a synonym.\n\nExamples\n--------\n\nSELECT POW(2,3);\n+----------+\n| POW(2,3) |\n+----------+\n| 8 |\n+----------+\n\nSELECT POW(2,-2);\n+-----------+\n| POW(2,-2) |\n+-----------+\n| 0.25 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/pow/
+[POWER]
+declaration=X,Y
+category=Numeric Functions
+description=This is a synonym for POW(), which returns the value of X raised to the power\nof Y.\n\nURL: https://mariadb.com/kb/en/power/
+[QUARTER]
+declaration=date
+category=Date and Time Functions
+description=Returns the quarter of the year for date, in the range 1 to 4. Returns 0 if\nmonth contains a zero value, or NULL if the given value is not otherwise a\nvalid date (zero values are accepted).\n\nExamples\n--------\n\nSELECT QUARTER('2008-04-01');\n+-----------------------+\n| QUARTER('2008-04-01') |\n+-----------------------+\n| 2 |\n+-----------------------+\n\nSELECT QUARTER('2019-00-01');\n+-----------------------+\n| QUARTER('2019-00-01') |\n+-----------------------+\n| 0 |\n+-----------------------+\n\nURL: https://mariadb.com/kb/en/quarter/
+[QUOTE]
+declaration=str
+category=String Functions
+description=Quotes a string to produce a result that can be used as a properly escaped\ndata value in an SQL statement. The string is returned enclosed by single\nquotes and with each instance of single quote ("'"), backslash ("\"), ASCII\nNUL, and Control-Z preceded by a backslash. If the argument is NULL, the\nreturn value is the word "NULL" without enclosing single quotes.\n\nExamples\n--------\n\nSELECT QUOTE("Don't!");\n+-----------------+\n| QUOTE("Don't!") |\n+-----------------+\n| 'Don\'t!' |\n+-----------------+\n\nSELECT QUOTE(NULL); \n+-------------+\n| QUOTE(NULL) |\n+-------------+\n| NULL |\n+-------------+\n\nURL: https://mariadb.com/kb/en/quote/
+[RADIANS]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from degrees to radians. Note that π radians\nequals 180 degrees.\n\nThis is the converse of the DEGREES() function.\n\nExamples\n--------\n\nSELECT RADIANS(45);\n+-------------------+\n| RADIANS(45) |\n+-------------------+\n| 0.785398163397448 |\n+-------------------+\n\nSELECT RADIANS(90);\n+-----------------+\n| RADIANS(90) |\n+-----------------+\n| 1.5707963267949 |\n+-----------------+\n\nSELECT RADIANS(PI());\n+--------------------+\n| RADIANS(PI()) |\n+--------------------+\n| 0.0548311355616075 |\n+--------------------+\n\nSELECT RADIANS(180);\n+------------------+\n| RADIANS(180) |\n+------------------+\n| 3.14159265358979 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/radians/
+[RAND]
+declaration=
+category=Numeric Functions
+description=Returns a random DOUBLE precision floating point value v in the range 0 <= v <\n1.0. If a constant integer argument N is specified, it is used as the seed\nvalue, which produces a repeatable sequence of column values. In the example\nbelow, note that the sequences of values produced by RAND(3) is the same both\nplaces where it occurs.\n\nIn a WHERE clause, RAND() is evaluated each time the WHERE is executed.\n\nStatements using the RAND() function are not safe for statement-based\nreplication.\n\nPractical uses\n--------------\n\nThe expression to get a random integer from a given range is the following:\n\nFLOOR(min_value + RAND() * (max_value - min_value +1))\n\nRAND() is often used to read random rows from a table, as follows:\n\nSELECT * FROM my_table ORDER BY RAND() LIMIT 10;\n\nNote, however, that this technique should never be used on a large table as it\nwill be extremely slow. MariaDB will read all rows in the table, generate a\nrandom value for each of them, order them, and finally will apply the LIMIT\nclause.\n\nExamples\n--------\n\nCREATE TABLE t (i INT);\n\nINSERT INTO t VALUES(1),(2),(3);\n\nSELECT i, RAND() FROM t;\n+------+-------------------+\n| i | RAND() |\n+------+-------------------+\n| 1 | 0.255651095188829 |\n| 2 | 0.833920199269355 |\n| 3 | 0.40264774151393 |\n+------+-------------------+\n\nSELECT i, RAND(3) FROM t;\n+------+-------------------+\n| i | RAND(3) |\n+------+-------------------+\n| 1 | 0.90576975597606 |\n| 2 | 0.373079058130345 |\n| 3 | 0.148086053457191 |\n ...
+[RANDOM_BYTES]
+declaration=length
+category=Encryption Functions
+description=Given a length from 1 to 1024, generates a binary string of length consisting\nof random bytes generated by the SSL library's random number generator.\n\nSee the RAND_bytes() function documentation of your SSL library for\ninformation on the random number generator. In the case of OpenSSL, a\ncryptographically secure pseudo random generator (CSPRNG) is used.\n\nStatements containing the RANDOM_BYTES function are unsafe for statement-based\nreplication.\n\nAn error occurs if length is outside the range 1 to 1024.\n\nURL: https://mariadb.com/kb/en/random_bytes/
+[RANK]
+declaration=
+category=Window Functions
+description=RANK() is a window function that displays the number of a given row, starting\nat one and following the ORDER BY sequence of the window function, with\nidentical values receiving the same result. It is similar to the ROW_NUMBER()\nfunction except that in that function, identical values will receive a\ndifferent row number for each result.\n\nExamples\n--------\n\nThe distinction between DENSE_RANK(), RANK() and ROW_NUMBER():\n\nCREATE TABLE student(course VARCHAR(10), mark int, name varchar(10));\n\nINSERT INTO student VALUES \n ('Maths', 60, 'Thulile'),\n ('Maths', 60, 'Pritha'),\n ('Maths', 70, 'Voitto'),\n ('Maths', 55, 'Chun'),\n ('Biology', 60, 'Bilal'),\n ('Biology', 70, 'Roger');\n\nSELECT \n RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS rank,\n DENSE_RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS dense_rank,\n ROW_NUMBER() OVER (PARTITION BY course ORDER BY mark DESC) AS row_num,\n course, mark, name\nFROM student ORDER BY course, mark DESC;\n+------+------------+---------+---------+------+---------+\n| rank | dense_rank | row_num | course | mark | name |\n+------+------------+---------+---------+------+---------+\n| 1 | 1 | 1 | Biology | 70 | Roger |\n| 2 | 2 | 2 | Biology | 60 | Bilal |\n| 1 | 1 | 1 | Maths | 70 | Voitto |\n| 2 | 2 | 2 | Maths | 60 | Thulile |\n| 2 | 2 | 3 | Maths | 60 | Pritha |\n| 4 | 3 | 4 | Maths | 55 | Chun |\n+------+------------+---------+---------+------+---------+\n\nURL: https://mariadb.com/kb/en/rank/
+[REGEXP_INSTR]
+declaration=subject, pattern
+category=String Functions
+description=Returns the position of the first occurrence of the regular expression pattern\nin the string subject, or 0 if pattern was not found.\n\nThe positions start with 1 and are measured in characters (i.e. not in bytes),\nwhich is important for multi-byte character sets. You can cast a multi-byte\ncharacter set to BINARY to get offsets in bytes.\n\nThe function follows the case sensitivity rules of the effective collation.\nMatching is performed case insensitively for case insensitive collations, and\ncase sensitively for case sensitive collations and for binary data.\n\nThe collation case sensitivity can be overwritten using the (?i) and (?-i)\nPCRE flags.\n\nMariaDB uses the PCRE regular expression library for enhanced regular\nexpression performance, and REGEXP_INSTR was introduced as part of this\nenhancement.\n\nExamples\n--------\n\nSELECT REGEXP_INSTR('abc','b');\n-> 2\n\nSELECT REGEXP_INSTR('abc','x');\n-> 0\n\nSELECT REGEXP_INSTR('BJÖRN','N');\n-> 5\n\nCasting a multi-byte character set as BINARY to get offsets in bytes:\n\nSELECT REGEXP_INSTR(BINARY 'BJÖRN','N') AS cast_utf8_to_binary;\n-> 6\n\nCase sensitivity:\n\nSELECT REGEXP_INSTR('ABC','b');\n-> 2\n\nSELECT REGEXP_INSTR('ABC' COLLATE utf8_bin,'b');\n-> 0\n\nSELECT REGEXP_INSTR(BINARY'ABC','b');\n-> 0\n\nSELECT REGEXP_INSTR('ABC','(?-i)b');\n-> 0\n\nSELECT REGEXP_INSTR('ABC' COLLATE utf8_bin,'(?i)b');\n-> 2\n\nURL: https://mariadb.com/kb/en/regexp_instr/
+[REGEXP_REPLACE]
+declaration=subject, pattern, replace
+category=String Functions
+description=REGEXP_REPLACE returns the string subject with all occurrences of the regular\nexpression pattern replaced by the string replace. If no occurrences are\nfound, then subject is returned as is.\n\nThe replace string can have backreferences to the subexpressions in the form\n\N, where N is a number from 1 to 9.\n\nThe function follows the case sensitivity rules of the effective collation.\nMatching is performed case insensitively for case insensitive collations, and\ncase sensitively for case sensitive collations and for binary data.\n\nThe collation case sensitivity can be overwritten using the (?i) and (?-i)\nPCRE flags.\n\nMariaDB uses the PCRE regular expression library for enhanced regular\nexpression performance, and REGEXP_REPLACE was introduced as part of this\nenhancement.\n\nThe default_regex_flags variable addresses the remaining compatibilities\nbetween PCRE and the old regex library.\n\nExamples\n--------\n\nSELECT REGEXP_REPLACE('ab12cd','[0-9]','') AS remove_digits;\n-> abcd\n\nSELECT\nREGEXP_REPLACE('titlebody',\n'<.+?>',' ')\nAS strip_html;\n-> title body\n\nBackreferences to the subexpressions in the form \N, where N is a number from\n1 to 9:\n\nSELECT REGEXP_REPLACE('James Bond','^(.*) (.*)$','\\2, \\1') AS reorder_name;\n-> Bond, James\n\nCase insensitive and case sensitive matches:\n\nSELECT REGEXP_REPLACE('ABC','b','-') AS case_insensitive;\n-> A-C\n\nSELECT REGEXP_REPLACE('ABC' COLLATE utf8_bin,'b','-') AS case_sensitive;\n-> ABC\n\nSELECT REGEXP_REPLACE(BINARY 'ABC','b','-') AS binary_data;\n-> ABC\n\n ...
+[REGEXP_SUBSTR]
+declaration=subject,pattern
+category=String Functions
+description=Returns the part of the string subject that matches the regular expression\npattern, or an empty string if pattern was not found.\n\nThe function follows the case sensitivity rules of the effective collation.\nMatching is performed case insensitively for case insensitive collations, and\ncase sensitively for case sensitive collations and for binary data.\n\nThe collation case sensitivity can be overwritten using the (?i) and (?-i)\nPCRE flags.\n\nMariaDB uses the PCRE regular expression library for enhanced regular\nexpression performance, and REGEXP_SUBSTR was introduced as part of this\nenhancement.\n\nThe default_regex_flags variable addresses the remaining compatibilities\nbetween PCRE and the old regex library.\n\nExamples\n--------\n\nSELECT REGEXP_SUBSTR('ab12cd','[0-9]+');\n-> 12\n\nSELECT REGEXP_SUBSTR(\n 'See https://mariadb.org/en/foundation/ for details',\n 'https?://[^/]*');\n-> https://mariadb.org\n\nSELECT REGEXP_SUBSTR('ABC','b');\n-> B\n\nSELECT REGEXP_SUBSTR('ABC' COLLATE utf8_bin,'b');\n->\n\nSELECT REGEXP_SUBSTR(BINARY'ABC','b');\n->\n\nSELECT REGEXP_SUBSTR('ABC','(?i)b');\n-> B\n\nSELECT REGEXP_SUBSTR('ABC' COLLATE utf8_bin,'(?+i)b');\n-> B\n\nURL: https://mariadb.com/kb/en/regexp_substr/
+[RELEASE_ALL_LOCKS]
+declaration=
+category=Miscellaneous Functions
+description=Releases all named locks held by the current session. Returns the number of\nlocks released, or 0 if none were held.\n\nStatements using the RELEASE_ALL_LOCKS function are not safe for\nstatement-based replication.\n\nExamples\n--------\n\nSELECT RELEASE_ALL_LOCKS();\n+---------------------+\n| RELEASE_ALL_LOCKS() | \n+---------------------+\n| 0 |\n+---------------------+\n\nSELECT GET_LOCK('lock1',10);\n+----------------------+\n| GET_LOCK('lock1',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSELECT RELEASE_ALL_LOCKS();\n+---------------------+\n| RELEASE_ALL_LOCKS() | \n+---------------------+\n| 1 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/release_all_locks/
+[RELEASE_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Releases the lock named by the string str that was obtained with GET_LOCK().\nReturns 1 if the lock was released, 0 if the lock was not established by this\nthread (in which case the lock is not released), and NULL if the named lock\ndid not exist. The lock does not exist if it was never obtained by a call to\nGET_LOCK() or if it has previously been released.\n\nstr is case insensitive. If str is an empty string or NULL, RELEASE_LOCK()\nreturns NULL and does nothing.\n\nStatements using the RELEASE_LOCK function are not safe for statement-based\nreplication.\n\nThe DO statement is convenient to use with RELEASE_LOCK().\n\nExamples\n--------\n\nConnection1:\n\nSELECT GET_LOCK('lock1',10);\n+----------------------+\n| GET_LOCK('lock1',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 2:\n\nSELECT GET_LOCK('lock2',10);\n+----------------------+\n| GET_LOCK('lock2',10) |\n+----------------------+\n| 1 |\n+----------------------+\n\nConnection 1:\n\nSELECT RELEASE_LOCK('lock1'), RELEASE_LOCK('lock2'), RELEASE_LOCK('lock3');\n+-----------------------+-----------------------+-----------------------+\n| RELEASE_LOCK('lock1') | RELEASE_LOCK('lock2') | RELEASE_LOCK('lock3') |\n+-----------------------+-----------------------+-----------------------+\n| 1 | 0 | NULL |\n+-----------------------+-----------------------+-----------------------+\n\nIt is possible to hold the same lock recursively. This example is viewed using\nthe metadata_lock_info plugin:\n\nSELECT GET_LOCK('lock3',10);\n+----------------------+\n| GET_LOCK('lock3',10) |\n ...
+[RETURN]
+declaration=SELECT COUNT(DISTINCT User
+category=Compound Statements
+description=END;\n\nURL: https://mariadb.com/kb/en/return/
+[REVERSE]
+declaration=str
+category=String Functions
+description=Returns the string str with the order of the characters reversed.\n\nExamples\n--------\n\nSELECT REVERSE('desserts');\n+---------------------+\n| REVERSE('desserts') |\n+---------------------+\n| stressed |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/reverse/
+[RIGHT]
+declaration=str,len
+category=String Functions
+description=Returns the rightmost len characters from the string str, or NULL if any\nargument is NULL.\n\nExamples\n--------\n\nSELECT RIGHT('MariaDB', 2);\n+---------------------+\n| RIGHT('MariaDB', 2) |\n+---------------------+\n| DB |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/right/
+[ROLLBACK]
+declaration=the keyword WORK is simply noise and can be omitted without changing the effect
+category=Transactions
+description=The optional AND CHAIN clause is a convenience for initiating a new\ntransaction as soon as the old transaction terminates. If AND CHAIN is\nspecified, then there is effectively nothing between the old and new\ntransactions, although they remain separate. The characteristics of the new\ntransaction will be the same as the characteristics of the old one - that is,\nthe new transaction will have the same access mode, isolation level and\ndiagnostics area size (we'll discuss all of these shortly) as the transaction\njust terminated. The AND NO CHAIN option just tells your DBMS to end the\ntransaction - that is, these four SQL statements are equivalent:\n\nROLLBACK; \nROLLBACK WORK; \nROLLBACK AND NO CHAIN; \nROLLBACK WORK AND NO CHAIN;\n\nAll of them end a transaction without saving any transaction characteristics.\nThe only other options, the equivalent statements:\n\nROLLBACK AND CHAIN;\nROLLBACK WORK AND CHAIN;\n\nboth tell your DBMS to end a transaction, but to save that transaction's\ncharacteristics for the next transaction.\n\nROLLBACK is much simpler than COMMIT: it may involve no more than a few\ndeletions (of Cursors, locks, prepared SQL statements and log-file entries).\nIt's usually assumed that ROLLBACK can't fail, although such a thing is\nconceivable (for example, an encompassing transaction might reject an attempt\nto ROLLBACK because it's lining up for a COMMIT).\n\nROLLBACK cancels all effects of a transaction. It does not cancel effects on\nobjects outside the DBMS's control (for example the values in host program\nvariables or the settings made by some SQL/CLI function calls). But in\ngeneral, it is a convenient statement for those situations when you say "oops,\nthis isn't working" or when you simply don't care whether your temporary work\nbecomes permanent or not.\n\nHere is a moot question. If all you've been doing is SELECTs, so that there\nhave been no data changes, should you end the transaction with ROLLBACK or\nCOMMIT? It shouldn't really matter because both ROLLBACK and COMMIT do the\nsame transaction-terminating job. However, the popular conception is that\nROLLBACK implies failure, so after a successful series of SELECT statements\nthe convention is to end the transaction with COMMIT rather than ROLLBACK.\n\nMariaDB (and most other DBMSs) supports rollback of SQL-data change\nstatements, but not of SQL-Schema statements. This means that if you use any\nof CREATE, ALTER, DROP, GRANT, REVOKE, you are implicitly committing at\nexecution time.\n\nINSERT INTO Table_2 VALUES(5); \n ...
+[ROUND]
+declaration=X
+category=Numeric Functions
+description=Rounds the argument X to D decimal places. D defaults to 0 if not specified. D\ncan be negative to cause D digits left of the decimal point of the value X to\nbecome zero.\n\nThe rounding algorithm depends on the data type of X:\n\n* for floating point types (FLOAT, DOUBLE) the C libraries rounding function\nis used, so the behavior *may* differ between operating systems\n* for fixed point types (DECIMAL, DEC/NUMBER/FIXED) the "round half up" rule\nis used, meaning that e.g. a value ending in exactly .5 is always rounded up.\n\nExamples\n--------\n\nSELECT ROUND(-1.23);\n+--------------+\n| ROUND(-1.23) |\n+--------------+\n| -1 |\n+--------------+\n\nSELECT ROUND(-1.58);\n+--------------+\n| ROUND(-1.58) |\n+--------------+\n| -2 |\n+--------------+\n\nSELECT ROUND(1.58); \n+-------------+\n| ROUND(1.58) |\n+-------------+\n| 2 |\n+-------------+\n\nSELECT ROUND(1.298, 1);\n+-----------------+\n| ROUND(1.298, 1) |\n+-----------------+\n| 1.3 |\n+-----------------+\n\nSELECT ROUND(1.298, 0);\n+-----------------+\n| ROUND(1.298, 0) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT ROUND(23.298, -1);\n ...
+[ROW]
+declaration= [{, }... ]
+category=Data Types
+description=ROW is a data type for stored procedure variables.\n\nFeatures\n--------\n\nROW fields as normal variables\n------------------------------\n\nROW fields (members) act as normal variables, and are able to appear in all\nquery parts where a stored procedure variable is allowed:\n\n* Assignment is using the := operator and the SET command:\n\na.x:= 10;\na.x:= b.x;\nSET a.x= 10, a.y=20, a.z= b.z;\n\n* Passing to functions and operators:\n\nSELECT f1(rec.a), rec.a<10;\n\n* Clauses (select list, WHERE, HAVING, LIMIT, etc...,):\n\nSELECT var.a, t1.b FROM t1 WHERE t1.b=var.b LIMIT var.c;\n\n* INSERT values:\n\nINSERT INTO t1 VALUES (rec.a, rec.b, rec.c);\n\n* SELECT .. INTO targets\n\nSELECT a,b INTO rec.a, rec.b FROM t1 WHERE t1.id=10;\n\n* Dynamic SQL out parameters (EXECUTE and EXECUTE IMMEDIATE)\n\nEXECUTE IMMEDIATE 'CALL proc_with_out_param(?)' USING rec.a;\n\nROW type variables as FETCH targets\n-----------------------------------\n\nROW type variables are allowed as FETCH targets:\n\nFETCH cur INTO rec;\n\nwhere cur is a CURSOR and rec is a ROW type stored procedure variable.\n\nNote, currently an attempt to use FETCH for a ROW type variable returns this\nerror:\n\nERROR 1328 (HY000): Incorrect number of FETCH variables\n ...
+[ROWNUM]
+declaration=
+category=Information Functions
+description=ROWNUM() returns the current number of accepted rows in the current context.\nIt main purpose is to emulate the ROWNUM pseudo column in Oracle. For MariaDB\nnative applications, we recommend the usage of LIMIT, as it is easier to use\nand gives more predictable results than the usage of ROWNUM().\n\nThe main difference between using LIMIT and ROWNUM() to limit the rows in the\nresult is that LIMIT works on the result set while ROWNUM works on the number\nof accepted rows (before any ORDER or GROUP BY clauses).\n\nThe following queries will return the same results:\n\nSELECT * from t1 LIMIT 10;\nSELECT * from t1 WHERE ROWNUM() <= 10;\n\nWhile the following may return different results based on in which orders the\nrows are found:\n\nSELECT * from t1 ORDER BY a LIMIT 10;\nSELECT * from t1 ORDER BY a WHERE ROWNUM() <= 10;\n\nThe recommended way to use ROWNUM to limit the number of returned rows and get\npredictable results is to have the query in a subquery and test for ROWNUM()\nin the outer query:\n\nSELECT * FROM (select * from t1 ORDER BY a) WHERE ROWNUM() <= 10;\n\nROWNUM() can be used in the following contexts:\n\n* SELECT\n* INSERT\n* UPDATE\n* DELETE\n* LOAD DATA INFILE\n\nUsed in other contexts, ROWNUM() will return 0.\n\nExamples\n--------\n\nINSERT INTO t1 VALUES (1,ROWNUM()),(2,ROWNUM()),(3,ROWNUM());\n\nINSERT INTO t1 VALUES (1),(2) returning a, ROWNUM();\n\nUPDATE t1 SET row_num_column=ROWNUM();\n\nDELETE FROM t1 WHERE a < 10 AND ROWNUM() < 2;\n\nLOAD DATA INFILE 'filename' into table t1 fields terminated by ',' \n lines terminated by "\r\n" (a,b) set c=ROWNUM();\n\n ...
+[ROW_COUNT]
+declaration=
+category=Information Functions
+description=ROW_COUNT() returns the number of rows updated, inserted or deleted by the\npreceding statement. This is the same as the row count that the mariadb client\ndisplays and the value from the mysql_affected_rows() C API function.\n\nGenerally:\n\n* For statements which return a result set (such as SELECT, SHOW, DESC or\nHELP), returns -1, even when the result set is empty. This is also true for\nadministrative statements, such as OPTIMIZE.\n* For DML statements other than SELECT and for ALTER TABLE, returns the number\nof affected rows.\n* For DDL statements (including TRUNCATE) and for other statements which don't\nreturn any result set (such as USE, DO, SIGNAL or DEALLOCATE PREPARE), returns\n0.\n\nFor UPDATE, affected rows is by default the number of rows that were actually\nchanged. If the CLIENT_FOUND_ROWS flag to mysql_real_connect() is specified\nwhen connecting to mysqld, affected rows is instead the number of rows matched\nby the WHERE clause.\n\nFor REPLACE, deleted rows are also counted. So, if REPLACE deletes a row and\nadds a new row, ROW_COUNT() returns 2.\n\nFor INSERT ... ON DUPLICATE KEY, updated rows are counted twice. So, if INSERT\nadds a new rows and modifies another row, ROW_COUNT() returns 3.\n\nROW_COUNT() does not take into account rows that are not directly\ndeleted/updated by the last statement. This means that rows deleted by foreign\nkeys or triggers are not counted.\n\nWarning: You can use ROW_COUNT() with prepared statements, but you need to\ncall it after EXECUTE, not after DEALLOCATE PREPARE, because the row count for\nallocate prepare is always 0.\n\nWarning: When used after a CALL statement, this function returns the number of\nrows affected by the last statement in the procedure, not by the whole\nprocedure.\n\nWarning: After INSERT DELAYED, ROW_COUNT() returns the number of the rows you\ntried to insert, not the number of the successful writes.\n\nThis information can also be found in the diagnostics area.\n\nStatements using the ROW_COUNT() function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nCREATE TABLE t (A INT);\n ...
+[ROW_NUMBER]
+declaration=
+category=Window Functions
+description=ROW_NUMBER() is a window function that displays the number of a given row,\nstarting at one and following the ORDER BY sequence of the window function,\nwith identical values receiving different row numbers. It is similar to the\nRANK() and DENSE_RANK() functions except that in that function, identical\nvalues will receive the same rank for each result.\n\nExamples\n--------\n\nThe distinction between DENSE_RANK(), RANK() and ROW_NUMBER():\n\nCREATE TABLE student(course VARCHAR(10), mark int, name varchar(10));\n\nINSERT INTO student VALUES \n ('Maths', 60, 'Thulile'),\n ('Maths', 60, 'Pritha'),\n ('Maths', 70, 'Voitto'),\n ('Maths', 55, 'Chun'),\n ('Biology', 60, 'Bilal'),\n ('Biology', 70, 'Roger');\n\nSELECT \n RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS rank,\n DENSE_RANK() OVER (PARTITION BY course ORDER BY mark DESC) AS dense_rank,\n ROW_NUMBER() OVER (PARTITION BY course ORDER BY mark DESC) AS row_num,\n course, mark, name\nFROM student ORDER BY course, mark DESC;\n+------+------------+---------+---------+------+---------+\n| rank | dense_rank | row_num | course | mark | name |\n+------+------------+---------+---------+------+---------+\n| 1 | 1 | 1 | Biology | 70 | Roger |\n| 2 | 2 | 2 | Biology | 60 | Bilal |\n| 1 | 1 | 1 | Maths | 70 | Voitto |\n| 2 | 2 | 2 | Maths | 60 | Thulile |\n| 2 | 2 | 3 | Maths | 60 | Pritha |\n| 4 | 3 | 4 | Maths | 55 | Chun |\n+------+------------+---------+---------+------+---------+\n\nURL: https://mariadb.com/kb/en/row_number/
+[RPAD]
+declaration=str, len [, padstr]
+category=String Functions
+description=Returns the string str, right-padded with the string padstr to a length of len\ncharacters. If str is longer than len, the return value is shortened to len\ncharacters. If padstr is omitted, the RPAD function pads spaces.\n\nPrior to MariaDB 10.3.1, the padstr parameter was mandatory.\n\nReturns NULL if given a NULL argument. If the result is empty (a length of\nzero), returns either an empty string, or, from MariaDB 10.3.6 with\nSQL_MODE=Oracle, NULL.\n\nThe Oracle mode version of the function can be accessed outside of Oracle mode\nby using RPAD_ORACLE as the function name.\n\nExamples\n--------\n\nSELECT RPAD('hello',10,'.');\n+----------------------+\n| RPAD('hello',10,'.') |\n+----------------------+\n| hello..... |\n+----------------------+\n\nSELECT RPAD('hello',2,'.');\n+---------------------+\n| RPAD('hello',2,'.') |\n+---------------------+\n| he |\n+---------------------+\n\nFrom MariaDB 10.3.1, with the pad string defaulting to space.\n\nSELECT RPAD('hello',30);\n+--------------------------------+\n| RPAD('hello',30) |\n+--------------------------------+\n| hello |\n+--------------------------------+\n\nOracle mode version from MariaDB 10.3.6:\n\nSELECT RPAD('',0),RPAD_ORACLE('',0);\n+------------+-------------------+\n| RPAD('',0) | RPAD_ORACLE('',0) |\n+------------+-------------------+\n| | NULL |\n+------------+-------------------+\n\nURL: https://mariadb.com/kb/en/rpad/
+[RTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with trailing space characters removed.\n\nReturns NULL if given a NULL argument. If the result is empty, returns either\nan empty string, or, from MariaDB 10.3.6 with SQL_MODE=Oracle, NULL.\n\nThe Oracle mode version of the function can be accessed outside of Oracle mode\nby using RTRIM_ORACLE as the function name.\n\nExamples\n--------\n\nSELECT QUOTE(RTRIM('MariaDB '));\n+-----------------------------+\n| QUOTE(RTRIM('MariaDB ')) |\n+-----------------------------+\n| 'MariaDB' |\n+-----------------------------+\n\nOracle mode version from MariaDB 10.3.6:\n\nSELECT RTRIM(''),RTRIM_ORACLE('');\n+-----------+------------------+\n| RTRIM('') | RTRIM_ORACLE('') |\n+-----------+------------------+\n| | NULL |\n+-----------+------------------+\n\nURL: https://mariadb.com/kb/en/rtrim/
+[SCHEMA]
+declaration=
+category=Information Functions
+description=This function is a synonym for DATABASE().\n\nURL: https://mariadb.com/kb/en/schema/
+[SECOND]
+declaration=time
+category=Date and Time Functions
+description=Returns the second for a given time (which can include microseconds), in the\nrange 0 to 59, or NULL if not given a valid time value.\n\nExamples\n--------\n\nSELECT SECOND('10:05:03');\n+--------------------+\n| SECOND('10:05:03') |\n+--------------------+\n| 3 |\n+--------------------+\n\nSELECT SECOND('10:05:01.999999');\n+---------------------------+\n| SECOND('10:05:01.999999') |\n+---------------------------+\n| 1 |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/second/
+[SEC_TO_TIME]
+declaration=seconds
+category=Date and Time Functions
+description=Returns the seconds argument, converted to hours, minutes, and seconds, as a\nTIME value. The range of the result is constrained to that of the TIME data\ntype. A warning occurs if the argument corresponds to a value outside that\nrange.\n\nThe time will be returned in the format hh:mm:ss, or hhmmss if used in a\nnumeric calculation.\n\nExamples\n--------\n\nSELECT SEC_TO_TIME(12414);\n+--------------------+\n| SEC_TO_TIME(12414) |\n+--------------------+\n| 03:26:54 |\n+--------------------+\n\nSELECT SEC_TO_TIME(12414)+0;\n+----------------------+\n| SEC_TO_TIME(12414)+0 |\n+----------------------+\n| 32654 |\n+----------------------+\n\nSELECT SEC_TO_TIME(9999999);\n+----------------------+\n| SEC_TO_TIME(9999999) |\n+----------------------+\n| 838:59:59 |\n+----------------------+\n1 row in set, 1 warning (0.00 sec)\n\nSHOW WARNINGS;\n+---------+------+-------------------------------------------+\n| Level | Code | Message |\n+---------+------+-------------------------------------------+\n| Warning | 1292 | Truncated incorrect time value: '9999999' |\n+---------+------+-------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sec_to_time/
+[SESSION_USER]
+declaration=
+category=Information Functions
+description=SESSION_USER() is a synonym for USER().\n\nURL: https://mariadb.com/kb/en/session_user/
+[SETVAL]
+declaration=sequence_name, next_value, [is_used, [round]]
+category=Sequences
+description=Set the next value to be returned for a SEQUENCE.\n\nThis function is compatible with PostgreSQL syntax, extended with the round\nargument.\n\nIf the is_used argument is not given or is 1 or true, then the next used value\nwill one after the given value. If is_used is 0 or false then the next\ngenerated value will be the given value.\n\nIf round is used then it will set the round value (or the internal cycle\ncount, starting at zero) for the sequence. If round is not used, it's assumed\nto be 0.\n\nnext_value must be an integer literal.\n\nFor SEQUENCE tables defined with CYCLE (see CREATE SEQUENCE) one should use\nboth next_value and round to define the next value. In this case the current\nsequence value is defined to be round, next_value.\n\nThe result returned by SETVAL() is next_value or NULL if the given next_value\nand round is smaller than the current value.\n\nSETVAL() will not set the SEQUENCE value to a something that is less than its\ncurrent value. This is needed to ensure that SETVAL() is replication safe. If\nyou want to set the SEQUENCE to a smaller number use ALTER SEQUENCE.\n\nIf CYCLE is used, first round and then next_value are compared to see if the\nvalue is bigger than the current value.\n\nInternally, in the MariaDB server, SETVAL() is used to inform slaves that a\nSEQUENCE has changed value. The slave may get SETVAL() statements out of\norder, but this is ok as only the biggest one will have an effect.\n\nSETVAL requires the INSERT privilege.\n\nExamples\n--------\n\nSELECT setval(foo, 42); -- Next nextval will return 43\nSELECT setval(foo, 42, true); -- Same as above\nSELECT setval(foo, 42, false); -- Next nextval will return 42\n\nSETVAL setting higher and lower values on a sequence with an increment of 10:\n\nSELECT NEXTVAL(s);\n+------------+\n| NEXTVAL(s) |\n+------------+\n| 50 |\n+------------+\n ...
+[SFORMAT]
+declaration="The answer is {}.", 42
+category=String Functions
+description=+----------------------------------+\n| SFORMAT("The answer is {}.", 42) |\n+----------------------------------+\n| The answer is 42. |\n+----------------------------------+\n\nCREATE TABLE test_sformat(mdb_release char(6), mdev int, feature char(20));\n\nINSERT INTO test_sformat VALUES('10.7.0', 25015, 'Python style sformat'), \n ('10.7.0', 4958, 'UUID');\n\nSELECT * FROM test_sformat;\n+-------------+-------+----------------------+\n| mdb_release | mdev | feature |\n+-------------+-------+----------------------+\n| 10.7.0 | 25015 | Python style sformat |\n| 10.7.0 | 4958 | UUID |\n+-------------+-------+----------------------+\n\nSELECT SFORMAT('MariaDB Server {} has a preview for MDEV-{} which is about\n{}', \n mdb_release, mdev, feature) AS 'Preview Release Examples'\n FROM test_sformat;\n+------------------------------------------------------------------------------\n---------+\n| Preview Release Examples \n |\n+------------------------------------------------------------------------------\n---------+\n| MariaDB Server 10.7.0 has a preview for MDEV-25015 which is about Python\nstyle sformat |\n| MariaDB Server 10.7.0 has a preview for MDEV-4958 which is about UUID \n |\n+------------------------------------------------------------------------------\n---------+\n\nURL: https://mariadb.com/kb/en/sformat/
+[SHA1]
+declaration=str
+category=Encryption Functions
+description=Calculates an SHA-1 160-bit checksum for the string str, as described in RFC\n3174 (Secure Hash Algorithm).\n\nThe value is returned as a string of 40 hex digits, or NULL if the argument\nwas NULL. As of MariaDB 5.5, the return value is a nonbinary string in the\nconnection character set and collation, determined by the values of the\ncharacter_set_connection and collation_connection system variables. Before\n5.5, the return value was a binary string.\n\nExamples\n--------\n\nSELECT SHA1('some boring text');\n+------------------------------------------+\n| SHA1('some boring text') |\n+------------------------------------------+\n| af969fc2085b1bb6d31e517d5c456def5cdd7093 |\n+------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sha1/
+[SHA2]
+declaration=str,hash_len
+category=Encryption Functions
+description=Given a string str, calculates an SHA-2 checksum, which is considered more\ncryptographically secure than its SHA-1 equivalent. The SHA-2 family includes\nSHA-224, SHA-256, SHA-384, and SHA-512, and the hash_len must correspond to\none of these, i.e. 224, 256, 384 or 512. 0 is equivalent to 256.\n\nThe return value is a nonbinary string in the connection character set and\ncollation, determined by the values of the character_set_connection and\ncollation_connection system variables.\n\nNULL is returned if the hash length is not valid, or the string str is NULL.\n\nSHA2 will only work if MariaDB was has been configured with TLS support.\n\nExamples\n--------\n\nSELECT SHA2('Maria',224);\n+----------------------------------------------------------+\n| SHA2('Maria',224) |\n+----------------------------------------------------------+\n| 6cc67add32286412efcab9d0e1675a43a5c2ef3cec8879f81516ff83 |\n+----------------------------------------------------------+\n\nSELECT SHA2('Maria',256);\n+------------------------------------------------------------------+\n| SHA2('Maria',256) |\n+------------------------------------------------------------------+\n| 9ff18ebe7449349f358e3af0b57cf7a032c1c6b2272cb2656ff85eb112232f16 |\n+------------------------------------------------------------------+\n\nSELECT SHA2('Maria',0);\n+------------------------------------------------------------------+\n| SHA2('Maria',0) |\n+------------------------------------------------------------------+\n| 9ff18ebe7449349f358e3af0b57cf7a032c1c6b2272cb2656ff85eb112232f16 |\n+------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/sha2/
+[SIGN]
+declaration=X
+category=Numeric Functions
+description=Returns the sign of the argument as -1, 0, or 1, depending on whether X is\nnegative, zero, or positive.\n\nExamples\n--------\n\nSELECT SIGN(-32);\n+-----------+\n| SIGN(-32) |\n+-----------+\n| -1 |\n+-----------+\n\nSELECT SIGN(0);\n+---------+\n| SIGN(0) |\n+---------+\n| 0 |\n+---------+\n\nSELECT SIGN(234);\n+-----------+\n| SIGN(234) |\n+-----------+\n| 1 |\n+-----------+\n\nURL: https://mariadb.com/kb/en/sign/
+[SIN]
+declaration=X
+category=Numeric Functions
+description=Returns the sine of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT SIN(1.5707963267948966);\n+-------------------------+\n| SIN(1.5707963267948966) |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT SIN(PI());\n+----------------------+\n| SIN(PI()) |\n+----------------------+\n| 1.22460635382238e-16 |\n+----------------------+\n\nSELECT ROUND(SIN(PI()));\n+------------------+\n| ROUND(SIN(PI())) |\n+------------------+\n| 0 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/sin/
+[SLEEP]
+declaration=duration
+category=Miscellaneous Functions
+description=Sleeps (pauses) for the number of seconds given by the duration argument, then\nreturns 0. If SLEEP() is interrupted, it returns 1. The duration may have a\nfractional part given in microseconds.\n\nStatements using the SLEEP() function are not safe for statement-based\nreplication.\n\nExample\n-------\n\nSELECT SLEEP(5.5);\n+------------+\n| SLEEP(5.5) |\n+------------+\n| 0 |\n+------------+\n1 row in set (5.50 sec)\n\nURL: https://mariadb.com/kb/en/sleep/
+[SMALLINT]
+declaration=M
+category=Data Types
+description=A small integer. The signed range is -32768 to 32767. The unsigned range is 0\nto 65535.\n\nIf a column has been set to ZEROFILL, all values will be prepended by zeros so\nthat the SMALLINT value contains a number of M digits.\n\nNote: If the ZEROFILL attribute has been specified, the column will\nautomatically become UNSIGNED.\n\nINT2 is a synonym for SMALLINT.\n\nFor more details on the attributes, see Numeric Data Type Overview.\n\nExamples\n--------\n\nCREATE TABLE smallints (a SMALLINT,b SMALLINT UNSIGNED,c SMALLINT ZEROFILL);\n\nWith strict_mode set, the default from MariaDB 10.2.4:\n\nINSERT INTO smallints VALUES (-10,-10,-10);\nERROR 1264 (22003): Out of range value for column 'b' at row 1\n\nINSERT INTO smallints VALUES (-10,10,-10);\nERROR 1264 (22003): Out of range value for column 'c' at row 1\n\nINSERT INTO smallints VALUES (-10,10,10);\n\nINSERT INTO smallints VALUES (32768,32768,32768);\nERROR 1264 (22003): Out of range value for column 'a' at row 1\n\nINSERT INTO smallints VALUES (32767,32768,32768);\n\nSELECT * FROM smallints;\n+-------+-------+-------+\n| a | b | c |\n+-------+-------+-------+\n| -10 | 10 | 00010 |\n| 32767 | 32768 | 32768 |\n+-------+-------+-------+\n\nWith strict_mode unset, the default until MariaDB 10.2.3:\n\nINSERT INTO smallints VALUES (-10,-10,-10);\nQuery OK, 1 row affected, 2 warnings (0.09 sec)\nWarning (Code 1264): Out of range value for column 'b' at row 1\nWarning (Code 1264): Out of range value for column 'c' at row 1\n\nINSERT INTO smallints VALUES (-10,10,-10);\nQuery OK, 1 row affected, 1 warning (0.08 sec)\n ...
+[SOUNDEX]
+declaration=str
+category=String Functions
+description=Returns a soundex string from str. Two strings that sound almost the same\nshould have identical soundex strings. A standard soundex string is four\ncharacters long, but the SOUNDEX() function returns an arbitrarily long\nstring. You can use SUBSTRING() on the result to get a standard soundex\nstring. All non-alphabetic characters in str are ignored. All international\nalphabetic characters outside the A-Z range are treated as vowels.\n\nImportant: When using SOUNDEX(), you should be aware of the following details:\n\n* This function, as currently implemented, is intended to work well with\n strings that are in the English language only. Strings in other languages may\n not produce reasonable results.\n\n* This function implements the original Soundex algorithm, not the more\npopular enhanced version (also described by D. Knuth). The difference is that\noriginal version discards vowels first and duplicates second, whereas the\nenhanced version discards duplicates first and vowels second.\n\nExamples\n--------\n\nSOUNDEX('Hello');\n+------------------+\n| SOUNDEX('Hello') |\n+------------------+\n| H400 |\n+------------------+\n\nSELECT SOUNDEX('MariaDB');\n+--------------------+\n| SOUNDEX('MariaDB') |\n+--------------------+\n| M631 |\n+--------------------+\n\nSELECT SOUNDEX('Knowledgebase');\n+--------------------------+\n| SOUNDEX('Knowledgebase') |\n+--------------------------+\n| K543212 |\n+--------------------------+\n\nSELECT givenname, surname FROM users WHERE SOUNDEX(givenname) =\nSOUNDEX("robert");\n+-----------+---------+\n| givenname | surname |\n+-----------+---------+\n| Roberto | Castro |\n+-----------+---------+\n\nURL: https://mariadb.com/kb/en/soundex/
+[SPACE]
+declaration=N
+category=String Functions
+description=Returns a string consisting of N space characters. If N is NULL, returns NULL.\n\nExamples\n--------\n\nSELECT QUOTE(SPACE(6));\n+-----------------+\n| QUOTE(SPACE(6)) |\n+-----------------+\n| ' ' |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/space/
+[SPIDER_BG_DIRECT_SQL]
+declaration='sql', 'tmp_table_list', 'parameters'
+category=Spider Functions
+description=Executes the given SQL statement in the background on the remote server, as\ndefined in the parameters listing. If the query returns a result-set, it\nsttores the results in the given temporary table. When the given SQL statement\nexecutes successfully, this function returns the number of called UDF's. It\nreturns 0 when the given SQL statement fails.\n\nThis function is a UDF installed with the Spider storage engine.\n\nExamples\n--------\n\nSELECT SPIDER_BG_DIRECT_SQL('SELECT * FROM example_table', '', \n 'srv "node1", port "8607"') AS "Direct Query";\n+--------------+\n| Direct Query | \n+--------------+\n| 1 |\n+--------------+\n\nParameters\n----------\n\nerror_rw_mode\n-------------\n\n* Description: Returns empty results on network error.\n0 : Return error on getting network error.\n1: Return 0 records on getting network error.\n\n* Default Table Value: 0\n* DSN Parameter Name: erwm\n\nURL: https://mariadb.com/kb/en/spider_bg_direct_sql/
+[SPIDER_COPY_TABLES]
+declaration=spider_table_name, source_link_id, destination_link_id_list [,parameters]
+category=Spider Functions
+description=A UDF installed with the Spider Storage Engine, this function copies table\ndata from source_link_id to destination_link_id_list. The service does not\nneed to be stopped in order to copy.\n\nIf the Spider table is partitioned, the name must be of the format\ntable_name#P#partition_name. The partition name can be viewed in the\nmysql.spider_tables table, for example:\n\nSELECT table_name FROM mysql.spider_tables;\n+-------------+\n| table_name |\n+-------------+\n| spt_a#P#pt1 |\n| spt_a#P#pt2 |\n| spt_a#P#pt3 |\n+-------------+\n\nReturns 1 if the data was copied successfully, or 0 if copying the data failed.\n\nURL: https://mariadb.com/kb/en/spider_copy_tables/
+[SPIDER_DIRECT_SQL]
+declaration='sql', 'tmp_table_list', 'parameters'
+category=Spider Functions
+description=A UDF installed with the Spider Storage Engine, this function is used to\nexecute the SQL string sql on the remote server, as defined in parameters. If\nany resultsets are returned, they are stored in the tmp_table_list.\n\nThe function returns 1 if the SQL executes successfully, or 0 if it fails.\n\nExamples\n--------\n\nSELECT SPIDER_DIRECT_SQL('SELECT * FROM s', '', 'srv "node1", port "8607"');\n+----------------------------------------------------------------------+\n| SPIDER_DIRECT_SQL('SELECT * FROM s', '', 'srv "node1", port "8607"') |\n+----------------------------------------------------------------------+\n| 1 |\n+----------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/spider_direct_sql/
+[SPIDER_FLUSH_TABLE_MON_CACHE]
+declaration=
+category=Spider Functions
+description=A UDF installed with the Spider Storage Engine, this function is used for\nrefreshing monitoring server information. It returns a value of 1.\n\nExamples\n--------\n\nSELECT SPIDER_FLUSH_TABLE_MON_CACHE();\n+--------------------------------+\n| SPIDER_FLUSH_TABLE_MON_CACHE() |\n+--------------------------------+\n| 1 |\n+--------------------------------+\n\nURL: https://mariadb.com/kb/en/spider_flush_table_mon_cache/
+[SQRT]
+declaration=X
+category=Numeric Functions
+description=Returns the square root of X. If X is negative, NULL is returned.\n\nExamples\n--------\n\nSELECT SQRT(4);\n+---------+\n| SQRT(4) |\n+---------+\n| 2 |\n+---------+\n\nSELECT SQRT(20);\n+------------------+\n| SQRT(20) |\n+------------------+\n| 4.47213595499958 |\n+------------------+\n\nSELECT SQRT(-16);\n+-----------+\n| SQRT(-16) |\n+-----------+\n| NULL |\n+-----------+\n\nSELECT SQRT(1764);\n+------------+\n| SQRT(1764) |\n+------------+\n| 42 |\n+------------+\n\nURL: https://mariadb.com/kb/en/sqrt/
+[STD]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr. This is an extension to\nstandard SQL. The standard SQL function STDDEV_POP() can be used instead.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTD() can be used as a window function.\n\nThis function returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n| Kaolin | SQL | 56 | 16.9466 |\n ...
+[STDDEV]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr. This function is provided\nfor compatibility with Oracle. The standard SQL function STDDEV_POP() can be\nused instead.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV() can be used as a window function.\n\nThis function returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n ...
+[STDDEV_POP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr (the square root of\nVAR_POP()). You can also use STD() or STDDEV(), which are equivalent but not\nstandard SQL.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV_POP() can be used as a window function.\n\nSTDDEV_POP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87);\n\nSELECT name, test, score, STDDEV_POP(score) \n OVER (PARTITION BY test) AS stddev_results FROM student_test;\n+---------+--------+-------+----------------+\n| name | test | score | stddev_results |\n+---------+--------+-------+----------------+\n| Chun | SQL | 75 | 16.9466 |\n| Chun | Tuning | 73 | 24.1247 |\n| Esben | SQL | 43 | 16.9466 |\n| Esben | Tuning | 31 | 24.1247 |\n ...
+[STDDEV_SAMP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sample standard deviation of expr (the square root of VAR_SAMP()).\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nSTDDEV_SAMP() can be used as a window function.\n\nSTDDEV_SAMP() returns NULL if there were no matching rows.\n\nURL: https://mariadb.com/kb/en/stddev_samp/
+[STRCMP]
+declaration=expr1,expr2
+category=String Functions
+description=STRCMP() returns 0 if the strings are the same, -1 if the first argument is\nsmaller than the second according to the current sort order, and 1 if the\nstrings are otherwise not the same. Returns NULL is either argument is NULL.\n\nExamples\n--------\n\nSELECT STRCMP('text', 'text2');\n+-------------------------+\n| STRCMP('text', 'text2') |\n+-------------------------+\n| -1 |\n+-------------------------+\n\nSELECT STRCMP('text2', 'text');\n+-------------------------+\n| STRCMP('text2', 'text') |\n+-------------------------+\n| 1 |\n+-------------------------+\n\nSELECT STRCMP('text', 'text');\n+------------------------+\n| STRCMP('text', 'text') |\n+------------------------+\n| 0 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/strcmp/
+[STR_TO_DATE]
+declaration=str,format
+category=Date and Time Functions
+description=This is the inverse of the DATE_FORMAT() function. It takes a string str and a\nformat string format. STR_TO_DATE() returns a DATETIME value if the format\nstring contains both date and time parts, or a DATE or TIME value if the\nstring contains only date or time parts.\n\nThe date, time, or datetime values contained in str should be given in the\nformat indicated by format. If str contains an illegal date, time, or datetime\nvalue, STR_TO_DATE() returns NULL. An illegal value also produces a warning.\n\nUnder specific SQL_MODE settings an error may also be generated if the str\nisn't a valid date:\n\n* ALLOW_INVALID_DATES\n* NO_ZERO_DATE\n* NO_ZERO_IN_DATE\n\nThe options that can be used by STR_TO_DATE(), as well as its inverse\nDATE_FORMAT() and the FROM_UNIXTIME() function, are:\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| %a | Short weekday name in current locale |\n| | (Variable lc_time_names). |\n+---------------------------+------------------------------------------------+\n| %b | Short form month name in current locale. For |\n| | locale en_US this is one of: |\n| | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov |\n| | or Dec. |\n+---------------------------+------------------------------------------------+\n| %c | Month with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %D | Day with English suffix 'th', 'nd', 'st' or |\n| | 'rd''. (1st, 2nd, 3rd...). |\n+---------------------------+------------------------------------------------+\n| %d | Day with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %e | Day with 1 or 2 digits. |\n+---------------------------+------------------------------------------------+\n| %f | Microseconds 6 digits. |\n+---------------------------+------------------------------------------------+\n| %H | Hour with 2 digits between 00-23. |\n+---------------------------+------------------------------------------------+\n| %h | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %I | Hour with 2 digits between 01-12. |\n+---------------------------+------------------------------------------------+\n| %i | Minute with 2 digits. |\n+---------------------------+------------------------------------------------+\n| %j | Day of the year (001-366) |\n ...
+[ST_AREA]
+declaration=poly
+category=Polygon Properties
+description=Returns as a double-precision number the area of the Polygon value poly, as\nmeasured in its spatial reference system.\n\nST_Area() and Area() are synonyms.\n\nExamples\n--------\n\nSET @poly = 'Polygon((0 0,0 3,3 0,0 0),(1 1,1 2,2 1,1 1))';\n\nSELECT Area(GeomFromText(@poly));\n+---------------------------+\n| Area(GeomFromText(@poly)) |\n+---------------------------+\n| 4 |\n+---------------------------+\n\nURL: https://mariadb.com/kb/en/st_area/
+[ST_AsBinary]
+declaration=g
+category=WKB
+description=Converts a value in internal geometry format to its WKB representation and\nreturns the binary result.\n\nST_AsBinary(), AsBinary(), ST_AsWKB() and AsWKB() are synonyms,\n\nExamples\n--------\n\nSET @poly = ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))');\nSELECT ST_AsBinary(@poly);\n\nSELECT ST_AsText(ST_GeomFromWKB(ST_AsWKB(@poly)));\n+--------------------------------------------+\n| ST_AsText(ST_GeomFromWKB(ST_AsWKB(@poly))) |\n+--------------------------------------------+\n| POLYGON((0 0,0 1,1 1,1 0,0 0)) |\n+--------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_asbinary/
+[ST_AsGeoJSON]
+declaration=g[, max_decimals[, options]]
+category=GeoJSON
+description=Returns the given geometry g as a GeoJSON element. The optional max_decimals\nlimits the maximum number of decimals displayed.\n\nThe optional options flag can be set to 1 to add a bounding box to the output.\n\nExamples\n--------\n\nSELECT ST_AsGeoJSON(ST_GeomFromText('POINT(5.3 7.2)'));\n+-------------------------------------------------+\n| ST_AsGeoJSON(ST_GeomFromText('POINT(5.3 7.2)')) |\n+-------------------------------------------------+\n| {"type": "Point", "coordinates": [5.3, 7.2]} |\n+-------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/geojson-st_asgeojson/
+[ST_AsText]
+declaration=g
+category=WKT
+description=Converts a value in internal geometry format to its WKT representation and\nreturns the string result.\n\nST_AsText(), AsText(), ST_AsWKT() and AsWKT() are all synonyms.\n\nExamples\n--------\n\nSET @g = 'LineString(1 1,4 4,6 6)';\n\nSELECT ST_AsText(ST_GeomFromText(@g));\n+--------------------------------+\n| ST_AsText(ST_GeomFromText(@g)) |\n+--------------------------------+\n| LINESTRING(1 1,4 4,6 6) |\n+--------------------------------+\n\nURL: https://mariadb.com/kb/en/st_astext/
+[ST_BOUNDARY]
+declaration=g
+category=Geometry Properties
+description=Returns a geometry that is the closure of the combinatorial boundary of the\ngeometry value g.\n\nBOUNDARY() is a synonym.\n\nExamples\n--------\n\nSELECT ST_AsText(ST_Boundary(ST_GeomFromText('LINESTRING(3 3,0 0, -3 3)')));\n+----------------------------------------------------------------------+\n| ST_AsText(ST_Boundary(ST_GeomFromText('LINESTRING(3 3,0 0, -3 3)'))) |\n+----------------------------------------------------------------------+\n| MULTIPOINT(3 3,-3 3) |\n+----------------------------------------------------------------------+\n\nSELECT ST_AsText(ST_Boundary(ST_GeomFromText('POLYGON((3 3,0 0, -3 3, 3\n3))')));\n+--------------------------------------------------------------------------+\n| ST_AsText(ST_Boundary(ST_GeomFromText('POLYGON((3 3,0 0, -3 3, 3 3))'))) |\n+--------------------------------------------------------------------------+\n| LINESTRING(3 3,0 0,-3 3,3 3) |\n+--------------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_boundary/
+[ST_BUFFER]
+declaration=g1,r
+category=Geometry Constructors
+description=Returns a geometry that represents all points whose distance from geometry g1\nis less than or equal to distance, or radius, r.\n\nUses for this function could include creating for example a new geometry\nrepresenting a buffer zone around an island.\n\nBUFFER() is a synonym.\n\nExamples\n--------\n\nDetermining whether a point is within a buffer zone:\n\nSET @g1 = ST_GEOMFROMTEXT('POLYGON((10 10, 10 20, 20 20, 20 10, 10 10))');\n\nSET @g2 = ST_GEOMFROMTEXT('POINT(8 8)');\n\nSELECT ST_WITHIN(@g2,ST_BUFFER(@g1,5));\n+---------------------------------+\n| ST_WITHIN(@g2,ST_BUFFER(@g1,5)) |\n+---------------------------------+\n| 1 |\n+---------------------------------+\n\nSELECT ST_WITHIN(@g2,ST_BUFFER(@g1,1));\n+---------------------------------+\n| ST_WITHIN(@g2,ST_BUFFER(@g1,1)) |\n+---------------------------------+\n| 0 |\n+---------------------------------+\n\nURL: https://mariadb.com/kb/en/st_buffer/
+[ST_CENTROID]
+declaration=mpoly
+category=Polygon Properties
+description=Returns a point reflecting the mathematical centroid (geometric center) for\nthe MultiPolygon mpoly. The resulting point will not necessarily be on the\nMultiPolygon.\n\nST_Centroid() and Centroid() are synonyms.\n\nExamples\n--------\n\nSET @poly = ST_GeomFromText('POLYGON((0 0,20 0,20 20,0 20,0 0))');\nSELECT ST_AsText(ST_Centroid(@poly)) AS center;\n+--------------+\n| center |\n+--------------+\n| POINT(10 10) |\n+--------------+\n\nURL: https://mariadb.com/kb/en/st_centroid/
+[ST_CONTAINS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether a geometry g1 completely contains geometry\ng2.\n\nST_CONTAINS() uses object shapes, while CONTAINS(), based on the original\nMySQL implementation, uses object bounding rectangles.\n\nST_CONTAINS tests the opposite relationship to ST_WITHIN().\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POLYGON((175 150, 20 40, 50 60, 125 100, 175\n150))');\n\nSET @g2 = ST_GEOMFROMTEXT('POINT(174 149)');\n\nSELECT ST_CONTAINS(@g1,@g2);\n+----------------------+\n| ST_CONTAINS(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSET @g2 = ST_GEOMFROMTEXT('POINT(175 151)');\n\nSELECT ST_CONTAINS(@g1,@g2);\n+----------------------+\n| ST_CONTAINS(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st-contains/
+[ST_CONVEXHULL]
+declaration=
+category=Geometry Constructors
+description=Given a geometry, returns a geometry that is the minimum convex geometry\nenclosing all geometries within the set. Returns NULL if the geometry value is\nNULL or an empty value.\n\nST_ConvexHull() and ConvexHull() are synonyms.\n\nExamples\n--------\n\nThe ConvexHull of a single point is simply the single point:\n\nSET @g = ST_GEOMFROMTEXT('Point(0 0)');\n\nSELECT ST_ASTEXT(ST_CONVEXHULL(@g));\n+------------------------------+\n| ST_ASTEXT(ST_CONVEXHULL(@g)) |\n+------------------------------+\n| POINT(0 0) |\n+------------------------------+\n\nSET @g = ST_GEOMFROMTEXT('MultiPoint(0 0, 1 2, 2 3)');\n\nSELECT ST_ASTEXT(ST_CONVEXHULL(@g));\n+------------------------------+\n| ST_ASTEXT(ST_CONVEXHULL(@g)) |\n+------------------------------+\n| POLYGON((0 0,1 2,2 3,0 0)) |\n+------------------------------+\n\nSET @g = ST_GEOMFROMTEXT('MultiPoint( 1 1, 2 2, 5 3, 7 2, 9 3, 8 4, 6 6, 6 9,\n4 9, 1 5 )');\n\nSELECT ST_ASTEXT(ST_CONVEXHULL(@g));\n+----------------------------------------+\n| ST_ASTEXT(ST_CONVEXHULL(@g)) |\n+----------------------------------------+\n| POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1 1)) |\n+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_convexhull/
+[ST_CROSSES]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 if geometry g1 spatially crosses geometry g2. Returns NULL if g1 is\na Polygon or a MultiPolygon, or if g2 is a Point or a MultiPoint. Otherwise,\nreturns 0.\n\nThe term spatially crosses denotes a spatial relation between two given\ngeometries that has the following properties:\n\n* The two geometries intersect\n* Their intersection results in a geometry that has a dimension that is one\n less than the maximum dimension of the two given geometries\n* Their intersection is not equal to either of the two given geometries\n\nST_CROSSES() uses object shapes, while CROSSES(), based on the original MySQL\nimplementation, uses object bounding rectangles.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('LINESTRING(174 149, 176 151)');\n\nSET @g2 = ST_GEOMFROMTEXT('POLYGON((175 150, 20 40, 50 60, 125 100, 175\n150))');\n\nSELECT ST_CROSSES(@g1,@g2);\n+---------------------+\n| ST_CROSSES(@g1,@g2) |\n+---------------------+\n| 1 |\n+---------------------+\n\nSET @g1 = ST_GEOMFROMTEXT('LINESTRING(176 149, 176 151)');\n\nSELECT ST_CROSSES(@g1,@g2);\n+---------------------+\n| ST_CROSSES(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/st-crosses/
+[ST_DIFFERENCE]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns a geometry representing the point set difference of the given geometry\nvalues.\n\nExample\n-------\n\nSET @g1 = POINT(10,10), @g2 = POINT(20,20);\n\nSELECT ST_AsText(ST_Difference(@g1, @g2));\n+------------------------------------+\n| ST_AsText(ST_Difference(@g1, @g2)) |\n+------------------------------------+\n| POINT(10 10) |\n+------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_difference/
+[ST_DIMENSION]
+declaration=g
+category=Geometry Properties
+description=Returns the inherent dimension of the geometry value g. The result can be\n\n+------------------------------------+---------------------------------------+\n| Dimension | Definition |\n+------------------------------------+---------------------------------------+\n| -1 | empty geometry |\n+------------------------------------+---------------------------------------+\n| 0 | geometry with no length or area |\n+------------------------------------+---------------------------------------+\n| 1 | geometry with no area but nonzero |\n| | length |\n+------------------------------------+---------------------------------------+\n| 2 | geometry with nonzero area |\n+------------------------------------+---------------------------------------+\n\nST_Dimension() and Dimension() are synonyms.\n\nExamples\n--------\n\nSELECT Dimension(GeomFromText('LineString(1 1,2 2)'));\n+------------------------------------------------+\n| Dimension(GeomFromText('LineString(1 1,2 2)')) |\n+------------------------------------------------+\n| 1 |\n+------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_dimension/
+[ST_DISJOINT]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 is spatially disjoint from\n(does not intersect with) geometry g2.\n\nST_DISJOINT() uses object shapes, while DISJOINT(), based on the original\nMySQL implementation, uses object bounding rectangles.\n\nST_DISJOINT() tests the opposite relationship to ST_INTERSECTS().\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(0 0)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(2 0, 0 2)');\n\nSELECT ST_DISJOINT(@g1,@g2);\n+----------------------+\n| ST_DISJOINT(@g1,@g2) |\n+----------------------+\n| 1 |\n+----------------------+\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(0 0, 0 2)');\n\nSELECT ST_DISJOINT(@g1,@g2);\n+----------------------+\n| ST_DISJOINT(@g1,@g2) |\n+----------------------+\n| 0 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st_disjoint/
+[ST_DISTANCE]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns the distance between two geometries, or null if not given valid inputs.\n\nExample\n-------\n\nSELECT ST_Distance(POINT(1,2),POINT(2,2));\n+------------------------------------+\n| ST_Distance(POINT(1,2),POINT(2,2)) |\n+------------------------------------+\n| 1 |\n+------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_distance/
+[ST_DISTANCE_SPHERE]
+declaration=g1,g2,[r]
+category=Geometry Relations
+description=Returns the spherical distance between two geometries (point or multipoint) on\na sphere with the optional radius r (default is the Earth radius if r is not\nspecified), or NULL if not given valid inputs.\n\nExample\n-------\n\nset @zenica = ST_GeomFromText('POINT(17.907743 44.203438)');\nset @sarajevo = ST_GeomFromText('POINT(18.413076 43.856258)');\nSELECT ST_Distance_Sphere(@zenica, @sarajevo);\n55878.59337591705\n\nURL: https://mariadb.com/kb/en/st_distance_sphere/
+[ST_ENDPOINT]
+declaration=ls
+category=LineString Properties
+description=Returns the Point that is the endpoint of the LineString value ls.\n\nST_EndPoint() and EndPoint() are synonyms.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT AsText(EndPoint(GeomFromText(@ls)));\n+-------------------------------------+\n| AsText(EndPoint(GeomFromText(@ls))) |\n+-------------------------------------+\n| POINT(3 3) |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_endpoint/
+[ST_ENVELOPE]
+declaration=g
+category=Geometry Properties
+description=Returns the Minimum Bounding Rectangle (MBR) for the geometry value g. The\nresult is returned as a Polygon value.\n\nThe polygon is defined by the corner points of the bounding box:\n\nPOLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))\n\nST_ENVELOPE() and ENVELOPE() are synonyms.\n\nExamples\n--------\n\nSELECT AsText(ST_ENVELOPE(GeomFromText('LineString(1 1,4 4)')));\n+----------------------------------------------------------+\n| AsText(ST_ENVELOPE(GeomFromText('LineString(1 1,4 4)'))) |\n+----------------------------------------------------------+\n| POLYGON((1 1,4 1,4 4,1 4,1 1)) |\n+----------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_envelope/
+[ST_EQUALS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 is spatially equal to geometry\ng2.\n\nST_EQUALS() uses object shapes, while EQUALS(), based on the original MySQL\nimplementation, uses object bounding rectangles.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('LINESTRING(174 149, 176 151)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(176 151, 174 149)');\n\nSELECT ST_EQUALS(@g1,@g2);\n+--------------------+\n| ST_EQUALS(@g1,@g2) |\n+--------------------+\n| 1 |\n+--------------------+\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(0 2)');\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(2 0)');\n\nSELECT ST_EQUALS(@g1,@g2);\n+--------------------+\n| ST_EQUALS(@g1,@g2) |\n+--------------------+\n| 0 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/st-equals/
+[ST_ExteriorRing]
+declaration=poly
+category=Polygon Properties
+description=Returns the exterior ring of the Polygon value poly as a LineString.\n\nST_ExteriorRing() and ExteriorRing() are synonyms.\n\nExamples\n--------\n\nSET @poly = 'Polygon((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))';\n\nSELECT AsText(ExteriorRing(GeomFromText(@poly)));\n+-------------------------------------------+\n| AsText(ExteriorRing(GeomFromText(@poly))) |\n+-------------------------------------------+\n| LINESTRING(0 0,0 3,3 3,3 0,0 0) |\n+-------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_exteriorring/
+[ST_GEOMETRYN]
+declaration=gc,N
+category=Geometry Properties
+description=Returns the N-th geometry in the GeometryCollection gc. Geometries are\nnumbered beginning with 1.\n\nST_GeometryN() and GeometryN() are synonyms.\n\nExample\n-------\n\nSET @gc = 'GeometryCollection(Point(1 1),LineString(12 14, 9 11))';\n\nSELECT AsText(GeometryN(GeomFromText(@gc),1));\n+----------------------------------------+\n| AsText(GeometryN(GeomFromText(@gc),1)) |\n+----------------------------------------+\n| POINT(1 1) |\n+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_geometryn/
+[ST_GEOMETRYTYPE]
+declaration=g
+category=Geometry Properties
+description=Returns as a string the name of the geometry type of which the geometry\ninstance g is a member. The name corresponds to one of the instantiable\nGeometry subclasses.\n\nST_GeometryType() and GeometryType() are synonyms.\n\nExamples\n--------\n\nSELECT GeometryType(GeomFromText('POINT(1 1)'));\n+------------------------------------------+\n| GeometryType(GeomFromText('POINT(1 1)')) |\n+------------------------------------------+\n| POINT |\n+------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_geometrytype/
+[ST_GeomCollFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a GEOMETRYCOLLECTION value using its WKT representation and SRID.\n\nST_GeomCollFromText(), ST_GeometryCollectionFromText(), GeomCollFromText() and\nGeometryCollectionFromText() are all synonyms.\n\nExample\n-------\n\nCREATE TABLE gis_geometrycollection (g GEOMETRYCOLLECTION);\nSHOW FIELDS FROM gis_geometrycollection;\nINSERT INTO gis_geometrycollection VALUES\n (GeomCollFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0,10\n10))')),\n (GeometryFromWKB(AsWKB(GeometryCollection(Point(44, 6),\nLineString(Point(3, 6), Point(7, 9)))))),\n (GeomFromText('GeometryCollection()')),\n (GeomFromText('GeometryCollection EMPTY'));\n\nURL: https://mariadb.com/kb/en/st_geomcollfromtext/
+[ST_GeomCollFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a GEOMETRYCOLLECTION value using its WKB representation and SRID.\n\nST_GeomCollFromWKB(), ST_GeometryCollectionFromWKB(), GeomCollFromWKB() and\nGeometryCollectionFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(ST_GeomFromText('GEOMETRYCOLLECTION(\n POLYGON((5 5,10 5,10 10,5 5)),POINT(10 10))'));\n\nSELECT ST_AsText(ST_GeomCollFromWKB(@g));\n+----------------------------------------------------------------+\n| ST_AsText(ST_GeomCollFromWKB(@g)) |\n+----------------------------------------------------------------+\n| GEOMETRYCOLLECTION(POLYGON((5 5,10 5,10 10,5 5)),POINT(10 10)) |\n+----------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_geomcollfromwkb/
+[ST_GeomFromGeoJSON]
+declaration=g[, option]
+category=GeoJSON
+description=Given a GeoJSON input g, returns a geometry object. The option specifies what\nto do if g contains geometries with coordinate dimensions higher than 2.\n\n+---------------------------+------------------------------------------------+\n| Option | Description |\n+---------------------------+------------------------------------------------+\n| 1 | Return an error (the default) |\n+---------------------------+------------------------------------------------+\n| 2 - 4 | The document is accepted, but the coordinates |\n| | for higher coordinate dimensions are stripped |\n| | off. |\n+---------------------------+------------------------------------------------+\n\nNote that this function did not work correctly before MariaDB 10.2.8 - see\nMDEV-12180.\n\nExamples\n--------\n\nSET @j = '{ "type": "Point", "coordinates": [5.3, 15.0]}';\n\nSELECT ST_AsText(ST_GeomFromGeoJSON(@j));\n+-----------------------------------+\n| ST_AsText(ST_GeomFromGeoJSON(@j)) |\n+-----------------------------------+\n| POINT(5.3 15) |\n+-----------------------------------+\n\nURL: https://mariadb.com/kb/en/st_geomfromgeojson/
+[ST_GeomFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a geometry value of any type using its WKT representation and SRID.\n\nGeomFromText(), GeometryFromText(), ST_GeomFromText() and\nST_GeometryFromText() are all synonyms.\n\nExample\n-------\n\nSET @g = ST_GEOMFROMTEXT('POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1 1))');\n\nURL: https://mariadb.com/kb/en/st_geomfromtext/
+[ST_GeomFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a geometry value of any type using its WKB representation and SRID.\n\nST_GeomFromWKB(), ST_GeometryFromWKB(), GeomFromWKB() and GeometryFromWKB()\nare synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(ST_LineFromText('LINESTRING(0 4, 4 6)'));\n\nSELECT ST_AsText(ST_GeomFromWKB(@g));\n+-------------------------------+\n| ST_AsText(ST_GeomFromWKB(@g)) |\n+-------------------------------+\n| LINESTRING(0 4,4 6) |\n+-------------------------------+\n\nURL: https://mariadb.com/kb/en/st_geomfromwkb/
+[ST_INTERSECTION]
+declaration=g1,g2
+category=Geometry Constructors
+description=Returns a geometry that is the intersection, or shared portion, of geometry g1\nand geometry g2.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(2 1)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(2 1, 0 2)');\n\nSELECT ASTEXT(ST_INTERSECTION(@g1,@g2));\n+----------------------------------+\n| ASTEXT(ST_INTERSECTION(@g1,@g2)) |\n+----------------------------------+\n| POINT(2 1) |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/st_intersection/
+[ST_INTERSECTS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 spatially intersects geometry\ng2.\n\nST_INTERSECTS() uses object shapes, while INTERSECTS(), based on the original\nMySQL implementation, uses object bounding rectangles.\n\nST_INTERSECTS() tests the opposite relationship to ST_DISJOINT().\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(0 0)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(0 0, 0 2)');\n\nSELECT ST_INTERSECTS(@g1,@g2);\n+------------------------+\n| ST_INTERSECTS(@g1,@g2) |\n+------------------------+\n| 1 |\n+------------------------+\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(2 0, 0 2)');\n\nSELECT ST_INTERSECTS(@g1,@g2);\n+------------------------+\n| ST_INTERSECTS(@g1,@g2) |\n+------------------------+\n| 0 |\n+------------------------+\n\nURL: https://mariadb.com/kb/en/st-intersects/
+[ST_ISCLOSED]
+declaration=g
+category=Geometry Properties
+description=Returns 1 if a given LINESTRING's start and end points are the same, or 0 if\nthey are not the same. Before MariaDB 10.1.5, returns NULL if not given a\nLINESTRING. After MariaDB 10.1.5, returns -1.\n\nST_IsClosed() and IsClosed() are synonyms.\n\nExamples\n--------\n\nSET @ls = 'LineString(0 0, 0 4, 4 4, 0 0)';\nSELECT ST_ISCLOSED(GEOMFROMTEXT(@ls));\n+--------------------------------+\n| ST_ISCLOSED(GEOMFROMTEXT(@ls)) |\n+--------------------------------+\n| 1 |\n+--------------------------------+\n\nSET @ls = 'LineString(0 0, 0 4, 4 4, 0 1)';\nSELECT ST_ISCLOSED(GEOMFROMTEXT(@ls));\n+--------------------------------+\n| ST_ISCLOSED(GEOMFROMTEXT(@ls)) |\n+--------------------------------+\n| 0 |\n+--------------------------------+\n\nURL: https://mariadb.com/kb/en/st_isclosed/
+[ST_ISEMPTY]
+declaration=g
+category=Geometry Properties
+description=IsEmpty is a function defined by the OpenGIS specification, but is not fully\nimplemented by MariaDB or MySQL.\n\nSince MariaDB and MySQL do not support GIS EMPTY values such as POINT EMPTY,\nas implemented it simply returns 1 if the geometry value g is invalid, 0 if it\nis valid, and NULL if the argument is NULL.\n\nST_IsEmpty() and IsEmpty() are synonyms.\n\nURL: https://mariadb.com/kb/en/st_isempty/
+[ST_InteriorRingN]
+declaration=poly,N
+category=Polygon Properties
+description=Returns the N-th interior ring for the Polygon value poly as a LineString.\nRings are numbered beginning with 1.\n\nST_InteriorRingN() and InteriorRingN() are synonyms.\n\nExamples\n--------\n\nSET @poly = 'Polygon((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))';\n\nSELECT AsText(InteriorRingN(GeomFromText(@poly),1));\n+----------------------------------------------+\n| AsText(InteriorRingN(GeomFromText(@poly),1)) |\n+----------------------------------------------+\n| LINESTRING(1 1,1 2,2 2,2 1,1 1) |\n+----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_interiorringn/
+[ST_IsRing]
+declaration=g
+category=Geometry Properties
+description=Returns true if a given LINESTRING is a ring, that is, both ST_IsClosed and\nST_IsSimple. A simple curve does not pass through the same point more than\nonce. However, see MDEV-7510.\n\nSt_IsRing() and IsRing() are synonyms.\n\nURL: https://mariadb.com/kb/en/st_isring/
+[ST_IsSimple]
+declaration=g
+category=Geometry Properties
+description=Returns true if the given Geometry has no anomalous geometric points, false if\nit does, or NULL if given a NULL value.\n\nST_IsSimple() and IsSimple() are synonyms.\n\nExamples\n--------\n\nA POINT is always simple.\n\nSET @g = 'Point(1 2)';\n\nSELECT ST_ISSIMPLE(GEOMFROMTEXT(@g));\n+-------------------------------+\n| ST_ISSIMPLE(GEOMFROMTEXT(@g)) |\n+-------------------------------+\n| 1 |\n+-------------------------------+\n\nURL: https://mariadb.com/kb/en/st_issimple/
+[ST_LENGTH]
+declaration=ls
+category=Geometry Relations
+description=Returns as a double-precision number the length of the LineString value ls in\nits associated spatial reference.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT ST_LENGTH(ST_GeomFromText(@ls));\n+---------------------------------+\n| ST_LENGTH(ST_GeomFromText(@ls)) |\n+---------------------------------+\n| 2.82842712474619 |\n+---------------------------------+\n\nURL: https://mariadb.com/kb/en/st_length/
+[ST_LineFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a LINESTRING value using its WKT representation and SRID.\n\nST_LineFromText(), ST_LineStringFromText(), ST_LineFromText() and\nST_LineStringFromText() are all synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_line (g LINESTRING);\nSHOW FIELDS FROM gis_line;\nINSERT INTO gis_line VALUES\n (LineFromText('LINESTRING(0 0,0 10,10 0)')),\n (LineStringFromText('LINESTRING(10 10,20 10,20 20,10 20,10 10)')),\n (LineStringFromWKB(AsWKB(LineString(Point(10, 10), Point(40, 10)))));\n\nURL: https://mariadb.com/kb/en/st_linefromtext/
+[ST_LineFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a LINESTRING value using its WKB representation and SRID.\n\nST_LineFromWKB(), LineFromWKB(), ST_LineStringFromWKB(), and\nLineStringFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(ST_LineFromText('LineString(0 4,4 6)'));\n\nSELECT ST_AsText(ST_LineFromWKB(@g)) AS l;\n+---------------------+\n| l |\n+---------------------+\n| LINESTRING(0 4,4 6) |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/st_linefromwkb/
+[ST_NUMGEOMETRIES]
+declaration=gc
+category=Geometry Properties
+description=Returns the number of geometries in the GeometryCollection gc.\n\nST_NumGeometries() and NumGeometries() are synonyms.\n\nExample\n-------\n\nSET @gc = 'GeometryCollection(Point(1 1),LineString(2 2, 3 3))';\n\nSELECT NUMGEOMETRIES(GeomFromText(@gc));\n+----------------------------------+\n| NUMGEOMETRIES(GeomFromText(@gc)) |\n+----------------------------------+\n| 2 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/st_numgeometries/
+[ST_NUMPOINTS]
+declaration=ls
+category=LineString Properties
+description=Returns the number of Point objects in the LineString value ls.\n\nST_NumPoints() and NumPoints() are synonyms.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT NumPoints(GeomFromText(@ls));\n+------------------------------+\n| NumPoints(GeomFromText(@ls)) |\n+------------------------------+\n| 3 |\n+------------------------------+\n\nURL: https://mariadb.com/kb/en/st_numpoints/
+[ST_NumInteriorRings]
+declaration=poly
+category=Polygon Properties
+description=Returns an integer containing the number of interior rings in the Polygon\nvalue poly.\n\nNote that according the the OpenGIS standard, a POLYGON should have exactly\none ExteriorRing and all other rings should lie within that ExteriorRing and\nthus be the InteriorRings. Practically, however, some systems, including\nMariaDB's, permit polygons to have several 'ExteriorRings'. In the case of\nthere being multiple, non-overlapping exterior rings ST_NumInteriorRings()\nwill return 1.\n\nST_NumInteriorRings() and NumInteriorRings() are synonyms.\n\nExamples\n--------\n\nSET @poly = 'Polygon((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))';\n\nSELECT NumInteriorRings(GeomFromText(@poly));\n+---------------------------------------+\n| NumInteriorRings(GeomFromText(@poly)) |\n+---------------------------------------+\n| 1 |\n+---------------------------------------+\n\nNon-overlapping 'polygon':\n\nSELECT ST_NumInteriorRings(ST_PolyFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),\n (-1 -1,-5 -1,-5 -5,-1 -5,-1 -1))')) AS NumInteriorRings;\n+------------------+\n| NumInteriorRings |\n+------------------+\n| 1 |\n+------------------+\n\nURL: https://mariadb.com/kb/en/st_numinteriorrings/
+[ST_OVERLAPS]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 spatially overlaps geometry g2.\n\nThe term spatially overlaps is used if two geometries intersect and their\nintersection results in a geometry of the same dimension but not equal to\neither of the given geometries.\n\nST_OVERLAPS() uses object shapes, while OVERLAPS(), based on the original\nMySQL implementation, uses object bounding rectangles.\n\nURL: https://mariadb.com/kb/en/st-overlaps/
+[ST_POINTN]
+declaration=ls,N
+category=LineString Properties
+description=Returns the N-th Point in the LineString value ls. Points are numbered\nbeginning with 1.\n\nST_PointN() and PointN() are synonyms.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT AsText(PointN(GeomFromText(@ls),2));\n+-------------------------------------+\n| AsText(PointN(GeomFromText(@ls),2)) |\n+-------------------------------------+\n| POINT(2 2) |\n+-------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_pointn/
+[ST_POINTONSURFACE]
+declaration=
+category=Geometry Constructors
+description=Given a geometry, returns a POINT guaranteed to intersect a surface. However,\nsee MDEV-7514.\n\nST_PointOnSurface() and PointOnSurface() are synonyms.\n\nURL: https://mariadb.com/kb/en/st_pointonsurface/
+[ST_PointFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a POINT value using its WKT representation and SRID.\n\nST_PointFromText() and PointFromText() are synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_point (g POINT);\nSHOW FIELDS FROM gis_point;\nINSERT INTO gis_point VALUES\n (PointFromText('POINT(10 10)')),\n (PointFromText('POINT(20 10)')),\n (PointFromText('POINT(20 20)')),\n (PointFromWKB(AsWKB(PointFromText('POINT(10 20)'))));\n\nURL: https://mariadb.com/kb/en/st_pointfromtext/
+[ST_PointFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a POINT value using its WKB representation and SRID.\n\nST_PointFromWKB() and PointFromWKB() are synonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(ST_PointFromText('POINT(0 4)'));\n\nSELECT ST_AsText(ST_PointFromWKB(@g)) AS p;\n+------------+\n| p |\n+------------+\n| POINT(0 4) |\n+------------+\n\nURL: https://mariadb.com/kb/en/st_pointfromwkb/
+[ST_PolyFromText]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a POLYGON value using its WKT representation and SRID.\n\nST_PolyFromText(), ST_PolygonFromText(), PolyFromText() and\nST_PolygonFromText() are all synonyms.\n\nExamples\n--------\n\nCREATE TABLE gis_polygon (g POLYGON);\nINSERT INTO gis_polygon VALUES\n (PolygonFromText('POLYGON((10 10,20 10,20 20,10 20,10 10))')),\n (PolyFromText('POLYGON((0 0,50 0,50 50,0 50,0 0), (10 10,20 10,20 20,10\n20,10 10))'));\n\nURL: https://mariadb.com/kb/en/st_polyfromtext/
+[ST_PolyFromWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a POLYGON value using its WKB representation and SRID.\n\nST_PolyFromWKB(), ST_PolygonFromWKB(), PolyFromWKB() and PolygonFromWKB() are\nsynonyms.\n\nExamples\n--------\n\nSET @g = ST_AsBinary(ST_PolyFromText('POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1\n1))'));\n\nSELECT ST_AsText(ST_PolyFromWKB(@g)) AS p;\n+----------------------------------------+\n| p |\n+----------------------------------------+\n| POLYGON((1 1,1 5,4 9,6 9,9 3,7 2,1 1)) |\n+----------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_polyfromwkb/
+[ST_RELATE]
+declaration=
+category=Geometry Properties
+description=Returns true if Geometry g1 is spatially related to Geometryg2 by testing for\nintersections between the interior, boundary and exterior of the two\ngeometries as specified by the values in intersection matrix pattern i.\n\nURL: https://mariadb.com/kb/en/st_relate/
+[ST_SRID]
+declaration=g
+category=Geometry Properties
+description=Returns an integer indicating the Spatial Reference System ID for the geometry\nvalue g.\n\nIn MariaDB, the SRID value is just an integer associated with the geometry\nvalue. All calculations are done assuming Euclidean (planar) geometry.\n\nST_SRID() and SRID() are synonyms.\n\nExamples\n--------\n\nSELECT SRID(GeomFromText('LineString(1 1,2 2)',101));\n+-----------------------------------------------+\n| SRID(GeomFromText('LineString(1 1,2 2)',101)) |\n+-----------------------------------------------+\n| 101 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_srid/
+[ST_STARTPOINT]
+declaration=ls
+category=LineString Properties
+description=Returns the Point that is the start point of the LineString value ls.\n\nST_StartPoint() and StartPoint() are synonyms.\n\nExamples\n--------\n\nSET @ls = 'LineString(1 1,2 2,3 3)';\n\nSELECT AsText(StartPoint(GeomFromText(@ls)));\n+---------------------------------------+\n| AsText(StartPoint(GeomFromText(@ls))) |\n+---------------------------------------+\n| POINT(1 1) |\n+---------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_startpoint/
+[ST_SYMDIFFERENCE]
+declaration=g1,g2
+category=Geometry Constructors
+description=Returns a geometry that represents the portions of geometry g1 and geometry g2\nthat don't intersect.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('LINESTRING(10 20, 10 40)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(10 15, 10 25)');\n\nSELECT ASTEXT(ST_SYMDIFFERENCE(@g1,@g2));\n+----------------------------------------------+\n| ASTEXT(ST_SYMDIFFERENCE(@g1,@g2)) |\n+----------------------------------------------+\n| MULTILINESTRING((10 15,10 20),(10 25,10 40)) |\n+----------------------------------------------+\n\nSET @g2 = ST_GeomFromText('LINESTRING(10 20, 10 41)');\n\nSELECT ASTEXT(ST_SYMDIFFERENCE(@g1,@g2));\n+-----------------------------------+\n| ASTEXT(ST_SYMDIFFERENCE(@g1,@g2)) |\n+-----------------------------------+\n| LINESTRING(10 40,10 41) |\n+-----------------------------------+\n\nURL: https://mariadb.com/kb/en/st_symdifference/
+[ST_TOUCHES]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 spatially touches geometry g2.\nTwo geometries spatially touch if the interiors of the geometries do not\nintersect, but the boundary of one of the geometries intersects either the\nboundary or the interior of the other.\n\nST_TOUCHES() uses object shapes, while TOUCHES(), based on the original MySQL\nimplementation, uses object bounding rectangles.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(2 0)');\n\nSET @g2 = ST_GEOMFROMTEXT('LINESTRING(2 0, 0 2)');\n\nSELECT ST_TOUCHES(@g1,@g2);\n+---------------------+\n| ST_TOUCHES(@g1,@g2) |\n+---------------------+\n| 1 |\n+---------------------+\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(2 1)');\n\nSELECT ST_TOUCHES(@g1,@g2);\n+---------------------+\n| ST_TOUCHES(@g1,@g2) |\n+---------------------+\n| 0 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/st-touches/
+[ST_UNION]
+declaration=g1,g2
+category=Geometry Constructors
+description=Returns a geometry that is the union of the geometry g1 and geometry g2.\n\nExamples\n--------\n\nSET @g1 = GEOMFROMTEXT('POINT (0 2)');\n\nSET @g2 = GEOMFROMTEXT('POINT (2 0)');\n\nSELECT ASTEXT(ST_UNION(@g1,@g2));\n+---------------------------+\n| ASTEXT(ST_UNION(@g1,@g2)) |\n+---------------------------+\n| MULTIPOINT(2 0,0 2) |\n+---------------------------+\n\nSET @g1 = GEOMFROMTEXT('POLYGON((0 0,0 3,3 3,3 0,0 0))');\n\nSET @g2 = GEOMFROMTEXT('POLYGON((2 2,4 2,4 4,2 4,2 2))');\n\nSELECT ASTEXT(ST_UNION(@g1,@g2));\n+------------------------------------------------+\n| ASTEXT(ST_UNION(@g1,@g2)) |\n+------------------------------------------------+\n| POLYGON((0 0,0 3,2 3,2 4,4 4,4 2,3 2,3 0,0 0)) |\n+------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/st_union/
+[ST_WITHIN]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether geometry g1 is spatially within geometry g2.\n\nThis tests the opposite relationship as ST_CONTAINS().\n\nST_WITHIN() uses object shapes, while WITHIN(), based on the original MySQL\nimplementation, uses object bounding rectangles.\n\nExamples\n--------\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(174 149)');\n\nSET @g2 = ST_GEOMFROMTEXT('POLYGON((175 150, 20 40, 50 60, 125 100, 175\n150))');\n\nSELECT ST_WITHIN(@g1,@g2);\n+--------------------+\n| ST_WITHIN(@g1,@g2) |\n+--------------------+\n| 1 |\n+--------------------+\n\nSET @g1 = ST_GEOMFROMTEXT('POINT(176 151)');\n\nSELECT ST_WITHIN(@g1,@g2);\n+--------------------+\n| ST_WITHIN(@g1,@g2) |\n+--------------------+\n| 0 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/st-within/
+[ST_X]
+declaration=p
+category=Point Properties
+description=Returns the X-coordinate value for the point p as a double-precision number.\n\nST_X() and X() are synonyms.\n\nExamples\n--------\n\nSET @pt = 'Point(56.7 53.34)';\n\nSELECT X(GeomFromText(@pt));\n+----------------------+\n| X(GeomFromText(@pt)) |\n+----------------------+\n| 56.7 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st_x/
+[ST_Y]
+declaration=p
+category=Point Properties
+description=Returns the Y-coordinate value for the point p as a double-precision number.\n\nST_Y() and Y() are synonyms.\n\nExamples\n--------\n\nSET @pt = 'Point(56.7 53.34)';\n\nSELECT Y(GeomFromText(@pt));\n+----------------------+\n| Y(GeomFromText(@pt)) |\n+----------------------+\n| 53.34 |\n+----------------------+\n\nURL: https://mariadb.com/kb/en/st_y/
+[SUBDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, SUBDATE() is a\nsynonym for DATE_SUB(). See Date and Time Units for a complete list of\npermitted units.\n\nThe second form allows the use of an integer value for days. In such cases, it\nis interpreted as the number of days to be subtracted from the date or\ndatetime expression expr.\n\nExamples\n--------\n\nSELECT DATE_SUB('2008-01-02', INTERVAL 31 DAY);\n+-----------------------------------------+\n| DATE_SUB('2008-01-02', INTERVAL 31 DAY) |\n+-----------------------------------------+\n| 2007-12-02 |\n+-----------------------------------------+\n\nSELECT SUBDATE('2008-01-02', INTERVAL 31 DAY);\n+----------------------------------------+\n| SUBDATE('2008-01-02', INTERVAL 31 DAY) |\n+----------------------------------------+\n| 2007-12-02 |\n+----------------------------------------+\n\nSELECT SUBDATE('2008-01-02 12:00:00', 31);\n+------------------------------------+\n| SUBDATE('2008-01-02 12:00:00', 31) |\n+------------------------------------+\n| 2007-12-02 12:00:00 |\n+------------------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d, SUBDATE(d, 10) from t1;\n+---------------------+---------------------+\n| d | SUBDATE(d, 10) |\n+---------------------+---------------------+\n| 2007-01-30 21:31:07 | 2007-01-20 21:31:07 |\n| 1983-10-15 06:42:51 | 1983-10-05 06:42:51 |\n| 2011-04-21 12:34:56 | 2011-04-11 12:34:56 |\n| 2011-10-30 06:31:41 | 2011-10-20 06:31:41 |\n| 2011-01-30 14:03:25 | 2011-01-20 14:03:25 |\n ...
+[SUBSTR]
+declaration=
+category=String Functions
+description=URL: https://mariadb.com/kb/en/substr/
+[SUBSTRING]
+declaration=str,pos
+category=String Functions
+description=The forms without a len argument return a substring from string str starting\nat position pos.\n\nThe forms with a len argument return a substring len characters long from\nstring str, starting at position pos.\n\nThe forms that use FROM are standard SQL syntax.\n\nIt is also possible to use a negative value for pos. In this case, the\nbeginning of the substring is pos characters from the end of the string,\nrather than the beginning. A negative value may be used for pos in any of the\nforms of this function.\n\nBy default, the position of the first character in the string from which the\nsubstring is to be extracted is reckoned as 1. For Oracle-compatibility, from\nMariaDB 10.3.3, when sql_mode is set to 'oracle', position zero is treated as\nposition 1 (although the first character is still reckoned as 1).\n\nIf any argument is NULL, returns NULL.\n\nExamples\n--------\n\nSELECT SUBSTRING('Knowledgebase',5);\n+------------------------------+\n| SUBSTRING('Knowledgebase',5) |\n+------------------------------+\n| ledgebase |\n+------------------------------+\n\nSELECT SUBSTRING('MariaDB' FROM 6);\n+-----------------------------+\n| SUBSTRING('MariaDB' FROM 6) |\n+-----------------------------+\n| DB |\n+-----------------------------+\n\nSELECT SUBSTRING('Knowledgebase',3,7);\n+--------------------------------+\n| SUBSTRING('Knowledgebase',3,7) |\n+--------------------------------+\n| owledge |\n+--------------------------------+\n\nSELECT SUBSTRING('Knowledgebase', -4);\n+--------------------------------+\n| SUBSTRING('Knowledgebase', -4) |\n+--------------------------------+\n| base |\n+--------------------------------+\n ...
+[SUBSTRING_INDEX]
+declaration=str,delim,count
+category=String Functions
+description=Returns the substring from string str before count occurrences of the\ndelimiter delim. If count is positive, everything to the left of the final\ndelimiter (counting from the left) is returned. If count is negative,\neverything to the right of the final delimiter (counting from the right) is\nreturned. SUBSTRING_INDEX() performs a case-sensitive match when searching for\ndelim.\n\nIf any argument is NULL, returns NULL.\n\nFor example\n\nSUBSTRING_INDEX('www.mariadb.org', '.', 2)\n\nmeans "Return all of the characters up to the 2nd occurrence of ."\n\nExamples\n--------\n\nSELECT SUBSTRING_INDEX('www.mariadb.org', '.', 2);\n+--------------------------------------------+\n| SUBSTRING_INDEX('www.mariadb.org', '.', 2) |\n+--------------------------------------------+\n| www.mariadb |\n+--------------------------------------------+\n\nSELECT SUBSTRING_INDEX('www.mariadb.org', '.', -2);\n+---------------------------------------------+\n| SUBSTRING_INDEX('www.mariadb.org', '.', -2) |\n+---------------------------------------------+\n| mariadb.org |\n+---------------------------------------------+\n\nURL: https://mariadb.com/kb/en/substring_index/
+[SUBTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=SUBTIME() returns expr1 - expr2 expressed as a value in the same format as\nexpr1. expr1 is a time or datetime expression, and expr2 is a time expression.\n\nExamples\n--------\n\nSELECT SUBTIME('2007-12-31 23:59:59.999999','1 1:1:1.000002');\n+--------------------------------------------------------+\n| SUBTIME('2007-12-31 23:59:59.999999','1 1:1:1.000002') |\n+--------------------------------------------------------+\n| 2007-12-30 22:58:58.999997 |\n+--------------------------------------------------------+\n\nSELECT SUBTIME('01:00:00.999999', '02:00:00.999998');\n+-----------------------------------------------+\n| SUBTIME('01:00:00.999999', '02:00:00.999998') |\n+-----------------------------------------------+\n| -00:59:59.999999 |\n+-----------------------------------------------+\n\nURL: https://mariadb.com/kb/en/subtime/
+[SUM]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sum of expr. If the return set has no rows, SUM() returns NULL.\nThe DISTINCT keyword can be used to sum only the distinct values of expr.\n\nSUM() can be used as a window function, although not with the DISTINCT\nspecifier.\n\nExamples\n--------\n\nCREATE TABLE sales (sales_value INT);\nINSERT INTO sales VALUES(10),(20),(20),(40);\n\nSELECT SUM(sales_value) FROM sales;\n+------------------+\n| SUM(sales_value) |\n+------------------+\n| 90 |\n+------------------+\n\nSELECT SUM(DISTINCT(sales_value)) FROM sales;\n+----------------------------+\n| SUM(DISTINCT(sales_value)) |\n+----------------------------+\n| 70 |\n+----------------------------+\n\nCommonly, SUM is used with a GROUP BY clause:\n\nCREATE TABLE sales (name CHAR(10), month CHAR(10), units INT);\n\nINSERT INTO sales VALUES \n ('Chun', 'Jan', 75), ('Chun', 'Feb', 73),\n ('Esben', 'Jan', 43), ('Esben', 'Feb', 31),\n ('Kaolin', 'Jan', 56), ('Kaolin', 'Feb', 88),\n ('Tatiana', 'Jan', 87), ('Tatiana', 'Feb', 83);\n\nSELECT name, SUM(units) FROM sales GROUP BY name;\n+---------+------------+\n| name | SUM(units) |\n+---------+------------+\n| Chun | 148 |\n| Esben | 74 |\n| Kaolin | 144 |\n| Tatiana | 170 |\n+---------+------------+\n\nThe GROUP BY clause is required when using an aggregate function along with\nregular column data, otherwise the result will be a mismatch, as in the\nfollowing common type of mistake:\n\n ...
+[SYSDATE]
+declaration=[precision]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nSYSDATE() returns the time at which it executes. This differs from the\nbehavior for NOW(), which returns a constant time that indicates the time at\nwhich the statement began to execute. (Within a stored routine or trigger,\nNOW() returns the time at which the routine or triggering statement began to\nexecute.)\n\nIn addition, changing the timestamp system variable with a SET timestamp\nstatement affects the value returned by NOW() but not by SYSDATE(). This means\nthat timestamp settings in the binary log have no effect on invocations of\nSYSDATE().\n\nBecause SYSDATE() can return different values even within the same statement,\nand is not affected by SET TIMESTAMP, it is non-deterministic and therefore\nunsafe for replication if statement-based binary logging is used. If that is a\nproblem, you can use row-based logging, or start the server with the mysqld\noption --sysdate-is-now to cause SYSDATE() to be an alias for NOW(). The\nnon-deterministic nature of SYSDATE() also means that indexes cannot be used\nfor evaluating expressions that refer to it, and that statements using the\nSYSDATE() function are unsafe for statement-based replication.\n\nExamples\n--------\n\nDifference between NOW() and SYSDATE():\n\nSELECT NOW(), SLEEP(2), NOW();\n+---------------------+----------+---------------------+\n| NOW() | SLEEP(2) | NOW() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:40 | 0 | 2010-03-27 13:23:40 |\n+---------------------+----------+---------------------+\n\nSELECT SYSDATE(), SLEEP(2), SYSDATE();\n+---------------------+----------+---------------------+\n| SYSDATE() | SLEEP(2) | SYSDATE() |\n+---------------------+----------+---------------------+\n| 2010-03-27 13:23:52 | 0 | 2010-03-27 13:23:54 |\n+---------------------+----------+---------------------+\n\nWith precision:\n\nSELECT SYSDATE(4);\n+--------------------------+\n ...
+[SYSTEM_USER]
+declaration=
+category=Information Functions
+description=SYSTEM_USER() is a synonym for USER().\n\nURL: https://mariadb.com/kb/en/system_user/
+[SYS_GUID]
+declaration=
+category=Miscellaneous Functions
+description=Returns a 16-byte globally unique identifier (GUID), similar to the UUID\nfunction, but without the - character.\n\nExample\n-------\n\nSELECT SYS_GUID();\n+----------------------------------+\n| SYS_GUID() |\n+----------------------------------+\n| 2C574E45BA2811EBB265F859713E4BE4 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/sys_guid/
+[TAN]
+declaration=X
+category=Numeric Functions
+description=Returns the tangent of X, where X is given in radians.\n\nExamples\n--------\n\nSELECT TAN(0.7853981633974483);\n+-------------------------+\n| TAN(0.7853981633974483) |\n+-------------------------+\n| 0.9999999999999999 |\n+-------------------------+\n\nSELECT TAN(PI());\n+-----------------------+\n| TAN(PI()) |\n+-----------------------+\n| -1.22460635382238e-16 |\n+-----------------------+\n\nSELECT TAN(PI()+1);\n+-----------------+\n| TAN(PI()+1) |\n+-----------------+\n| 1.5574077246549 |\n+-----------------+\n\nSELECT TAN(RADIANS(PI()));\n+--------------------+\n| TAN(RADIANS(PI())) |\n+--------------------+\n| 0.0548861508080033 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/tan/
+[TEXT]
+declaration=M
+category=Data Types
+description=A TEXT column with a maximum length of 65,535 (216 - 1) characters. The\neffective maximum length is less if the value contains multi-byte characters.\nEach TEXT value is stored using a two-byte length prefix that indicates the\nnumber of bytes in the value. If you need a bigger storage, consider using\nMEDIUMTEXT instead.\n\nAn optional length M can be given for this type. If this is done, MariaDB\ncreates the column as the smallest TEXT type large enough to hold values M\ncharacters long.\n\nBefore MariaDB 10.2, all MariaDB collations were of type PADSPACE, meaning\nthat TEXT (as well as VARCHAR and CHAR values) are compared without regard for\ntrailing spaces. This does not apply to the LIKE pattern-matching operator,\nwhich takes into account trailing spaces.\n\nBefore MariaDB 10.2.1, BLOB and TEXT columns could not be assigned a DEFAULT\nvalue. This restriction was lifted in MariaDB 10.2.1.\n\nExamples\n--------\n\nTrailing spaces:\n\nCREATE TABLE strtest (d TEXT(10));\nINSERT INTO strtest VALUES('Maria ');\n\nSELECT d='Maria',d='Maria ' FROM strtest;\n+-----------+--------------+\n| d='Maria' | d='Maria ' |\n+-----------+--------------+\n| 1 | 1 |\n+-----------+--------------+\n\nSELECT d LIKE 'Maria',d LIKE 'Maria ' FROM strtest;\n+----------------+-------------------+\n| d LIKE 'Maria' | d LIKE 'Maria ' |\n+----------------+-------------------+\n| 0 | 1 |\n+----------------+-------------------+\n\nIndexing\n--------\n\nTEXT columns can only be indexed over a specified length. This means that they\ncannot be used as the primary key of a table norm until MariaDB 10.4, can a\nunique index be created on them.\n\nMariaDB starting with 10.4\n--------------------------\nStarting with MariaDB 10.4, a unique index can be created on a TEXT column.\n ...
+[TIME]
+declaration=
+category=Data Types
+description=A time. The range is '-838:59:59.999999' to '838:59:59.999999'. Microsecond\nprecision can be from 0-6; if not specified 0 is used. Microseconds have been\navailable since MariaDB 5.3.\n\nMariaDB displays TIME values in 'HH:MM:SS.ssssss' format, but allows\nassignment of times in looser formats, including 'D HH:MM:SS', 'HH:MM:SS',\n'HH:MM', 'D HH:MM', 'D HH', 'SS', or 'HHMMSS', as well as permitting dropping\nof any leading zeros when a delimiter is provided, for example '3:9:10'. For\ndetails, see date and time literals.\n\nMariaDB 10.1.2 introduced the --mysql56-temporal-format option, on by default,\nwhich allows MariaDB to store TIMEs using the same low-level format MySQL 5.6\nuses.\n\nInternal Format\n---------------\n\nIn MariaDB 10.1.2 a new temporal format was introduced from MySQL 5.6 that\nalters how the TIME, DATETIME and TIMESTAMP columns operate at lower levels.\nThese changes allow these temporal data types to have fractional parts and\nnegative values. You can disable this feature using the\nmysql56_temporal_format system variable.\n\nTables that include TIMESTAMP values that were created on an older version of\nMariaDB or that were created while the mysql56_temporal_format system variable\nwas disabled continue to store data using the older data type format.\n\nIn order to update table columns from the older format to the newer format,\nexecute an ALTER TABLE... MODIFY COLUMN statement that changes the column to\nthe *same* data type. This change may be needed if you want to export the\ntable's tablespace and import it onto a server that has\nmysql56_temporal_format=ON set (see MDEV-15225).\n\nFor instance, if you have a TIME column in your table:\n\nSHOW VARIABLES LIKE 'mysql56_temporal_format';\n\n+-------------------------+-------+\n| Variable_name | Value |\n+-------------------------+-------+\n| mysql56_temporal_format | ON |\n+-------------------------+-------+\n\nALTER TABLE example_table MODIFY ts_col TIME;\n\nWhen MariaDB executes the ALTER TABLE statement, it converts the data from the\nolder temporal format to the newer one.\n\nIn the event that you have several tables and columns using temporal data\ntypes that you want to switch over to the new format, make sure the system\n ...
+[TIMEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=TIMEDIFF() returns expr1 - expr2 expressed as a time value. expr1 and expr2\nare time or date-and-time expressions, but both must be of the same type.\n\nExamples\n--------\n\nSELECT TIMEDIFF('2000:01:01 00:00:00', '2000:01:01 00:00:00.000001');\n+---------------------------------------------------------------+\n| TIMEDIFF('2000:01:01 00:00:00', '2000:01:01 00:00:00.000001') |\n+---------------------------------------------------------------+\n| -00:00:00.000001 |\n+---------------------------------------------------------------+\n\nSELECT TIMEDIFF('2008-12-31 23:59:59.000001', '2008-12-30 01:01:01.000002');\n+----------------------------------------------------------------------+\n| TIMEDIFF('2008-12-31 23:59:59.000001', '2008-12-30 01:01:01.000002') |\n+----------------------------------------------------------------------+\n| 46:58:57.999999 |\n+----------------------------------------------------------------------+\n\nURL: https://mariadb.com/kb/en/timediff/
+[TIMESTAMP]
+declaration==3;\n+------+\n| i |\n+------+\n| 1 |\n| 2 |\n| 3 |\n| 4 |\n| 5 |\n| 6 |\n+------+\n\nSELECT i FROM seqs WHERE i <= 3 UNION ALL SELECT i FROM seqs WHERE i>=3;\n+------+\n ...
+[UNIX_TIMESTAMP]
+declaration=
+category=Date and Time Functions
+description=If called with no argument, returns a Unix timestamp (seconds since\n'1970-01-01 00:00:00' UTC) as an unsigned integer. If UNIX_TIMESTAMP() is\ncalled with a date argument, it returns the value of the argument as seconds\nsince '1970-01-01 00:00:00' UTC. date may be a DATE string, a DATETIME string,\na TIMESTAMP, or a number in the format YYMMDD or YYYYMMDD. The server\ninterprets date as a value in the current time zone and converts it to an\ninternal value in UTC. Clients can set their time zone as described in time\nzones.\n\nThe inverse function of UNIX_TIMESTAMP() is FROM_UNIXTIME()\n\nUNIX_TIMESTAMP() supports microseconds.\n\nTimestamps in MariaDB have a maximum value of 2147483647, equivalent to\n2038-01-19 05:14:07. This is due to the underlying 32-bit limitation. Using\nthe function on a date beyond this will result in NULL being returned. Use\nDATETIME as a storage type if you require dates beyond this.\n\nError Handling\n--------------\n\nReturns NULL for wrong arguments to UNIX_TIMESTAMP(). In MySQL and MariaDB\nbefore 5.3 wrong arguments to UNIX_TIMESTAMP() returned 0.\n\nCompatibility\n-------------\n\nAs you can see in the examples above, UNIX_TIMESTAMP(constant-date-string)\nreturns a timestamp with 6 decimals while MariaDB 5.2 and before returns it\nwithout decimals. This can cause a problem if you are using UNIX_TIMESTAMP()\nas a partitioning function. You can fix this by using\nFLOOR(UNIX_TIMESTAMP(..)) or changing the date string to a date number, like\n20080101000000.\n\nExamples\n--------\n\nSELECT UNIX_TIMESTAMP();\n+------------------+\n| UNIX_TIMESTAMP() |\n+------------------+\n| 1269711082 |\n+------------------+\n\nSELECT UNIX_TIMESTAMP('2007-11-30 10:30:19');\n+---------------------------------------+\n| UNIX_TIMESTAMP('2007-11-30 10:30:19') |\n+---------------------------------------+\n| 1196436619.000000 |\n+---------------------------------------+\n ...
+[UPDATEXML]
+declaration=xml_target, xpath_expr, new_xml
+category=String Functions
+description=This function replaces a single portion of a given fragment of XML markup\nxml_target with a new XML fragment new_xml, and then returns the changed XML.\nThe portion of xml_target that is replaced matches an XPath expression\nxpath_expr supplied by the user. If no expression matching xpath_expr is\nfound, or if multiple matches are found, the function returns the original\nxml_target XML fragment. All three arguments should be strings.\n\nExamples\n--------\n\nSELECT\n UpdateXML('ccc', '/a', 'fff') AS val1,\n UpdateXML('ccc', '/b', 'fff') AS val2,\n UpdateXML('ccc', '//b', 'fff') AS val3,\n UpdateXML('ccc', '/a/d', 'fff') AS val4,\n UpdateXML('ccc', '/a/d', 'fff') AS val5\n \G\n*************************** 1. row ***************************\nval1: fff\nval2: ccc\nval3: fff\nval4: cccfff\nval5: ccc\n1 row in set (0.00 sec)\n\nURL: https://mariadb.com/kb/en/updatexml/
+[UPPER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to uppercase according to\nthe current character set mapping. The default is latin1 (cp1252 West\nEuropean).\n\nUCASE is a synonym.\n\nSELECT UPPER(surname), givenname FROM users ORDER BY surname;\n+----------------+------------+\n| UPPER(surname) | givenname |\n+----------------+------------+\n| ABEL | Jacinto |\n| CASTRO | Robert |\n| COSTA | Phestos |\n| MOSCHELLA | Hippolytos |\n+----------------+------------+\n\nUPPER() is ineffective when applied to binary strings (BINARY, VARBINARY,\nBLOB). The description of LOWER() shows how to perform lettercase conversion\nof binary strings.\n\nPrior to MariaDB 11.3, the query optimizer did not handle queries of the\nformat UCASE(varchar_col)=.... An optimizer_switch option,\nsargable_casefold=ON, was added in MariaDB 11.3.0 to handle this case.\n(MDEV-31496)\n\nURL: https://mariadb.com/kb/en/upper/
+[USER]
+declaration=
+category=Information Functions
+description=Returns the current MariaDB user name and host name, given when authenticating\nto MariaDB, as a string in the utf8 character set.\n\nNote that the value of USER() may differ from the value of CURRENT_USER(),\nwhich is the user used to authenticate the current client. CURRENT_ROLE()\nreturns the current active role.\n\nSYSTEM_USER() and SESSION_USER are synonyms for USER().\n\nStatements using the USER() function or one of its synonyms are not safe for\nstatement level replication.\n\nExamples\n--------\n\nshell> mysql --user="anonymous"\n\nSELECT USER(),CURRENT_USER();\n+---------------------+----------------+\n| USER() | CURRENT_USER() |\n+---------------------+----------------+\n| anonymous@localhost | @localhost |\n+---------------------+----------------+\n\nTo select only the IP address, use SUBSTRING_INDEX(),\n\nSELECT SUBSTRING_INDEX(USER(), '@', -1);\n+----------------------------------+\n| SUBSTRING_INDEX(USER(), '@', -1) |\n+----------------------------------+\n| 192.168.0.101 |\n+----------------------------------+\n\nURL: https://mariadb.com/kb/en/user/
+[UTC_DATE]
+declaration=
+category=Date and Time Functions
+description=Returns the current UTC date as a value in 'YYYY-MM-DD' or YYYYMMDD format,\ndepending on whether the function is used in a string or numeric context.\n\nExamples\n--------\n\nSELECT UTC_DATE(), UTC_DATE() + 0;\n+------------+----------------+\n| UTC_DATE() | UTC_DATE() + 0 |\n+------------+----------------+\n| 2010-03-27 | 20100327 |\n+------------+----------------+\n\nURL: https://mariadb.com/kb/en/utc_date/
+[UTC_TIME]
+declaration=[precision]
+category=Date and Time Functions
+description=Returns the current UTC time as a value in 'HH:MM:SS' or HHMMSS.uuuuuu format,\ndepending on whether the function is used in a string or numeric context.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nExamples\n--------\n\nSELECT UTC_TIME(), UTC_TIME() + 0;\n+------------+----------------+\n| UTC_TIME() | UTC_TIME() + 0 |\n+------------+----------------+\n| 17:32:34 | 173234.000000 |\n+------------+----------------+\n\nWith precision:\n\nSELECT UTC_TIME(5);\n+----------------+\n| UTC_TIME(5) |\n+----------------+\n| 07:52:50.78369 |\n+----------------+\n\nURL: https://mariadb.com/kb/en/utc_time/
+[UTC_TIMESTAMP]
+declaration=[precision]
+category=Date and Time Functions
+description=Returns the current UTC date and time as a value in 'YYYY-MM-DD HH:MM:SS' or\nYYYYMMDDHHMMSS.uuuuuu format, depending on whether the function is used in a\nstring or numeric context.\n\nThe optional precision determines the microsecond precision. See Microseconds\nin MariaDB.\n\nExamples\n--------\n\nSELECT UTC_TIMESTAMP(), UTC_TIMESTAMP() + 0;\n+---------------------+-----------------------+\n| UTC_TIMESTAMP() | UTC_TIMESTAMP() + 0 |\n+---------------------+-----------------------+\n| 2010-03-27 17:33:16 | 20100327173316.000000 |\n+---------------------+-----------------------+\n\nWith precision:\n\nSELECT UTC_TIMESTAMP(4);\n+--------------------------+\n| UTC_TIMESTAMP(4) |\n+--------------------------+\n| 2018-07-10 07:51:09.1019 |\n+--------------------------+\n\nURL: https://mariadb.com/kb/en/utc_timestamp/
+[UUID]
+declaration=
+category=Miscellaneous Functions
+description=Returns a Universally Unique Identifier (UUID).\n\nA UUID is designed as a number that is globally unique in space and time. Two\ncalls to UUID() are expected to generate two different values, even if these\ncalls are performed on two separate computers that are not connected to each\nother.\n\nUUID() results are intended to be unique, but cannot always be relied upon to\nbe unpredictable and unguessable.\n\nA UUID is a 128-bit number represented by a utf8 string of five hexadecimal\nnumbers in aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee format:\n\n* The first three numbers are generated from a timestamp.\n* The fourth number preserves temporal uniqueness in case the timestamp value\n loses monotonicity (for example, due to daylight saving time).\n* The fifth number is an IEEE 802 node number that provides spatial uniqueness.\n A random number is substituted if the latter is not available (for example,\n because the host computer has no Ethernet card, or we do not know how to find\n the hardware address of an interface on your operating system). In this case,\n spatial uniqueness cannot be guaranteed. Nevertheless, a collision should\n have very low probability.\n\nCurrently, the MAC address of an interface is taken into account only on\nFreeBSD and Linux. On other operating systems, MariaDB uses a randomly\ngenerated 48-bit number.\n\nStatements using the UUID() function are not safe for statement-based\nreplication.\n\nThe function generates a UUIDv1 and the results are generated according to the\n"DCE 1.1:Remote Procedure Call" (Appendix A) CAE (Common Applications\nEnvironment) Specifications published by The Open Group in October 1997\n(Document Number C706).\n\nExamples\n--------\n\nSELECT UUID();\n+--------------------------------------+\n| UUID() |\n+--------------------------------------+\n| cd41294a-afb0-11df-bc9b-00241dd75637 |\n+--------------------------------------+\n\nURL: https://mariadb.com/kb/en/uuid/
+[UUID_SHORT]
+declaration=
+category=Miscellaneous Functions
+description=Returns a "short" universally unique identifier as a 64-bit unsigned integer\n(rather than a string-form 128-bit identifier as returned by the UUID()\nfunction).\n\nThe value of UUID_SHORT() is guaranteed to be unique if the following\nconditions hold:\n\n* The server_id of the current host is unique among your set of master and\n slave servers\n* server_id is between 0 and 255\n* You don't set back your system time for your server between mysqld restarts\n* You do not invoke UUID_SHORT() on average more than 16\n million times per second between mysqld restarts\n\nThe UUID_SHORT() return value is constructed this way:\n\n(server_id & 255) << 56\n+ (server_startup_time_in_seconds << 24)\n+ incremented_variable++;\n\nStatements using the UUID_SHORT() function are not safe for statement-based\nreplication.\n\nExamples\n--------\n\nSELECT UUID_SHORT();\n+-------------------+\n| UUID_SHORT() |\n+-------------------+\n| 21517162376069120 |\n+-------------------+\n\ncreate table t1 (a bigint unsigned default(uuid_short()) primary key);\ninsert into t1 values(),();\nselect * from t1;\n+-------------------+\n| a |\n+-------------------+\n| 98113699159474176 |\n| 98113699159474177 |\n+-------------------+\n\nURL: https://mariadb.com/kb/en/uuid_short/
+[VARBINARY]
+declaration=M
+category=Data Types
+description=The VARBINARY type is similar to the VARCHAR type, but stores binary byte\nstrings rather than non-binary character strings. M represents the maximum\ncolumn length in bytes.\n\nIt contains no character set, and comparison and sorting are based on the\nnumeric value of the bytes.\n\nIf the maximum length is exceeded, and SQL strict mode is not enabled , the\nextra characters will be dropped with a warning. If strict mode is enabled, an\nerror will occur.\n\nUnlike BINARY values, VARBINARYs are not right-padded when inserting.\n\nOracle Mode\n-----------\n\nIn Oracle mode from MariaDB 10.3, RAW is a synonym for VARBINARY.\n\nExamples\n--------\n\nInserting too many characters, first with strict mode off, then with it on:\n\nCREATE TABLE varbins (a VARBINARY(10));\n\nINSERT INTO varbins VALUES('12345678901');\nQuery OK, 1 row affected, 1 warning (0.04 sec)\n\nSELECT * FROM varbins;\n+------------+\n| a |\n+------------+\n| 1234567890 |\n+------------+\n\nSET sql_mode='STRICT_ALL_TABLES';\n\nINSERT INTO varbins VALUES('12345678901');\nERROR 1406 (22001): Data too long for column 'a' at row 1\n\nSorting is performed with the byte value:\n\nTRUNCATE varbins;\n\nINSERT INTO varbins VALUES('A'),('B'),('a'),('b');\n\nSELECT * FROM varbins ORDER BY a;\n+------+\n| a |\n+------+\n ...
+[VARCHAR]
+declaration=M
+category=Data Types
+description=A variable-length string. M represents the maximum column length in\ncharacters. The range of M is 0 to 65,532. The effective maximum length of a\nVARCHAR is subject to the maximum row size and the character set used. For\nexample, utf8 characters can require up to three bytes per character, so a\nVARCHAR column that uses the utf8 character set can be declared to be a\nmaximum of 21,844 characters.\n\nNote: For the ColumnStore engine, M represents the maximum column length in\nbytes.\n\nMariaDB stores VARCHAR values as a one-byte or two-byte length prefix plus\ndata. The length prefix indicates the number of bytes in the value. A VARCHAR\ncolumn uses one length byte if values require no more than 255 bytes, two\nlength bytes if values may require more than 255 bytes.\n\nMariaDB follows the standard SQL specification, and does not remove trailing\nspaces from VARCHAR values.\n\nVARCHAR(0) columns can contain 2 values: an empty string or NULL. Such columns\ncannot be part of an index. The CONNECT storage engine does not support\nVARCHAR(0).\n\nVARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the standard\nSQL way to define that a VARCHAR column should use some predefined character\nset. MariaDB uses utf8 as this predefined character set, as does MySQL 4.1 and\nup. NVARCHAR is shorthand for NATIONAL VARCHAR.\n\nBefore MariaDB 10.2, all MariaDB collations were of type PADSPACE, meaning\nthat VARCHAR (as well as CHAR and TEXT values) are compared without regard for\ntrailing spaces. This does not apply to the LIKE pattern-matching operator,\nwhich takes into account trailing spaces. From MariaDB 10.2, a number of NO\nPAD collations are available.\n\nIf a unique index consists of a column where trailing pad characters are\nstripped or ignored, inserts into that column where values differ only by the\nnumber of trailing pad characters will result in a duplicate-key error.\n\nExamples\n--------\n\nThe following are equivalent:\n\nVARCHAR(30) CHARACTER SET utf8\nNATIONAL VARCHAR(30)\nNVARCHAR(30)\nNCHAR VARCHAR(30)\nNATIONAL CHARACTER VARYING(30)\nNATIONAL CHAR VARYING(30)\n\nTrailing spaces:\n ...
+[VARIANCE]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard variance of expr. This is an extension to\nstandard SQL. The standard SQL function VAR_POP() can be used instead.\n\nVariance is calculated by\n\n* working out the mean for the set\n* for each number, subtracting the mean and squaring the result\n* calculate the average of the resulting differences\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVARIANCE() can be used as a window function.\n\nVARIANCE() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE v(i tinyint);\n\nINSERT INTO v VALUES(101),(99);\n\nSELECT VARIANCE(i) FROM v;\n+-------------+\n| VARIANCE(i) |\n+-------------+\n| 1.0000 |\n+-------------+\n\nINSERT INTO v VALUES(120),(80);\n\nSELECT VARIANCE(i) FROM v;\n+-------------+\n| VARIANCE(i) |\n+-------------+\n| 200.5000 |\n+-------------+\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n ...
+[VAR_POP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard variance of expr. It considers rows as the\nwhole population, not as a sample, so it has the number of rows as the\ndenominator. You can also use VARIANCE(), which is equivalent but is not\nstandard SQL.\n\nVariance is calculated by\n\n* working out the mean for the set\n* for each number, subtracting the mean and squaring the result\n* calculate the average of the resulting differences\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVAR_POP() can be used as a window function.\n\nVAR_POP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nCREATE TABLE v(i tinyint);\n\nINSERT INTO v VALUES(101),(99);\n\nSELECT VAR_POP(i) FROM v;\n+------------+\n| VAR_POP(i) |\n+------------+\n| 1.0000 |\n+------------+\n\nINSERT INTO v VALUES(120),(80);\n\nSELECT VAR_POP(i) FROM v;\n+------------+\n| VAR_POP(i) |\n+------------+\n| 200.5000 |\n+------------+\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n ...
+[VAR_SAMP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sample variance of expr. That is, the denominator is the number of\nrows minus one.\n\nIt is an aggregate function, and so can be used with the GROUP BY clause.\n\nVAR_SAMP() can be used as a window function.\n\nVAR_SAMP() returns NULL if there were no matching rows.\n\nExamples\n--------\n\nAs an aggregate function:\n\nCREATE OR REPLACE TABLE stats (category VARCHAR(2), x INT);\n\nINSERT INTO stats VALUES \n ('a',1),('a',2),('a',3),\n ('b',11),('b',12),('b',20),('b',30),('b',60);\n\nSELECT category, STDDEV_POP(x), STDDEV_SAMP(x), VAR_POP(x) \n FROM stats GROUP BY category;\n+----------+---------------+----------------+------------+\n| category | STDDEV_POP(x) | STDDEV_SAMP(x) | VAR_POP(x) |\n+----------+---------------+----------------+------------+\n| a | 0.8165 | 1.0000 | 0.6667 |\n| b | 18.0400 | 20.1693 | 325.4400 |\n+----------+---------------+----------------+------------+\n\nAs a window function:\n\nCREATE OR REPLACE TABLE student_test (name CHAR(10), test CHAR(10), score\nTINYINT);\n\nINSERT INTO student_test VALUES \n ('Chun', 'SQL', 75), ('Chun', 'Tuning', 73),\n ('Esben', 'SQL', 43), ('Esben', 'Tuning', 31),\n ('Kaolin', 'SQL', 56), ('Kaolin', 'Tuning', 88),\n ('Tatiana', 'SQL', 87);\n\nSELECT name, test, score, VAR_SAMP(score) \n OVER (PARTITION BY test) AS variance_results FROM student_test;\n+---------+--------+-------+------------------+\n| name | test | score | variance_results |\n+---------+--------+-------+------------------+\n| Chun | SQL | 75 | 382.9167 |\n| Chun | Tuning | 73 | 873.0000 |\n| Esben | SQL | 43 | 382.9167 |\n| Esben | Tuning | 31 | 873.0000 |\n| Kaolin | SQL | 56 | 382.9167 |\n ...
+[VERSION]
+declaration=
+category=Information Functions
+description=Returns a string that indicates the MariaDB server version. The string uses\nthe utf8 character set.\n\nExamples\n--------\n\nSELECT VERSION();\n+----------------+\n| VERSION() |\n+----------------+\n| 10.4.7-MariaDB |\n+----------------+\n\nThe VERSION() string may have one or more of the following suffixes:\n\n+---------------------------+------------------------------------------------+\n| Suffix | Description |\n+---------------------------+------------------------------------------------+\n| -embedded | The server is an embedded server |\n| | (libmariadbd). |\n+---------------------------+------------------------------------------------+\n| -log | General logging, slow logging or binary |\n| | (replication) logging is enabled. |\n+---------------------------+------------------------------------------------+\n| -debug | The server is compiled for debugging. |\n+---------------------------+------------------------------------------------+\n| -valgrind | The server is compiled to be instrumented |\n| | with valgrind. |\n+---------------------------+------------------------------------------------+\n\nChanging the Version String\n---------------------------\n\nSome old legacy code may break because they are parsing the VERSION string and\nexpecting a MySQL string or a simple version string like Joomla til API17, see\nMDEV-7780.\n\nOne can fool these applications by setting the version string from the command\nline or the my.cnf files with --version=....\n\nURL: https://mariadb.com/kb/en/version/
+[WEEK]
+declaration=date[,mode]
+category=Date and Time Functions
+description=This function returns the week number for date. The two-argument form of\nWEEK() allows you to specify whether the week starts on Sunday or Monday and\nwhether the return value should be in the range from 0 to 53 or from 1 to 53.\nIf the mode argument is omitted, the value of the default_week_format system\nvariable is used.\n\nModes\n-----\n\n+-------+---------------------+--------+------------------------------------+\n| Mode | 1st day of week | Range | Week 1 is the 1st week with |\n+-------+---------------------+--------+------------------------------------+\n| 0 | Sunday | 0-53 | a Sunday in this year |\n+-------+---------------------+--------+------------------------------------+\n| 1 | Monday | 0-53 | more than 3 days this year |\n+-------+---------------------+--------+------------------------------------+\n| 2 | Sunday | 1-53 | a Sunday in this year |\n+-------+---------------------+--------+------------------------------------+\n| 3 | Monday | 1-53 | more than 3 days this year |\n+-------+---------------------+--------+------------------------------------+\n| 4 | Sunday | 0-53 | more than 3 days this year |\n+-------+---------------------+--------+------------------------------------+\n| 5 | Monday | 0-53 | a Monday in this year |\n+-------+---------------------+--------+------------------------------------+\n| 6 | Sunday | 1-53 | more than 3 days this year |\n+-------+---------------------+--------+------------------------------------+\n| 7 | Monday | 1-53 | a Monday in this year |\n+-------+---------------------+--------+------------------------------------+\n\nWith the mode value of 3, which means 'more than 3 days this year', weeks are\nnumbered according to ISO 8601:1988.\n\nExamples\n--------\n\nSELECT WEEK('2008-02-20');\n+--------------------+\n| WEEK('2008-02-20') |\n+--------------------+\n| 7 |\n+--------------------+\n\nSELECT WEEK('2008-02-20',0);\n+----------------------+\n| WEEK('2008-02-20',0) |\n+----------------------+\n| 7 |\n+----------------------+\n\nSELECT WEEK('2008-02-20',1);\n ...
+[WEEKDAY]
+declaration=date
+category=Date and Time Functions
+description=Returns the weekday index for date (0 = Monday, 1 = Tuesday, ... 6 = Sunday).\n\nThis contrasts with DAYOFWEEK() which follows the ODBC standard (1 = Sunday, 2\n= Monday, ..., 7 = Saturday).\n\nExamples\n--------\n\nSELECT WEEKDAY('2008-02-03 22:23:00');\n+--------------------------------+\n| WEEKDAY('2008-02-03 22:23:00') |\n+--------------------------------+\n| 6 |\n+--------------------------------+\n\nSELECT WEEKDAY('2007-11-06');\n+-----------------------+\n| WEEKDAY('2007-11-06') |\n+-----------------------+\n| 1 |\n+-----------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT d FROM t1 where WEEKDAY(d) = 6;\n+---------------------+\n| d |\n+---------------------+\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n+---------------------+\n\nURL: https://mariadb.com/kb/en/weekday/
+[WEEKOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the calendar week of the date as a number in the range from 1 to 53.\nWEEKOFYEAR() is a compatibility function that is equivalent to WEEK(date,3).\n\nExamples\n--------\n\nSELECT WEEKOFYEAR('2008-02-20');\n+--------------------------+\n| WEEKOFYEAR('2008-02-20') |\n+--------------------------+\n| 8 |\n+--------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nselect * from t1;\n+---------------------+\n| d |\n+---------------------+\n| 2007-01-30 21:31:07 |\n| 1983-10-15 06:42:51 |\n| 2011-04-21 12:34:56 |\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n| 2004-10-07 11:19:34 |\n+---------------------+\n\nSELECT d, WEEKOFYEAR(d), WEEK(d,3) from t1;\n+---------------------+---------------+-----------+\n| d | WEEKOFYEAR(d) | WEEK(d,3) |\n+---------------------+---------------+-----------+\n| 2007-01-30 21:31:07 | 5 | 5 |\n| 1983-10-15 06:42:51 | 41 | 41 |\n| 2011-04-21 12:34:56 | 16 | 16 |\n| 2011-10-30 06:31:41 | 43 | 43 |\n| 2011-01-30 14:03:25 | 4 | 4 |\n| 2004-10-07 11:19:34 | 41 | 41 |\n+---------------------+---------------+-----------+\n\nURL: https://mariadb.com/kb/en/weekofyear/
+[WEIGHT_STRING]
+declaration=str [AS {CHAR|BINARY}(N
+category=String Functions
+description=Returns a binary string representing the string's sorting and comparison\nvalue. A string with a lower result means that for sorting purposes the string\nappears before a string with a higher result.\n\nWEIGHT_STRING() is particularly useful when adding new collations, for testing\npurposes.\n\nIf str is a non-binary string (CHAR, VARCHAR or TEXT), WEIGHT_STRING returns\nthe string's collation weight. If str is a binary string (BINARY, VARBINARY or\nBLOB), the return value is simply the input value, since the weight for each\nbyte in a binary string is the byte value.\n\nWEIGHT_STRING() returns NULL if given a NULL input.\n\nThe optional AS clause permits casting the input string to a binary or\nnon-binary string, as well as to a particular length.\n\nAS BINARY(N) measures the length in bytes rather than characters, and right\npads with 0x00 bytes to the desired length.\n\nAS CHAR(N) measures the length in characters, and right pads with spaces to\nthe desired length.\n\nN has a minimum value of 1, and if it is less than the length of the input\nstring, the string is truncated without warning.\n\nThe optional LEVEL clause specifies that the return value should contain\nweights for specific collation levels. The levels specifier can either be a\nsingle integer, a comma-separated list of integers, or a range of integers\nseparated by a dash (whitespace is ignored). Integers can range from 1 to a\nmaximum of 6, dependent on the collation, and need to be listed in ascending\norder.\n\nIf the LEVEL clause is no provided, a default of 1 to the maximum for the\ncollation is assumed.\n\nIf the LEVEL is specified without using a range, an optional modifier is\npermitted.\n\nASC, the default, returns the weights without any modification.\n\nDESC returns bitwise-inverted weights.\n\nREVERSE returns the weights in reverse order.\n\nExamples\n--------\n\nThe examples below use the HEX() function to represent non-printable results\nin hexadecimal format.\n ...
+[WITHIN]
+declaration=g1,g2
+category=Geometry Relations
+description=Returns 1 or 0 to indicate whether g1 is spatially within g2. This tests the\nopposite relationship as Contains().\n\nWITHIN() is based on the original MySQL implementation, and uses object\nbounding rectangles, while ST_WITHIN() uses object shapes.\n\nExamples\n--------\n\nSET @g1 = GEOMFROMTEXT('POINT(174 149)');\nSET @g2 = GEOMFROMTEXT('POINT(176 151)');\nSET @g3 = GEOMFROMTEXT('POLYGON((175 150, 20 40, 50 60, 125 100, 175 150))');\n\nSELECT within(@g1,@g3);\n+-----------------+\n| within(@g1,@g3) |\n+-----------------+\n| 1 |\n+-----------------+\n\nSELECT within(@g2,@g3);\n+-----------------+\n| within(@g2,@g3) |\n+-----------------+\n| 0 |\n+-----------------+\n\nURL: https://mariadb.com/kb/en/within/
+[WSREP_LAST_SEEN_GTID]
+declaration=
+category=Galera Functions
+description=Returns the Global Transaction ID of the most recent write transaction\nobserved by the client.\n\nThe result can be useful to determine the transaction to provide to\nWSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking purposes.\n\nURL: https://mariadb.com/kb/en/wsrep_last_seen_gtid/
+[WSREP_LAST_WRITTEN_GTID]
+declaration=
+category=Galera Functions
+description=Returns the Global Transaction ID of the most recent write transaction\nperformed by the client.\n\nURL: https://mariadb.com/kb/en/wsrep_last_written_gtid/
+[WSREP_SYNC_WAIT_UPTO_GTID]
+declaration=gtid[,timeout]
+category=Galera Functions
+description=Blocks the client until the transaction specified by the given Global\nTransaction ID is applied and committed by the node.\n\nThe optional timeout argument can be used to specify a block timeout in\nseconds. If not provided, the timeout will be indefinite.\n\nReturns the node that applied and committed the Global Transaction ID,\nER_LOCAL_WAIT_TIMEOUT if the function is timed out before this, or\nER_WRONG_ARGUMENTS if the function is given an invalid GTID.\n\nThe result from WSREP_LAST_SEEN_GTID can be useful to determine the\ntransaction to provide to WSREP_SYNC_WAIT_UPTO_GTID for waiting and unblocking\npurposes.\n\nURL: https://mariadb.com/kb/en/wsrep_sync_wait_upto_gtid/
+[YEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the year for the given date, in the range 1000 to 9999, or 0 for the\n"zero" date.\n\nExamples\n--------\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT * FROM t1;\n+---------------------+\n| d |\n+---------------------+\n| 2007-01-30 21:31:07 |\n| 1983-10-15 06:42:51 |\n| 2011-04-21 12:34:56 |\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n| 2004-10-07 11:19:34 |\n+---------------------+\n\nSELECT * FROM t1 WHERE YEAR(d) = 2011;\n+---------------------+\n| d |\n+---------------------+\n| 2011-04-21 12:34:56 |\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n+---------------------+\n\nSELECT YEAR('1987-01-01');\n+--------------------+\n| YEAR('1987-01-01') |\n+--------------------+\n| 1987 |\n+--------------------+\n\nURL: https://mariadb.com/kb/en/year/
+[YEARWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns year and week for a date. The mode argument works exactly like the\nmode argument to WEEK(). The year in the result may be different from the year\nin the date argument for the first and the last week of the year.\n\nExamples\n--------\n\nSELECT YEARWEEK('1987-01-01');\n+------------------------+\n| YEARWEEK('1987-01-01') |\n+------------------------+\n| 198652 |\n+------------------------+\n\nCREATE TABLE t1 (d DATETIME);\nINSERT INTO t1 VALUES\n ("2007-01-30 21:31:07"),\n ("1983-10-15 06:42:51"),\n ("2011-04-21 12:34:56"),\n ("2011-10-30 06:31:41"),\n ("2011-01-30 14:03:25"),\n ("2004-10-07 11:19:34");\n\nSELECT * FROM t1;\n+---------------------+\n| d |\n+---------------------+\n| 2007-01-30 21:31:07 |\n| 1983-10-15 06:42:51 |\n| 2011-04-21 12:34:56 |\n| 2011-10-30 06:31:41 |\n| 2011-01-30 14:03:25 |\n| 2004-10-07 11:19:34 |\n+---------------------+\n6 rows in set (0.02 sec)\n\nSELECT YEARWEEK(d) FROM t1 WHERE YEAR(d) = 2011;\n+-------------+\n| YEARWEEK(d) |\n+-------------+\n| 201116 |\n| 201144 |\n| 201105 |\n+-------------+\n3 rows in set (0.03 sec)\n\nURL: https://mariadb.com/kb/en/yearweek/
\ No newline at end of file
diff --git a/out/functions-mysql.ini b/out/functions-mysql.ini
new file mode 100644
index 00000000..9a47ab28
--- /dev/null
+++ b/out/functions-mysql.ini
@@ -0,0 +1,1340 @@
+[ABS]
+declaration=X
+category=Numeric Functions
+description=Returns the absolute value of X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[ACOS]
+declaration=X
+category=Numeric Functions
+description=Returns the arc cosine of X, that is, the value whose cosine is X.\nReturns NULL if X is not in the range -1 to 1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[ADDDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, ADDDATE()\nis a synonym for DATE_ADD(). The related function SUBDATE() is a\nsynonym for DATE_SUB(). For information on the INTERVAL unit argument,\nsee the discussion for DATE_ADD().\n\nmysql> SELECT DATE_ADD('2008-01-02', INTERVAL 31 DAY);\n -> '2008-02-02'\nmysql> SELECT ADDDATE('2008-01-02', INTERVAL 31 DAY);\n -> '2008-02-02'\n\nWhen invoked with the days form of the second argument, MySQL treats it\nas an integer number of days to be added to expr.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[ADDTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=ADDTIME() adds expr2 to expr1 and returns the result. expr1 is a time\nor datetime expression, and expr2 is a time expression.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[AES_DECRYPT]
+declaration=crypt_str,key_str[,init_vector]
+category=Encryption Functions
+description=This function decrypts data using the official AES (Advanced Encryption\nStandard) algorithm. For more information, see the description of\nAES_ENCRYPT().\n\nThe optional initialization vector argument, init_vector, is available\nas of MySQL 5.7.4. As of that version, statements that use\nAES_DECRYPT() are unsafe for statement-based replication and cannot be\nstored in the query cache.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[AES_ENCRYPT]
+declaration=str,key_str[,init_vector]
+category=Encryption Functions
+description=AES_ENCRYPT() and AES_DECRYPT() implement encryption and decryption of\ndata using the official AES (Advanced Encryption Standard) algorithm,\npreviously known as "Rijndael." The AES standard permits various key\nlengths. By default these functions implement AES with a 128-bit key\nlength. As of MySQL 5.7.4, key lengths of 196 or 256 bits can be used,\nas described later. The key length is a trade off between performance\nand security.\n\nAES_ENCRYPT() encrypts the string str using the key string key_str and\nreturns a binary string containing the encrypted output. AES_DECRYPT()\ndecrypts the encrypted string crypt_str using the key string key_str\nand returns the original plaintext string. If either function argument\nis NULL, the function returns NULL.\n\nThe str and crypt_str arguments can be any length, and padding is\nautomatically added to str so it is a multiple of a block as required\nby block-based algorithms such as AES. This padding is automatically\nremoved by the AES_DECRYPT() function. The length of crypt_str can be\ncalculated using this formula:\n\n16 * (trunc(string_length / 16) + 1)\n\nFor a key length of 128 bits, the most secure way to pass a key to the\nkey_str argument is to create a truly random 128-bit value and pass it\nas a binary value. For example:\n\nINSERT INTO t\nVALUES (1,AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));\n\nA passphrase can be used to generate an AES key by hashing the\npassphrase. For example:\n\nINSERT INTO t VALUES (1,AES_ENCRYPT('text', SHA2('My secret passphrase',512)));\n\nDo not pass a password or passphrase directly to crypt_str, hash it\nfirst. Previous versions of this documentation suggested the former\napproach, but it is no longer recommended as the examples shown here\nare more secure.\n\nIf AES_DECRYPT() detects invalid data or incorrect padding, it returns\nNULL. However, it is possible for AES_DECRYPT() to return a non-NULL\nvalue (possibly garbage) if the input data or the key is invalid.\n\nAs of MySQL 5.7.4, AES_ENCRYPT() and AES_DECRYPT() permit control of\nthe block encryption mode and take an optional init_vector\ninitialization vector argument:\n\no The block_encryption_mode system variable controls the mode for\n block-based encryption algorithms. Its default value is aes-128-ecb,\n which signifies encryption using a key length of 128 bits and ECB\n ...
+[ANY_VALUE]
+declaration=arg
+category=Miscellaneous Functions
+description=This function is useful for GROUP BY queries when the\nONLY_FULL_GROUP_BY SQL mode is enabled, for cases when MySQL rejects a\nquery that you know is valid for reasons that MySQL cannot determine.\nThe function return value and type are the same as the return value and\ntype of its argument, but the function result is not checked for the\nONLY_FULL_GROUP_BY SQL mode.\n\nFor example, if name is a nonindexed column, the following query fails\nwith ONLY_FULL_GROUP_BY enabled:\n\nmysql> SELECT name, address, MAX(age) FROM t GROUP BY name;\nERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP\nBY clause and contains nonaggregated column 'mydb.t.address' which\nis not functionally dependent on columns in GROUP BY clause; this\nis incompatible with sql_mode=only_full_group_by\n\nThe failure occurs because address is a nonaggregated column that is\nneither named among GROUP BY columns nor functionally dependent on\nthem. As a result, the address value for rows within each name group is\nnondeterministic. There are multiple ways to cause MySQL to accept the\nquery:\n\no Alter the table to make name a primary key or a unique NOT NULL\n column. This enables MySQL to determine that address is functionally\n dependent on name; that is, address is uniquely determined by name.\n (This technique is inapplicable if NULL must be permitted as a valid\n name value.)\n\no Use ANY_VALUE() to refer to address:\n\nSELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;\n\n In this case, MySQL ignores the nondeterminism of address values\n within each name group and accepts the query. This may be useful if\n you simply do not care which value of a nonaggregated column is\n chosen for each group. ANY_VALUE() is not an aggregate function,\n unlike functions such as SUM() or COUNT(). It simply acts to suppress\n the test for nondeterminism.\n\no Disable ONLY_FULL_GROUP_BY. This is equivalent to using ANY_VALUE()\n with ONLY_FULL_GROUP_BY enabled, as described in the previous item.\n\nANY_VALUE() is also useful if functional dependence exists between\ncolumns but MySQL cannot determine it. The following query is valid\nbecause age is functionally dependent on the grouping column age-1, but\nMySQL cannot tell that and rejects the query with ONLY_FULL_GROUP_BY\nenabled:\n\nSELECT age FROM t GROUP BY age-1;\n\n ...
+[AREA]
+declaration=poly
+category=Polygon properties
+description=ST_Area() and Area() are synonyms. For more information, see the\ndescription of ST_Area().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-polygon-property-functions.html
+[ASBINARY]
+declaration=g
+category=WKB
+description=Converts a value in internal geometry format to its WKB representation\nand returns the binary result.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-format-conversion-functions.html
+[ASCII]
+declaration=str
+category=String Functions
+description=Returns the numeric value of the leftmost character of the string str.\nReturns 0 if str is the empty string. Returns NULL if str is NULL.\nASCII() works for 8-bit characters.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[ASIN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc sine of X, that is, the value whose sine is X. Returns\nNULL if X is not in the range -1 to 1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[ASTEXT]
+declaration=g
+category=WKT
+description=Converts a value in internal geometry format to its WKT representation\nand returns the string result.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-format-conversion-functions.html
+[ATAN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc tangent of X, that is, the value whose tangent is X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[ATAN2]
+declaration=Y,X
+category=Numeric Functions
+description=Returns the arc tangent of the two variables X and Y. It is similar to\ncalculating the arc tangent of Y / X, except that the signs of both\narguments are used to determine the quadrant of the result.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[AVG]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the average value of expr. The DISTINCT option can be used to\nreturn the average of the distinct values of expr.\n\nAVG() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[BENCHMARK]
+declaration=count,expr
+category=Information Functions
+description=The BENCHMARK() function executes the expression expr repeatedly count\ntimes. It may be used to time how quickly MySQL processes the\nexpression. The result value is always 0. The intended use is from\nwithin the mysql client, which reports query execution times:\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[BIGINT]
+declaration=M
+category=Data Types
+description=A large integer. The signed range is -9223372036854775808 to\n9223372036854775807. The unsigned range is 0 to 18446744073709551615.\n\nSERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[BIN]
+declaration=N
+category=String Functions
+description=Returns a string representation of the binary value of N, where N is a\nlonglong (BIGINT) number. This is equivalent to CONV(N,10,2). Returns\nNULL if N is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[BINARY]
+declaration=M
+category=Data Types
+description=The BINARY type is similar to the CHAR type, but stores binary byte\nstrings rather than nonbinary character strings. M represents the\ncolumn length in bytes.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[BIT]
+declaration=M
+category=Data Types
+description=A bit-field type. M indicates the number of bits per value, from 1 to\n64. The default is 1 if M is omitted.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[BIT_AND]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise AND of all bits in expr. The calculation is\nperformed with 64-bit (BIGINT) precision.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[BIT_COUNT]
+declaration=N
+category=Bit Functions
+description=Returns the number of bits that are set in the argument N.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/bit-functions.html
+[BIT_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str in bits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[BIT_OR]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise OR of all bits in expr. The calculation is\nperformed with 64-bit (BIGINT) precision.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[BIT_XOR]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the bitwise XOR of all bits in expr. The calculation is\nperformed with 64-bit (BIGINT) precision.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[BLOB]
+declaration=M
+category=Data Types
+description=A BLOB column with a maximum length of 65,535 (216 - 1) bytes. Each\nBLOB value is stored using a 2-byte length prefix that indicates the\nnumber of bytes in the value.\n\nAn optional length M can be given for this type. If this is done, MySQL\ncreates the column as the smallest BLOB type large enough to hold\nvalues M bytes long.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[BUFFER]
+declaration=g,d
+category=GeometryCollection properties
+description=Returns a geometry that represents all points whose distance from the\ngeometry value g is less than or equal to a distance of d.\n\nBuffer() supports negative distances for polygons, multipolygons, and\ngeometry collections containing polygons or multipolygons. For point,\nmultipoint, linestring, multilinestring, and geometry collections not\ncontaining any polygons or multipolygons, Buffer() with a negative\ndistance returns NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[CAST]
+declaration=expr AS type
+category=String Functions
+description=The CAST() function takes an expression of any type and produces a\nresult value of a specified type, similar to CONVERT(). See the\ndescription of CONVERT() for more information.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/cast-functions.html
+[CEIL]
+declaration=X
+category=Numeric Functions
+description=CEIL() is a synonym for CEILING().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[CEILING]
+declaration=X
+category=Numeric Functions
+description=Returns the smallest integer value not less than X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[CENTROID]
+declaration=mpoly
+category=Polygon properties
+description=ST_Centroid() and Centroid() are synonyms. For more information, see\nthe description of ST_Centroid().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-multipolygon-property-functions.html
+[CHAR]
+declaration=M
+category=Data Types
+description=collation_name]\n\nA fixed-length string that is always right-padded with spaces to the\nspecified length when stored. M represents the column length in\ncharacters. The range of M is 0 to 255. If M is omitted, the length is\n1.\n\n*Note*: Trailing spaces are removed when CHAR values are retrieved\nunless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[CHARACTER_LENGTH]
+declaration=str
+category=String Functions
+description=CHARACTER_LENGTH() is a synonym for CHAR_LENGTH().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[CHARSET]
+declaration=str
+category=Information Functions
+description=Returns the character set of the string argument.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[CHAR_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str, measured in characters. A\nmultibyte character counts as a single character. This means that for a\nstring containing five 2-byte characters, LENGTH() returns 10, whereas\nCHAR_LENGTH() returns 5.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[COALESCE]
+declaration=value,...
+category=Comparison operators
+description=Returns the first non-NULL value in the list, or NULL if there are no\nnon-NULL values.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[COERCIBILITY]
+declaration=str
+category=Information Functions
+description=Returns the collation coercibility value of the string argument.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[COLLATION]
+declaration=str
+category=Information Functions
+description=Returns the collation of the string argument.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[COMPRESS]
+declaration=string_to_compress
+category=Encryption Functions
+description=Compresses a string and returns the result as a binary string. This\nfunction requires MySQL to have been compiled with a compression\nlibrary such as zlib. Otherwise, the return value is always NULL. The\ncompressed string can be uncompressed with UNCOMPRESS().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[CONCAT]
+declaration=str1,str2,...
+category=String Functions
+description=Returns the string that results from concatenating the arguments. May\nhave one or more arguments. If all arguments are nonbinary strings, the\nresult is a nonbinary string. If the arguments include any binary\nstrings, the result is a binary string. A numeric argument is converted\nto its equivalent nonbinary string form.\n\nCONCAT() returns NULL if any argument is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[CONCAT_WS]
+declaration=separator,str1,str2,...
+category=String Functions
+description=CONCAT_WS() stands for Concatenate With Separator and is a special form\nof CONCAT(). The first argument is the separator for the rest of the\narguments. The separator is added between the strings to be\nconcatenated. The separator can be a string, as can the rest of the\narguments. If the separator is NULL, the result is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[CONNECTION_ID]
+declaration=
+category=Information Functions
+description=Returns the connection ID (thread ID) for the connection. Every\nconnection has an ID that is unique among the set of currently\nconnected clients.\n\nThe value returned by CONNECTION_ID() is the same type of value as\ndisplayed in the ID column of the INFORMATION_SCHEMA.PROCESSLIST table,\nthe Id column of SHOW PROCESSLIST output, and the PROCESSLIST_ID column\nof the Performance Schema threads table.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[CONTAINS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 completely contains g2. This\ntests the opposite relationship as Within().\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBRContains() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[CONV]
+declaration=N,from_base,to_base
+category=Numeric Functions
+description=Converts numbers between different number bases. Returns a string\nrepresentation of the number N, converted from base from_base to base\nto_base. Returns NULL if any argument is NULL. The argument N is\ninterpreted as an integer, but may be specified as an integer or a\nstring. The minimum base is 2 and the maximum base is 36. If to_base is\na negative number, N is regarded as a signed number. Otherwise, N is\ntreated as unsigned. CONV() works with 64-bit precision.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[CONVERT]
+declaration=expr,type
+category=String Functions
+description=The CONVERT() and CAST() functions take an expression of any type and\nproduce a result value of a specified type.\n\nThe type for the result can be one of the following values:\n\no BINARY[(N)]\n\no CHAR[(N)]\n\no DATE\n\no DATETIME\n\no DECIMAL[(M[,D])]\n\no SIGNED [INTEGER]\n\no TIME\n\no UNSIGNED [INTEGER]\n\nBINARY produces a string with the BINARY data type. See\nhttp://dev.mysql.com/doc/refman/5.7/en/binary-varbinary.html for a\ndescription of how this affects comparisons. If the optional length N\nis given, BINARY(N) causes the cast to use no more than N bytes of the\nargument. Values shorter than N bytes are padded with 0x00 bytes to a\nlength of N.\n\nCHAR(N) causes the cast to use no more than N characters of the\nargument.\n\nCAST() and CONVERT(... USING ...) are standard SQL syntax. The\nnon-USING form of CONVERT() is ODBC syntax.\n\nCONVERT() with USING is used to convert data between different\ncharacter sets. In MySQL, transcoding names are the same as the\ncorresponding character set names. For example, this statement converts\nthe string 'abc' in the default character set to the corresponding\nstring in the utf8 character set:\n\nSELECT CONVERT('abc' USING utf8);\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/cast-functions.html
+[CONVERT_TZ]
+declaration=dt,from_tz,to_tz
+category=Date and Time Functions
+description=CONVERT_TZ() converts a datetime value dt from the time zone given by\nfrom_tz to the time zone given by to_tz and returns the resulting\nvalue. Time zones are specified as described in\nhttp://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html. This\nfunction returns NULL if the arguments are invalid.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[CONVEXHULL]
+declaration=g
+category=GeometryCollection properties
+description=ST_ConvexHull() and ConvexHull() are synonyms. For more information,\nsee the description of ST_ConvexHull().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[COS]
+declaration=X
+category=Numeric Functions
+description=Returns the cosine of X, where X is given in radians.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[COT]
+declaration=X
+category=Numeric Functions
+description=Returns the cotangent of X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[COUNT]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns a count of the number of non-NULL values of expr in the rows\nretrieved by a SELECT statement. The result is a BIGINT value.\n\nCOUNT() returns 0 if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[CRC32]
+declaration=expr
+category=Numeric Functions
+description=Computes a cyclic redundancy check value and returns a 32-bit unsigned\nvalue. The result is NULL if the argument is NULL. The argument is\nexpected to be a string and (if possible) is treated as one if it is\nnot.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[CROSSES]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 if g1 spatially crosses g2. Returns NULL if g1 is a Polygon\nor a MultiPolygon, or if g2 is a Point or a MultiPoint. Otherwise,\nreturns 0.\n\nThe term spatially crosses denotes a spatial relation between two given\ngeometries that has the following properties:\n\no The two geometries intersect\n\no Their intersection results in a geometry that has a dimension that is\n one less than the maximum dimension of the two given geometries\n\no Their intersection is not equal to either of the two given geometries\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[CURDATE]
+declaration=
+category=Date and Time Functions
+description=Returns the current date as a value in 'YYYY-MM-DD' or YYYYMMDD format,\ndepending on whether the function is used in a string or numeric\ncontext.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[CURRENT_DATE]
+declaration=
+category=Date and Time Functions
+description=CURRENT_DATE and CURRENT_DATE() are synonyms for CURDATE().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[CURRENT_TIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=CURRENT_TIME and CURRENT_TIME() are synonyms for CURTIME().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[CURRENT_TIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=CURRENT_TIMESTAMP and CURRENT_TIMESTAMP() are synonyms for NOW().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[CURRENT_USER]
+declaration=
+category=Information Functions
+description=Returns the user name and host name combination for the MySQL account\nthat the server used to authenticate the current client. This account\ndetermines your access privileges. The return value is a string in the\nutf8 character set.\n\nThe value of CURRENT_USER() can differ from the value of USER().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[CURTIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current time as a value in 'HH:MM:SS' or HHMMSS format,\ndepending on whether the function is used in a string or numeric\ncontext. The value is expressed in the current time zone.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DATABASE]
+declaration=
+category=Information Functions
+description=Returns the default (current) database name as a string in the utf8\ncharacter set. If there is no default database, DATABASE() returns\nNULL. Within a stored routine, the default database is the database\nthat the routine is associated with, which is not necessarily the same\nas the database that is the default in the calling context.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[DATEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=DATEDIFF() returns expr1 - expr2 expressed as a value in days from one\ndate to the other. expr1 and expr2 are date or date-and-time\nexpressions. Only the date parts of the values are used in the\ncalculation.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DATETIME]
+declaration=fsp
+category=Data Types
+description=A date and time combination. The supported range is '1000-01-01\n00:00:00.000000' to '9999-12-31 23:59:59.999999'. MySQL displays\nDATETIME values in 'YYYY-MM-DD HH:MM:SS[.fraction]' format, but permits\nassignment of values to DATETIME columns using either strings or\nnumbers.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nAutomatic initialization and updating to the current date and time for\nDATETIME columns can be specified using DEFAULT and ON UPDATE column\ndefinition clauses, as described in\nhttp://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-overview.html
+[DATE_ADD]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=These functions perform date arithmetic. The date argument specifies\nthe starting date or datetime value. expr is an expression specifying\nthe interval value to be added or subtracted from the starting date.\nexpr is a string; it may start with a "-" for negative intervals. unit\nis a keyword indicating the units in which the expression should be\ninterpreted.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DATE_FORMAT]
+declaration=date,format
+category=Date and Time Functions
+description=Formats the date value according to the format string.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DATE_SUB]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=See the description for DATE_ADD().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DAY]
+declaration=date
+category=Date and Time Functions
+description=DAY() is a synonym for DAYOFMONTH().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DAYNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the name of the weekday for date. The language used for the\nname is controlled by the value of the lc_time_names system variable\n(http://dev.mysql.com/doc/refman/5.7/en/locale-support.html).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DAYOFMONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the month for date, in the range 1 to 31, or 0 for\ndates such as '0000-00-00' or '2008-00-00' that have a zero day part.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DAYOFWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns the weekday index for date (1 = Sunday, 2 = Monday, ..., 7 =\nSaturday). These index values correspond to the ODBC standard.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DAYOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the year for date, in the range 1 to 366.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[DEC]
+declaration=M[,D]
+category=Data Types
+description=[ZEROFILL], FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]\n\nThese types are synonyms for DECIMAL. The FIXED synonym is available\nfor compatibility with other database systems.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[DECIMAL]
+declaration=M[,D]
+category=Data Types
+description=A packed "exact" fixed-point number. M is the total number of digits\n(the precision) and D is the number of digits after the decimal point\n(the scale). The decimal point and (for negative numbers) the "-" sign\nare not counted in M. If D is 0, values have no decimal point or\nfractional part. The maximum number of digits (M) for DECIMAL is 65.\nThe maximum number of supported decimals (D) is 30. If D is omitted,\nthe default is 0. If M is omitted, the default is 10.\n\nUNSIGNED, if specified, disallows negative values.\n\nAll basic calculations (+, -, *, /) with DECIMAL columns are done with\na precision of 65 digits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[DECODE]
+declaration=crypt_str,pass_str
+category=Encryption Functions
+description=DECODE() decrypts the encrypted string crypt_str using pass_str as the\npassword. crypt_str should be a string returned from ENCODE().\n\n*Note*: The ENCODE() and DECODE() functions are deprecated in MySQL\n5.7, will be removed in a future MySQL release, and should no longer be\nused. Consider using AES_ENCRYPT() and AES_DECRYPT() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[DEFAULT]
+declaration=col_name
+category=Miscellaneous Functions
+description=Returns the default value for a table column. An error results if the\ncolumn has no default value.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[DEGREES]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from radians to degrees.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[DES_DECRYPT]
+declaration=crypt_str[,key_str]
+category=Encryption Functions
+description=Decrypts a string encrypted with DES_ENCRYPT(). If an error occurs,\nthis function returns NULL.\n\nThis function works only if MySQL has been configured with SSL support.\nSee http://dev.mysql.com/doc/refman/5.7/en/ssl-connections.html.\n\nIf no key_str argument is given, DES_DECRYPT() examines the first byte\nof the encrypted string to determine the DES key number that was used\nto encrypt the original string, and then reads the key from the DES key\nfile to decrypt the message. For this to work, the user must have the\nSUPER privilege. The key file can be specified with the --des-key-file\nserver option.\n\nIf you pass this function a key_str argument, that string is used as\nthe key for decrypting the message.\n\nIf the crypt_str argument does not appear to be an encrypted string,\nMySQL returns the given crypt_str.\n\n*Note*: The DES_ENCRYPT() and DES_DECRYPT() functions are deprecated as\nof MySQL 5.7.6, will be removed in a future MySQL release, and should\nno longer be used. Consider using AES_ENCRYPT() and AES_DECRYPT()\ninstead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[DES_ENCRYPT]
+declaration=str[,{key_num|key_str}]
+category=Encryption Functions
+description=Encrypts the string with the given key using the Triple-DES algorithm.\n\nThis function works only if MySQL has been configured with SSL support.\nSee http://dev.mysql.com/doc/refman/5.7/en/ssl-connections.html.\n\nThe encryption key to use is chosen based on the second argument to\nDES_ENCRYPT(), if one was given. With no argument, the first key from\nthe DES key file is used. With a key_num argument, the given key number\n(0 to 9) from the DES key file is used. With a key_str argument, the\ngiven key string is used to encrypt str.\n\nThe key file can be specified with the --des-key-file server option.\n\nThe return string is a binary string where the first character is\nCHAR(128 | key_num). If an error occurs, DES_ENCRYPT() returns NULL.\n\nThe 128 is added to make it easier to recognize an encrypted key. If\nyou use a string key, key_num is 127.\n\nThe string length for the result is given by this formula:\n\nnew_len = orig_len + (8 - (orig_len % 8)) + 1\n\nEach line in the DES key file has the following format:\n\nkey_num des_key_str\n\nEach key_num value must be a number in the range from 0 to 9. Lines in\nthe file may be in any order. des_key_str is the string that is used to\nencrypt the message. There should be at least one space between the\nnumber and the key. The first key is the default key that is used if\nyou do not specify any key argument to DES_ENCRYPT().\n\nYou can tell MySQL to read new key values from the key file with the\nFLUSH DES_KEY_FILE statement. This requires the RELOAD privilege.\n\nOne benefit of having a set of default keys is that it gives\napplications a way to check for the existence of encrypted column\nvalues, without giving the end user the right to decrypt those values.\n\n*Note*: The DES_ENCRYPT() and DES_DECRYPT() functions are deprecated as\nof MySQL 5.7.6, will be removed in a future MySQL release, and should\nno longer be used. Consider using AES_ENCRYPT() and AES_DECRYPT()\ninstead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[DIMENSION]
+declaration=g
+category=Geometry properties
+description=Returns the inherent dimension of the geometry value g. The result can\nbe -1, 0, 1, or 2. The meaning of these values is given in\nhttp://dev.mysql.com/doc/refman/5.7/en/gis-class-geometry.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[DISJOINT]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially disjoint from (does\nnot intersect) g2.\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBRDisjoint() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[DISTANCE]
+declaration=g1,g2
+category=Geometry relations
+description=ST_Distance() and Distance() are synonyms. For more information, see\nthe description of ST_Distance().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[DOUBLE]
+declaration=M,D
+category=Data Types
+description=A normal-size (double-precision) floating-point number. Permissible\nvalues are -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and\n2.2250738585072014E-308 to 1.7976931348623157E+308. These are the\ntheoretical limits, based on the IEEE standard. The actual range might\nbe slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following\nthe decimal point. If M and D are omitted, values are stored to the\nlimits permitted by the hardware. A double-precision floating-point\nnumber is accurate to approximately 15 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[ELT]
+declaration=N,str1,str2,str3,...
+category=String Functions
+description=ELT() returns the Nth element of the list of strings: str1 if N = 1,\nstr2 if N = 2, and so on. Returns NULL if N is less than 1 or greater\nthan the number of arguments. ELT() is the complement of FIELD().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[ENCODE]
+declaration=str,pass_str
+category=Encryption Functions
+description=ENCODE() encrypts str using pass_str as the password. The result is a\nbinary string of the same length as str. To decrypt the result, use\nDECODE().\n\n*Note*: The ENCODE() and DECODE() functions are deprecated in MySQL\n5.7, will be removed in a future MySQL release, and should no longer be\nused.\n\nIf you still need to use ENCODE(), a salt value must be used with it to\nreduce risk. For example:\n\nENCODE('plaintext', CONCAT('my_random_salt','my_secret_password'))\n\nA new random salt value must be used whenever a password is updated.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[ENCRYPT]
+declaration=str[,salt]
+category=Encryption Functions
+description=Encrypts str using the Unix crypt() system call and returns a binary\nstring. The salt argument must be a string with at least two characters\nor the result will be NULL. If no salt argument is given, a random\nvalue is used.\n\n*Note*: The ENCRYPT() function is deprecated as of MySQL 5.7.6, will be\nremoved in a future MySQL release, and should no longer be used.\nConsider using AES_ENCRYPT() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[ENDPOINT]
+declaration=ls
+category=LineString properties
+description=Returns the Point that is the endpoint of the LineString value ls.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[ENUM]
+declaration='value1','value2',...
+category=Data Types
+description=collation_name]\n\nAn enumeration. A string object that can have only one value, chosen\nfrom the list of values 'value1', 'value2', ..., NULL or the special ''\nerror value. ENUM values are represented internally as integers.\n\nAn ENUM column can have a maximum of 65,535 distinct elements. (The\npractical limit is less than 3000.) A table can have no more than 255\nunique element list definitions among its ENUM and SET columns\nconsidered as a group. For more information on these limits, see\nhttp://dev.mysql.com/doc/refman/5.7/en/limits-frm-file.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[ENVELOPE]
+declaration=g
+category=Geometry properties
+description=ST_Envelope() and Envelope() are synonyms. For more information, see\nthe description of ST_Envelope().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[EQUALS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially equal to g2.\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBREquals() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[EXP]
+declaration=X
+category=Numeric Functions
+description=Returns the value of e (the base of natural logarithms) raised to the\npower of X. The inverse of this function is LOG() (using a single\nargument only) or LN().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[EXPORT_SET]
+declaration=bits,on,off[,separator[,number_of_bits]]
+category=String Functions
+description=Returns a string such that for every bit set in the value bits, you get\nan on string and for every bit not set in the value, you get an off\nstring. Bits in bits are examined from right to left (from low-order to\nhigh-order bits). Strings are added to the result from left to right,\nseparated by the separator string (the default being the comma\ncharacter ","). The number of bits examined is given by number_of_bits,\nwhich has a default of 64 if not specified. number_of_bits is silently\nclipped to 64 if larger than 64. It is treated as an unsigned integer,\nso a value of -1 is effectively the same as 64.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[EXTERIORRING]
+declaration=poly
+category=Polygon properties
+description=Returns the exterior ring of the Polygon value poly as a LineString.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-polygon-property-functions.html
+[EXTRACT]
+declaration=unit FROM date
+category=Date and Time Functions
+description=The EXTRACT() function uses the same kinds of unit specifiers as\nDATE_ADD() or DATE_SUB(), but extracts parts from the date rather than\nperforming date arithmetic.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[EXTRACTVALUE]
+declaration=xml_frag, xpath_expr
+category=String Functions
+description=ExtractValue() takes two string arguments, a fragment of XML markup\nxml_frag and an XPath expression xpath_expr (also known as a locator);\nit returns the text (CDATA) of the first text node which is a child of\nthe elements or elements matched by the XPath expression.\n\nUsing this function is the equivalent of performing a match using the\nxpath_expr after appending /text(). In other words,\nExtractValue('Sakila', '/a/b') and\nExtractValue('Sakila', '/a/b/text()') produce the same\nresult.\n\nIf multiple matches are found, the content of the first child text node\nof each matching element is returned (in the order matched) as a\nsingle, space-delimited string.\n\nIf no matching text node is found for the expression (including the\nimplicit /text())---for whatever reason, as long as xpath_expr is\nvalid, and xml_frag consists of elements which are properly nested and\nclosed---an empty string is returned. No distinction is made between a\nmatch on an empty element and no match at all. This is by design.\n\nIf you need to determine whether no matching element was found in\nxml_frag or such an element was found but contained no child text\nnodes, you should test the result of an expression that uses the XPath\ncount() function. For example, both of these statements return an empty\nstring, as shown here:\n\nmysql> SELECT ExtractValue('', '/a/b');\n+-------------------------------------+\n| ExtractValue('', '/a/b') |\n+-------------------------------------+\n| |\n+-------------------------------------+\n1 row in set (0.00 sec)\n\nmysql> SELECT ExtractValue('', '/a/b');\n+-------------------------------------+\n| ExtractValue('', '/a/b') |\n+-------------------------------------+\n| |\n+-------------------------------------+\n1 row in set (0.00 sec)\n\nHowever, you can determine whether there was actually a matching\nelement using the following:\n\nmysql> SELECT ExtractValue('', 'count(/a/b)');\n+-------------------------------------+\n| ExtractValue('', 'count(/a/b)') |\n+-------------------------------------+\n ...
+[FIELD]
+declaration=str,str1,str2,str3,...
+category=String Functions
+description=Returns the index (position) of str in the str1, str2, str3, ... list.\nReturns 0 if str is not found.\n\nIf all arguments to FIELD() are strings, all arguments are compared as\nstrings. If all arguments are numbers, they are compared as numbers.\nOtherwise, the arguments are compared as double.\n\nIf str is NULL, the return value is 0 because NULL fails equality\ncomparison with any value. FIELD() is the complement of ELT().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[FIND_IN_SET]
+declaration=str,strlist
+category=String Functions
+description=Returns a value in the range of 1 to N if the string str is in the\nstring list strlist consisting of N substrings. A string list is a\nstring composed of substrings separated by "," characters. If the first\nargument is a constant string and the second is a column of type SET,\nthe FIND_IN_SET() function is optimized to use bit arithmetic. Returns\n0 if str is not in strlist or if strlist is the empty string. Returns\nNULL if either argument is NULL. This function does not work properly\nif the first argument contains a comma (",") character.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[FLOAT]
+declaration=M,D
+category=Data Types
+description=A small (single-precision) floating-point number. Permissible values\nare -3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to\n3.402823466E+38. These are the theoretical limits, based on the IEEE\nstandard. The actual range might be slightly smaller depending on your\nhardware or operating system.\n\nM is the total number of digits and D is the number of digits following\nthe decimal point. If M and D are omitted, values are stored to the\nlimits permitted by the hardware. A single-precision floating-point\nnumber is accurate to approximately 7 decimal places.\n\nUNSIGNED, if specified, disallows negative values.\n\nUsing FLOAT might give you some unexpected problems because all\ncalculations in MySQL are done with double precision. See\nhttp://dev.mysql.com/doc/refman/5.7/en/no-matching-rows.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[FLOOR]
+declaration=X
+category=Numeric Functions
+description=Returns the largest integer value not greater than X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[FORMAT]
+declaration=X,D[,locale]
+category=String Functions
+description=Formats the number X to a format like '#,###,###.##', rounded to D\ndecimal places, and returns the result as a string. If D is 0, the\nresult has no decimal point or fractional part.\n\nThe optional third parameter enables a locale to be specified to be\nused for the result number's decimal point, thousands separator, and\ngrouping between separators. Permissible locale values are the same as\nthe legal values for the lc_time_names system variable (see\nhttp://dev.mysql.com/doc/refman/5.7/en/locale-support.html). If no\nlocale is specified, the default is 'en_US'.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[FOUND_ROWS]
+declaration=
+category=Information Functions
+description=A SELECT statement may include a LIMIT clause to restrict the number of\nrows the server returns to the client. In some cases, it is desirable\nto know how many rows the statement would have returned without the\nLIMIT, but without running the statement again. To obtain this row\ncount, include a SQL_CALC_FOUND_ROWS option in the SELECT statement,\nand then invoke FOUND_ROWS() afterward:\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[FROM_DAYS]
+declaration=N
+category=Date and Time Functions
+description=Given a day number N, returns a DATE value.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[FROM_UNIXTIME]
+declaration=unix_timestamp
+category=Date and Time Functions
+description=Returns a representation of the unix_timestamp argument as a value in\n'YYYY-MM-DD HH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether\nthe function is used in a string or numeric context. The value is\nexpressed in the current time zone. unix_timestamp is an internal\ntimestamp value such as is produced by the UNIX_TIMESTAMP() function.\n\nIf format is given, the result is formatted according to the format\nstring, which is used the same way as listed in the entry for the\nDATE_FORMAT() function.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[GEOMCOLLFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a GeometryCollection value using its WKT representation and\nSRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[GEOMCOLLFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a GeometryCollection value using its WKB representation and\nSRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[GEOMETRYCOLLECTION]
+declaration=g1,g2,...
+category=Geometry constructors
+description=Constructs a GeometryCollection.\n\nAs of MySQL 5.7.5, GeometryCollection() returns all the proper\ngeometries contained in the argument even if a nonsupported geometry is\npresent. Before 5.7.5, if the argument contains a nonsupported\ngeometry, the return value is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[GEOMETRYN]
+declaration=gc,N
+category=GeometryCollection properties
+description=Returns the N-th geometry in the GeometryCollection value gc.\nGeometries are numbered beginning with 1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-geometrycollection-property-functions.html
+[GEOMETRYTYPE]
+declaration=g
+category=Geometry properties
+description=Returns a binary string indicating the name of the geometry type of\nwhich the geometry instance g is a member. The name corresponds to one\nof the instantiable Geometry subclasses.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[GEOMFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a geometry value of any type using its WKT representation\nand SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[GEOMFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a geometry value of any type using its WKB representation\nand SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[GET_FORMAT]
+declaration={DATE|TIME|DATETIME}, {'EUR'|'USA'|'JIS'|'ISO'|'INTERNAL'}
+category=Date and Time Functions
+description=Returns a format string. This function is useful in combination with\nthe DATE_FORMAT() and the STR_TO_DATE() functions.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[GET_LOCK]
+declaration=str,timeout
+category=Miscellaneous Functions
+description=Tries to obtain a lock with a name given by the string str, using a\ntimeout of timeout seconds. A negative timeout value means infinite\ntimeout.\n\nReturns 1 if the lock was obtained successfully, 0 if the attempt timed\nout (for example, because another client has previously locked the\nname), or NULL if an error occurred (such as running out of memory or\nthe thread was killed with mysqladmin kill). If you have a lock\nobtained with GET_LOCK(), it is released when you execute\nRELEASE_LOCK() or your connection terminates (either normally or\nabnormally). Lock release may also occur with another call to\nGET_LOCK():\n\no Before 5.7.5, only a single simultaneous lock can be acquired and\n GET_LOCK() releases any existing lock.\n\no In MySQL 5.7.5, GET_LOCK() was reimplemented using the metadata\n locking (MDL) subsystem and its capabilities were extended. Multiple\n simultaneous locks can be acquired and GET_LOCK() does not release\n any existing locks. It is even possible for a given session to\n acquire multiple locks for the same name. Other sessions cannot\n acquire a lock with that name until the acquiring session releases\n all its locks for the name.\n\n As a result of the MDL reimplementation, locks acquired with\n GET_LOCK() appear in the Performance Schema metadata_locks table. The\n OBJECT_TYPE column says USER LEVEL LOCK and the OBJECT_NAME column\n indicates the lock name. Also, the capability of acquiring multiple\n locks introduces the possibility of deadlock among clients. An\n ER_USER_LOCK_DEADLOCK error is returned when this occurs.\n\nThe difference in lock acquisition behavior as of MySQL 5.7.5 can be\nseen by the following example. Suppose that you execute these\nstatements:\n\nSELECT GET_LOCK('lock1',10);\nSELECT GET_LOCK('lock2',10);\nSELECT RELEASE_LOCK('lock2');\nSELECT RELEASE_LOCK('lock1');\n\nIn MySQL 5.7.5 or later, the second GET_LOCK() acquires a second lock\nand both RELEASE_LOCK() calls return 1 (success). Before MySQL 5.7.5,\nthe second GET_LOCK() releases the first lock ('lock1') and the second\nRELEASE_LOCK() returns NULL (failure) because there is no 'lock1' to\nrelease.\n\nMySQL 5.7.5 and later enforces a maximum length on lock names of 64\ncharacters. Previously, no limit was enforced.\n\nLocks obtained with GET_LOCK() do not interact with transactions. That\n ...
+[GLENGTH]
+declaration=ls
+category=LineString properties
+description=Returns a double-precision number indicating the length of the\nLineString value ls in its associated spatial reference.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[GREATEST]
+declaration=value1,value2,...
+category=Comparison operators
+description=With two or more arguments, returns the largest (maximum-valued)\nargument. The arguments are compared using the same rules as for\nLEAST().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[GROUP_CONCAT]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=This function returns a string result with the concatenated non-NULL\nvalues from a group. It returns NULL if there are no non-NULL values.\nThe full syntax is as follows:\n\nGROUP_CONCAT([DISTINCT] expr [,expr ...]\n [ORDER BY {unsigned_integer | col_name | expr}\n [ASC | DESC] [,col_name ...]]\n [SEPARATOR str_val])\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[GTID_SUBSET]
+declaration=subset,set
+category=MBR
+description=Given two sets of global transaction IDs subset and set, returns true\nif all GTIDs in subset are also in set. Returns false otherwise.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gtid-functions.html
+[GTID_SUBTRACT]
+declaration=set,subset
+category=MBR
+description=Given two sets of global transaction IDs subset and set, returns only\nthose GTIDs from set that are not in subset.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gtid-functions.html
+[HEX]
+declaration=str
+category=String Functions
+description=For a string argument str, HEX() returns a hexadecimal string\nrepresentation of str where each byte of each character in str is\nconverted to two hexadecimal digits. (Multibyte characters therefore\nbecome more than two digits.) The inverse of this operation is\nperformed by the UNHEX() function.\n\nFor a numeric argument N, HEX() returns a hexadecimal string\nrepresentation of the value of N treated as a longlong (BIGINT) number.\nThis is equivalent to CONV(N,10,16). The inverse of this operation is\nperformed by CONV(HEX(N),16,10).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[HOUR]
+declaration=time
+category=Date and Time Functions
+description=Returns the hour for time. The range of the return value is 0 to 23 for\ntime-of-day values. However, the range of TIME values actually is much\nlarger, so HOUR can return values greater than 23.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[IFNULL]
+declaration=expr1,expr2
+category=Control flow functions
+description=If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns\nexpr2. IFNULL() returns a numeric or string value, depending on the\ncontext in which it is used.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/control-flow-functions.html
+[IN]
+declaration=value,...
+category=Comparison operators
+description=Returns 1 if expr is equal to any of the values in the IN list, else\nreturns 0. If all values are constants, they are evaluated according to\nthe type of expr and sorted. The search for the item then is done using\na binary search. This means IN is very quick if the IN value list\nconsists entirely of constants. Otherwise, type conversion takes place\naccording to the rules described in\nhttp://dev.mysql.com/doc/refman/5.7/en/type-conversion.html, but\napplied to all the arguments.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[INET6_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address as a string, returns a binary\nstring that represents the numeric value of the address in network byte\norder (big endian). Because numeric-format IPv6 addresses require more\nbytes than the largest integer type, the representation returned by\nthis function has the VARBINARY data type: VARBINARY(16) for IPv6\naddresses and VARBINARY(4) for IPv4 addresses. If the argument is not a\nvalid address, INET6_ATON() returns NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[INET6_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address represented in numeric form as a\nbinary string, returns the string representation of the address as a\nnonbinary string in the connection character set. If the argument is\nnot a valid address, INET6_NTOA() returns NULL.\n\nINET6_NTOA() has these properties:\n\no It does not use operating system functions to perform conversions,\n thus the output string is platform independent.\n\no The return string has a maximum length of 39 (4 x 8 + 7). Given this\n statement:\n\nCREATE TABLE t AS SELECT INET6_NTOA(expr) AS c1;\n\n The resulting table would have this definition:\n\nCREATE TABLE t (c1 VARCHAR(39) CHARACTER SET utf8 DEFAULT NULL);\n\no The return string uses lowercase letters for IPv6 addresses.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[INET_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given the dotted-quad representation of an IPv4 network address as a\nstring, returns an integer that represents the numeric value of the\naddress in network byte order (big endian). INET_ATON() returns NULL if\nit does not understand its argument.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[INET_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given a numeric IPv4 network address in network byte order, returns the\ndotted-quad string representation of the address as a nonbinary string\nin the connection character set. INET_NTOA() returns NULL if it does\nnot understand its argument.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[INSTR]
+declaration=str,substr
+category=String Functions
+description=Returns the position of the first occurrence of substring substr in\nstring str. This is the same as the two-argument form of LOCATE(),\nexcept that the order of the arguments is reversed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[INT]
+declaration=M
+category=Data Types
+description=A normal-size integer. The signed range is -2147483648 to 2147483647.\nThe unsigned range is 0 to 4294967295.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[INTEGER]
+declaration=M
+category=Data Types
+description=This type is a synonym for INT.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[INTERIORRINGN]
+declaration=poly,N
+category=Polygon properties
+description=Returns the N-th interior ring for the Polygon value poly as a\nLineString. Rings are numbered beginning with 1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-polygon-property-functions.html
+[INTERSECTS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially intersects g2.\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBRIntersects() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[INTERVAL]
+declaration=N,N1,N2,N3,...
+category=Comparison operators
+description=Returns 0 if N < N1, 1 if N < N2 and so on or -1 if N is NULL. All\narguments are treated as integers. It is required that N1 < N2 < N3 <\n... < Nn for this function to work correctly. This is because a binary\nsearch is used (very fast).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[ISCLOSED]
+declaration=ls
+category=LineString properties
+description=Returns 1 if the LineString value ls is closed (that is, its\nStartPoint() and EndPoint() values are the same) and is simple (does\nnot pass through the same point more than once). Returns 0 if ls is not\nclosed, and -1 if it is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[ISEMPTY]
+declaration=g
+category=Geometry properties
+description=This function is a placeholder that returns 0 for any valid geometry\nvalue, 1 for any invalid geometry value or NULL.\n\nMySQL does not support GIS EMPTY values such as POINT EMPTY.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[ISNULL]
+declaration=expr
+category=Comparison operators
+description=If expr is NULL, ISNULL() returns 1, otherwise it returns 0.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[ISSIMPLE]
+declaration=g
+category=Geometry properties
+description=Returns 1 if the geometry value g has no anomalous geometric points,\nsuch as self-intersection or self-tangency. IsSimple() returns 0 if the\nargument is not simple, and NULL if it is NULL.\n\nThe description of each instantiable geometric class given earlier in\nthe chapter includes the specific conditions that cause an instance of\nthat class to be classified as not simple. (See [HELP Geometry\nhierarchy].)\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[IS_FREE_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Checks whether the lock named str is free to use (that is, not locked).\nReturns 1 if the lock is free (no one is using the lock), 0 if the lock\nis in use, and NULL if an error occurs (such as an incorrect argument).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[IS_IPV4]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if the argument is a valid IPv4 address specified as a\nstring, 0 otherwise.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[IS_IPV4_COMPAT]
+declaration=expr
+category=Miscellaneous Functions
+description=This function takes an IPv6 address represented in numeric form as a\nbinary string, as returned by INET6_ATON(). It returns 1 if the\nargument is a valid IPv4-compatible IPv6 address, 0 otherwise.\nIPv4-compatible addresses have the form ::ipv4_address.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[IS_IPV4_MAPPED]
+declaration=expr
+category=Miscellaneous Functions
+description=This function takes an IPv6 address represented in numeric form as a\nbinary string, as returned by INET6_ATON(). It returns 1 if the\nargument is a valid IPv4-mapped IPv6 address, 0 otherwise. IPv4-mapped\naddresses have the form ::ffff:ipv4_address.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[IS_IPV6]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if the argument is a valid IPv6 address specified as a\nstring, 0 otherwise. This function does not consider IPv4 addresses to\nbe valid IPv6 addresses.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[IS_USED_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Checks whether the lock named str is in use (that is, locked). If so,\nit returns the connection identifier of the client that holds the lock.\nOtherwise, it returns NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[JOIN]
+declaration=t2, t3, t4
+category=Data Manipulation
+description=ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)\n\nis equivalent to:\n\nSELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)\n ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c)\n\nIn MySQL, JOIN, CROSS JOIN, and INNER JOIN are syntactic equivalents\n(they can replace each other). In standard SQL, they are not\nequivalent. INNER JOIN is used with an ON clause, CROSS JOIN is used\notherwise.\n\nIn general, parentheses can be ignored in join expressions containing\nonly inner join operations. MySQL also supports nested joins (see\nhttp://dev.mysql.com/doc/refman/5.7/en/nested-join-optimization.html).\n\nIndex hints can be specified to affect how the MySQL optimizer makes\nuse of indexes. For more information, see\nhttp://dev.mysql.com/doc/refman/5.7/en/index-hints.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/join.html
+[LAST_DAY]
+declaration=date
+category=Date and Time Functions
+description=Takes a date or datetime value and returns the corresponding value for\nthe last day of the month. Returns NULL if the argument is invalid.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[LAST_INSERT_ID]
+declaration=
+category=Information Functions
+description=With no argument, LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit)\nvalue representing the first automatically generated value successfully\ninserted for an AUTO_INCREMENT column as a result of the most recently\nexecuted INSERT statement. The value of LAST_INSERT_ID() remains\nunchanged if no rows are successfully inserted.\n\nWith an argument, LAST_INSERT_ID() returns an unsigned integer.\n\nFor example, after inserting a row that generates an AUTO_INCREMENT\nvalue, you can get the value like this:\n\nmysql> SELECT LAST_INSERT_ID();\n -> 195\n\nThe currently executing statement does not affect the value of\nLAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value\nwith one statement, and then refer to LAST_INSERT_ID() in a\nmultiple-row INSERT statement that inserts rows into a table with its\nown AUTO_INCREMENT column. The value of LAST_INSERT_ID() will remain\nstable in the second statement; its value for the second and later rows\nis not affected by the earlier row insertions. (However, if you mix\nreferences to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the effect is\nundefined.)\n\nIf the previous statement returned an error, the value of\nLAST_INSERT_ID() is undefined. For transactional tables, if the\nstatement is rolled back due to an error, the value of LAST_INSERT_ID()\nis left undefined. For manual ROLLBACK, the value of LAST_INSERT_ID()\nis not restored to that before the transaction; it remains as it was at\nthe point of the ROLLBACK.\n\nPrior to MySQL 5.7.3, this function was not replicated correctly if\nreplication filtering rules were in use. (Bug #17234370, Bug #69861)\n\nWithin the body of a stored routine (procedure or function) or a\ntrigger, the value of LAST_INSERT_ID() changes the same way as for\nstatements executed outside the body of these kinds of objects. The\neffect of a stored routine or trigger upon the value of\nLAST_INSERT_ID() that is seen by following statements depends on the\nkind of routine:\n\no If a stored procedure executes statements that change the value of\n LAST_INSERT_ID(), the changed value is seen by statements that follow\n the procedure call.\n\no For stored functions and triggers that change the value, the value is\n restored when the function or trigger ends, so following statements\n will not see a changed value.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[LCASE]
+declaration=str
+category=String Functions
+description=LCASE() is a synonym for LOWER().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LEAST]
+declaration=value1,value2,...
+category=Comparison operators
+description=With two or more arguments, returns the smallest (minimum-valued)\nargument. The arguments are compared using the following rules:\n\no If any argument is NULL, the result is NULL. No comparison is needed.\n\no If the return value is used in an INTEGER context or all arguments\n are integer-valued, they are compared as integers.\n\no If the return value is used in a REAL context or all arguments are\n real-valued, they are compared as reals.\n\no If the arguments comprise a mix of numbers and strings, they are\n compared as numbers.\n\no If any argument is a nonbinary (character) string, the arguments are\n compared as nonbinary strings.\n\no In all other cases, the arguments are compared as binary strings.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html
+[LEFT]
+declaration=str,len
+category=String Functions
+description=Returns the leftmost len characters from the string str, or NULL if any\nargument is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str, measured in bytes. A multibyte\ncharacter counts as multiple bytes. This means that for a string\ncontaining five 2-byte characters, LENGTH() returns 10, whereas\nCHAR_LENGTH() returns 5.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LINEFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a LineString value using its WKT representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[LINEFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a LineString value using its WKB representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[LINESTRING]
+declaration=pt1,pt2,...
+category=Geometry constructors
+description=Constructs a LineString value from a number of Point or WKB Point\narguments. If the number of arguments is less than two, the return\nvalue is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[LN]
+declaration=X
+category=Numeric Functions
+description=Returns the natural logarithm of X; that is, the base-e logarithm of X.\nAs of MySQL 5.7.4, if X is less than or equal to 0.0E0, the error\n"Invalid argument for logarithm" is reported in strict SQL mode, and\nNULL is returned in non-strict mode. Before MySQL 5.7.4, if X is less\nthan or equal to 0.0E0, NULL is returned.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[LOAD_FILE]
+declaration=file_name
+category=String Functions
+description=Reads the file and returns the file contents as a string. To use this\nfunction, the file must be located on the server host, you must specify\nthe full path name to the file, and you must have the FILE privilege.\nThe file must be readable by all and its size less than\nmax_allowed_packet bytes. If the secure_file_priv system variable is\nset to a nonempty directory name, the file to be loaded must be located\nin that directory.\n\nIf the file does not exist or cannot be read because one of the\npreceding conditions is not satisfied, the function returns NULL.\n\nThe character_set_filesystem system variable controls interpretation of\nfile names that are given as literal strings.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LOCALTIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=LOCALTIME and LOCALTIME() are synonyms for NOW().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[LOCALTIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=LOCALTIMESTAMP and LOCALTIMESTAMP() are synonyms for NOW().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[LOCATE]
+declaration=substr,str
+category=String Functions
+description=The first syntax returns the position of the first occurrence of\nsubstring substr in string str. The second syntax returns the position\nof the first occurrence of substring substr in string str, starting at\nposition pos. Returns 0 if substr is not in str.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LOG]
+declaration=X
+category=Numeric Functions
+description=If called with one parameter, this function returns the natural\nlogarithm of X. As of MySQL 5.7.4, if X is less than or equal to 0.0E0,\nthe error "Invalid argument for logarithm" is reported in strict SQL\nmode, and NULL is returned in non-strict mode. Before MySQL 5.7.4, if X\nis less than or equal to 0.0E0, NULL is returned.\n\nThe inverse of this function (when called with a single argument) is\nthe EXP() function.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[LOG10]
+declaration=X
+category=Numeric Functions
+description=Returns the base-10 logarithm of X. As of MySQL 5.7.4, if X is less\nthan or equal to 0.0E0, the error "Invalid argument for logarithm" is\nreported in strict SQL mode, and NULL is returned in non-strict mode.\nBefore MySQL 5.7.4, if X is less than or equal to 0.0E0, NULL is\nreturned.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[LOG2]
+declaration=X
+category=Numeric Functions
+description=Returns the base-2 logarithm of X. As of MySQL 5.7.4, if X is less than\nor equal to 0.0E0, the error "Invalid argument for logarithm" is\nreported in strict SQL mode, and NULL is returned in non-strict mode.\nBefore MySQL 5.7.4, if X is less than or equal to 0.0E0, NULL is\nreturned.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[LOWER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to lowercase\naccording to the current character set mapping. The default is latin1\n(cp1252 West European).\n\nmysql> SELECT LOWER('QUADRATICALLY');\n -> 'quadratically'\n\nLOWER() (and UPPER()) are ineffective when applied to binary strings\n(BINARY, VARBINARY, BLOB). To perform lettercase conversion, convert\nthe string to a nonbinary string:\n\nmysql> SET @str = BINARY 'New York';\nmysql> SELECT LOWER(@str), LOWER(CONVERT(@str USING latin1));\n+-------------+-----------------------------------+\n| LOWER(@str) | LOWER(CONVERT(@str USING latin1)) |\n+-------------+-----------------------------------+\n| New York | new york |\n+-------------+-----------------------------------+\n\nFor Unicode character sets, LOWER() and UPPER() work accounting to\nUnicode Collation Algorithm (UCA) 5.2.0 for xxx_unicode_520_ci\ncollations and for language-specific collations that are derived from\nthem. For other Unicode collations, LOWER() and UPPER() work accounting\nto Unicode Collation Algorithm (UCA) 4.0.0. See\nhttp://dev.mysql.com/doc/refman/5.7/en/charset-unicode-sets.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LPAD]
+declaration=str,len,padstr
+category=String Functions
+description=Returns the string str, left-padded with the string padstr to a length\nof len characters. If str is longer than len, the return value is\nshortened to len characters.\n\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[LTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with leading space characters removed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[MAKEDATE]
+declaration=year,dayofyear
+category=Date and Time Functions
+description=Returns a date, given year and day-of-year values. dayofyear must be\ngreater than 0 or the result is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MAKETIME]
+declaration=hour,minute,second
+category=Date and Time Functions
+description=Returns a time value calculated from the hour, minute, and second\narguments.\n\nThe second argument can have a fractional part.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MAKE_SET]
+declaration=bits,str1,str2,...
+category=String Functions
+description=Returns a set value (a string containing substrings separated by ","\ncharacters) consisting of the strings that have the corresponding bit\nin bits set. str1 corresponds to bit 0, str2 to bit 1, and so on. NULL\nvalues in str1, str2, ... are not appended to the result.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[MASTER_POS_WAIT]
+declaration=log_name,log_pos[,timeout]
+category=Miscellaneous Functions
+description=This function is useful for control of master/slave synchronization. It\nblocks until the slave has read and applied all updates up to the\nspecified position in the master log. The return value is the number of\nlog events the slave had to wait for to advance to the specified\nposition. The function returns NULL if the slave SQL thread is not\nstarted, the slave's master information is not initialized, the\narguments are incorrect, or an error occurs. It returns -1 if the\ntimeout has been exceeded. If the slave SQL thread stops while\nMASTER_POS_WAIT() is waiting, the function returns NULL. If the slave\nis past the specified position, the function returns immediately.\n\nIf a timeout value is specified, MASTER_POS_WAIT() stops waiting when\ntimeout seconds have elapsed. timeout must be greater than 0; a zero or\nnegative timeout means no timeout.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[MAX]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the maximum value of expr. MAX() may take a string argument; in\nsuch cases, it returns the maximum string value. See\nhttp://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html. The DISTINCT\nkeyword can be used to find the maximum of the distinct values of expr,\nhowever, this produces the same result as omitting DISTINCT.\n\nMAX() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[MBRCONTAINS]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\ncontains the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRWithin().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRCOVEREDBY]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\nis covered by the minimum bounding rectangle of g2. This tests the\nopposite relationship as MBRCovers().\n\nMBRCoveredBy() and MBRCovers() handle their arguments and return a\nvalue as follows:\n\no Return NULL if either argument is NULL or an empty geometry\n\no Return ER_GIS_INVALID_DATA if either argument is not a valid geometry\n byte string (SRID plus WKB value)\n\no Otherwise, return non-NULL\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRCOVERS]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\ncovers the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRCoveredBy(). See the description of MBRCoveredBy()\nfor examples and information about argument handling.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRDISJOINT]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 are disjoint (do not intersect).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBREQUAL]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 are the same.\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBREquals() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBREQUALS]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 are the same.\n\nThis function was added in MySQL 5.7.6. It should be used in preference\nto MBREqual().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRINTERSECTS]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 intersect.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBROVERLAPS]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 overlap. The term spatially overlaps is\nused if two geometries intersect and their intersection results in a\ngeometry of the same dimension but not equal to either of the given\ngeometries.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRTOUCHES]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 touch. Two geometries spatially touch if\nthe interiors of the geometries do not intersect, but the boundary of\none of the geometries intersects either the boundary or the interior of\nthe other.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MBRWITHIN]
+declaration=g1,g2
+category=MBR
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\nis within the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRContains().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mysql-specific.html
+[MD5]
+declaration=str
+category=Encryption Functions
+description=Calculates an MD5 128-bit checksum for the string. The value is\nreturned as a string of 32 hex digits, or NULL if the argument was\nNULL. The return value can, for example, be used as a hash key. See the\nnotes at the beginning of this section about storing hash values\nefficiently.\n\nThe return value is a nonbinary string in the connection character set.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[MEDIUMINT]
+declaration=M
+category=Data Types
+description=A medium-sized integer. The signed range is -8388608 to 8388607. The\nunsigned range is 0 to 16777215.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[MICROSECOND]
+declaration=expr
+category=Date and Time Functions
+description=Returns the microseconds from the time or datetime expression expr as a\nnumber in the range from 0 to 999999.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MID]
+declaration=str,pos,len
+category=String Functions
+description=MID(str,pos,len) is a synonym for SUBSTRING(str,pos,len).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[MIN]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the minimum value of expr. MIN() may take a string argument; in\nsuch cases, it returns the minimum string value. See\nhttp://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html. The DISTINCT\nkeyword can be used to find the minimum of the distinct values of expr,\nhowever, this produces the same result as omitting DISTINCT.\n\nMIN() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[MINUTE]
+declaration=time
+category=Date and Time Functions
+description=Returns the minute for time, in the range 0 to 59.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MLINEFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MultiLineString value using its WKT representation and\nSRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[MLINEFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MultiLineString value using its WKB representation and\nSRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[MOD]
+declaration=N,M
+category=Numeric Functions
+description=Modulo operation. Returns the remainder of N divided by M.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[MONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the month for date, in the range 1 to 12 for January to\nDecember, or 0 for dates such as '0000-00-00' or '2008-00-00' that have\na zero month part.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MONTHNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the full name of the month for date. The language used for the\nname is controlled by the value of the lc_time_names system variable\n(http://dev.mysql.com/doc/refman/5.7/en/locale-support.html).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[MPOINTFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MultiPoint value using its WKT representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[MPOINTFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MultiPoint value using its WKB representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[MPOLYFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a MultiPolygon value using its WKT representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[MPOLYFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a MultiPolygon value using its WKB representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[MULTILINESTRING]
+declaration=ls1,ls2,...
+category=Geometry constructors
+description=Constructs a MultiLineString value using LineString or WKB LineString\narguments.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[MULTIPOINT]
+declaration=pt1,pt2,...
+category=Geometry constructors
+description=Constructs a MultiPoint value using Point or WKB Point arguments.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[MULTIPOLYGON]
+declaration=poly1,poly2,...
+category=Geometry constructors
+description=Constructs a MultiPolygon value from a set of Polygon or WKB Polygon\narguments.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[NAME_CONST]
+declaration=name,value
+category=Miscellaneous Functions
+description=Returns the given value. When used to produce a result set column,\nNAME_CONST() causes the column to have the given name. The arguments\nshould be constants.\n\nmysql> SELECT NAME_CONST('myname', 14);\n+--------+\n| myname |\n+--------+\n| 14 |\n+--------+\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[NOW]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS'\nor YYYYMMDDHHMMSS format, depending on whether the function is used in\na string or numeric context. The value is expressed in the current time\nzone.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[NULLIF]
+declaration=expr1,expr2
+category=Control flow functions
+description=Returns NULL if expr1 = expr2 is true, otherwise returns expr1. This is\nthe same as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/control-flow-functions.html
+[NUMGEOMETRIES]
+declaration=gc
+category=GeometryCollection properties
+description=Returns the number of geometries in the GeometryCollection value gc.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-geometrycollection-property-functions.html
+[NUMINTERIORRINGS]
+declaration=poly
+category=Polygon properties
+description=Returns the number of interior rings in the Polygon value poly.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-polygon-property-functions.html
+[NUMPOINTS]
+declaration=ls
+category=LineString properties
+description=Returns the number of Point objects in the LineString value ls.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[OCT]
+declaration=N
+category=String Functions
+description=Returns a string representation of the octal value of N, where N is a\nlonglong (BIGINT) number. This is equivalent to CONV(N,10,8). Returns\nNULL if N is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[OCTET_LENGTH]
+declaration=str
+category=String Functions
+description=OCTET_LENGTH() is a synonym for LENGTH().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[OLD_PASSWORD]
+declaration=str
+category=Encryption Functions
+description=OLD_PASSWORD() was added when the implementation of PASSWORD() was\nchanged in MySQL 4.1 to improve security. OLD_PASSWORD() returns the\nvalue of the pre-4.1 implementation of PASSWORD() as a string, and is\nintended to permit you to reset passwords for any pre-4.1 clients that\nneed to connect to your version 5.7 MySQL server without locking them\nout. See http://dev.mysql.com/doc/refman/5.7/en/password-hashing.html.\n\nThe return value is a nonbinary string in the connection character set.\n\n*Note*: Passwords that use the pre-4.1 hashing method are less secure\nthan passwords that use the native password hashing method and should\nbe avoided. Pre-4.1 passwords are deprecated and support for them is\nremoved in MySQL 5.7.5. Consequently, OLD_PASSWORD() is deprecated and\nis removed in MySQL 5.7.5.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[ORD]
+declaration=str
+category=String Functions
+description=If the leftmost character of the string str is a multibyte character,\nreturns the code for that character, calculated from the numeric values\nof its constituent bytes using this formula:\n\n (1st byte code)\n+ (2nd byte code * 256)\n+ (3rd byte code * 2562) ...\n\nIf the leftmost character is not a multibyte character, ORD() returns\nthe same value as the ASCII() function.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[OVERLAPS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially overlaps g2. The term\nspatially overlaps is used if two geometries intersect and their\nintersection results in a geometry of the same dimension but not equal\nto either of the given geometries.\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBROverlaps() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[PASSWORD]
+declaration=str
+category=Encryption Functions
+description=*Note*: This function is deprecated as of MySQL 5.7.6 and will be\nremoved in a future MySQL release.\n\nReturns a hashed password string calculated from the cleartext password\nstr. The return value is a nonbinary string in the connection character\nset, or NULL if the argument is NULL. This function is the SQL\ninterface to the algorithm used by the server to encrypt MySQL\npasswords for storage in the mysql.user grant table.\n\nThe old_passwords system variable controls the password hashing method\nused by the PASSWORD() function. It also influences password hashing\nperformed by CREATE USER and GRANT statements that specify a password\nusing an IDENTIFIED BY clause.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[PERIOD_ADD]
+declaration=P,N
+category=Date and Time Functions
+description=Adds N months to period P (in the format YYMM or YYYYMM). Returns a\nvalue in the format YYYYMM. Note that the period argument P is not a\ndate value.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[PERIOD_DIFF]
+declaration=P1,P2
+category=Date and Time Functions
+description=Returns the number of months between periods P1 and P2. P1 and P2\nshould be in the format YYMM or YYYYMM. Note that the period arguments\nP1 and P2 are not date values.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[PI]
+declaration=
+category=Numeric Functions
+description=Returns the value of ? (pi). The default number of decimal places\ndisplayed is seven, but MySQL uses the full double-precision value\ninternally.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[POINT]
+declaration=x,y
+category=Geometry constructors
+description=Constructs a Point using its coordinates.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[POINTFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a Point value using its WKT representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[POINTFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a Point value using its WKB representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[POINTN]
+declaration=ls,N
+category=LineString properties
+description=Returns the N-th Point in the Linestring value ls. Points are numbered\nbeginning with 1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[POLYFROMTEXT]
+declaration=wkt[,srid]
+category=WKT
+description=Constructs a Polygon value using its WKT representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkt-functions.html
+[POLYFROMWKB]
+declaration=wkb[,srid]
+category=WKB
+description=Constructs a Polygon value using its WKB representation and SRID.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-wkb-functions.html
+[POLYGON]
+declaration=ls1,ls2,...
+category=Geometry constructors
+description=Constructs a Polygon value from a number of LineString or WKB\nLineString arguments. If any argument does not represent a LinearRing\n(that is, not a closed and simple LineString), the return value is\nNULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-mysql-specific-functions.html
+[POSITION]
+declaration=substr IN str
+category=String Functions
+description=POSITION(substr IN str) is a synonym for LOCATE(substr,str).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[POW]
+declaration=X,Y
+category=Numeric Functions
+description=Returns the value of X raised to the power of Y.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[POWER]
+declaration=X,Y
+category=Numeric Functions
+description=This is a synonym for POW().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[QUARTER]
+declaration=date
+category=Date and Time Functions
+description=Returns the quarter of the year for date, in the range 1 to 4.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[QUOTE]
+declaration=str
+category=String Functions
+description=Quotes a string to produce a result that can be used as a properly\nescaped data value in an SQL statement. The string is returned enclosed\nby single quotation marks and with each instance of backslash ("\"),\nsingle quote ("'"), ASCII NUL, and Control+Z preceded by a backslash.\nIf the argument is NULL, the return value is the word "NULL" without\nenclosing single quotation marks.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[RADIANS]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from degrees to radians. (Note that\n? radians equals 180 degrees.)\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[RAND]
+declaration=
+category=Numeric Functions
+description=Returns a random floating-point value v in the range 0 <= v < 1.0. If a\nconstant integer argument N is specified, it is used as the seed value,\nwhich produces a repeatable sequence of column values. In the following\nexample, note that the sequences of values produced by RAND(3) is the\nsame both places where it occurs.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[RANDOM_BYTES]
+declaration=len
+category=Encryption Functions
+description=This function returns a binary string of len random bytes generated\nusing the random number generator of the SSL library (OpenSSL or\nyaSSL). Permitted values of len range from 1 to 1024. For values\noutside that range, RANDOM_BYTES() generates a warning and returns\nNULL.\n\nRANDOM_BYTES() can be used to provide the initialization vector for the\nAES_DECRYPT() and AES_ENCRYPT() functions. For use in that context, len\nmust be at least 16. Larger values are permitted, but bytes in excess\nof 16 are ignored.\n\nRANDOM_BYTES() generates a random value, which makes its result\nnondeterministic. Consequently, statements that use this function are\nunsafe for statement-based replication and cannot be stored in the\nquery cache.\n\nThis function is available as of MySQL 5.7.4.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[RELEASE_ALL_LOCKS]
+declaration=
+category=Miscellaneous Functions
+description=Releases all named locks held by the current session and returns the\nnumber of locks released (0 if there were none)\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[RELEASE_LOCK]
+declaration=str
+category=Miscellaneous Functions
+description=Releases the lock named by the string str that was obtained with\nGET_LOCK(). Returns 1 if the lock was released, 0 if the lock was not\nestablished by this thread (in which case the lock is not released),\nand NULL if the named lock did not exist. The lock does not exist if it\nwas never obtained by a call to GET_LOCK() or if it has previously been\nreleased.\n\nThe DO statement is convenient to use with RELEASE_LOCK(). See [HELP\nDO].\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[REVERSE]
+declaration=str
+category=String Functions
+description=Returns the string str with the order of the characters reversed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[RIGHT]
+declaration=str,len
+category=String Functions
+description=Returns the rightmost len characters from the string str, or NULL if\nany argument is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[ROUND]
+declaration=X
+category=Numeric Functions
+description=Rounds the argument X to D decimal places. The rounding algorithm\ndepends on the data type of X. D defaults to 0 if not specified. D can\nbe negative to cause D digits left of the decimal point of the value X\nto become zero.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[ROW_COUNT]
+declaration=
+category=Information Functions
+description=In MySQL 5.7, ROW_COUNT() returns a value as follows:\n\no DDL statements: 0. This applies to statements such as CREATE TABLE or\n DROP TABLE.\n\no DML statements other than SELECT: The number of affected rows. This\n applies to statements such as UPDATE, INSERT, or DELETE (as before),\n but now also to statements such as ALTER TABLE and LOAD DATA INFILE.\n\no SELECT: -1 if the statement returns a result set, or the number of\n rows "affected" if it does not. For example, for SELECT * FROM t1,\n ROW_COUNT() returns -1. For SELECT * FROM t1 INTO OUTFILE\n 'file_name', ROW_COUNT() returns the number of rows written to the\n file.\n\no SIGNAL statements: 0.\n\nFor UPDATE statements, the affected-rows value by default is the number\nof rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to\nmysql_real_connect() when connecting to mysqld, the affected-rows value\nis the number of rows "found"; that is, matched by the WHERE clause.\n\nFor REPLACE statements, the affected-rows value is 2 if the new row\nreplaced an old row, because in this case, one row was inserted after\nthe duplicate was deleted.\n\nFor INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows\nvalue per row is 1 if the row is inserted as a new row, 2 if an\nexisting row is updated, and 0 if an existing row is set to its current\nvalues. If you specify the CLIENT_FOUND_ROWS flag, the affected-rows\nvalue is 1 (not 0) if an existing row is set to its current values.\n\nThe ROW_COUNT() value is similar to the value from the\nmysql_affected_rows() C API function and the row count that the mysql\nclient displays following statement execution.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[RPAD]
+declaration=str,len,padstr
+category=String Functions
+description=Returns the string str, right-padded with the string padstr to a length\nof len characters. If str is longer than len, the return value is\nshortened to len characters.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[RTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with trailing space characters removed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SCHEMA]
+declaration=
+category=Information Functions
+description=This function is a synonym for DATABASE().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[SECOND]
+declaration=time
+category=Date and Time Functions
+description=Returns the second for time, in the range 0 to 59.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[SEC_TO_TIME]
+declaration=seconds
+category=Date and Time Functions
+description=Returns the seconds argument, converted to hours, minutes, and seconds,\nas a TIME value. The range of the result is constrained to that of the\nTIME data type. A warning occurs if the argument corresponds to a value\noutside that range.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[SESSION_USER]
+declaration=
+category=Information Functions
+description=SESSION_USER() is a synonym for USER().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[SHA1]
+declaration=str
+category=Encryption Functions
+description=Calculates an SHA-1 160-bit checksum for the string, as described in\nRFC 3174 (Secure Hash Algorithm). The value is returned as a string of\n40 hex digits, or NULL if the argument was NULL. One of the possible\nuses for this function is as a hash key. See the notes at the beginning\nof this section about storing hash values efficiently. You can also use\nSHA1() as a cryptographic function for storing passwords. SHA() is\nsynonymous with SHA1().\n\nThe return value is a nonbinary string in the connection character set.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[SHA2]
+declaration=str, hash_length
+category=Encryption Functions
+description=Calculates the SHA-2 family of hash functions (SHA-224, SHA-256,\nSHA-384, and SHA-512). The first argument is the cleartext string to be\nhashed. The second argument indicates the desired bit length of the\nresult, which must have a value of 224, 256, 384, 512, or 0 (which is\nequivalent to 256). If either argument is NULL or the hash length is\nnot one of the permitted values, the return value is NULL. Otherwise,\nthe function result is a hash value containing the desired number of\nbits. See the notes at the beginning of this section about storing hash\nvalues efficiently.\n\nThe return value is a nonbinary string in the connection character set.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[SIGN]
+declaration=X
+category=Numeric Functions
+description=Returns the sign of the argument as -1, 0, or 1, depending on whether X\nis negative, zero, or positive.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[SIN]
+declaration=X
+category=Numeric Functions
+description=Returns the sine of X, where X is given in radians.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[SLEEP]
+declaration=duration
+category=Miscellaneous Functions
+description=Sleeps (pauses) for the number of seconds given by the duration\nargument, then returns 0. If SLEEP() is interrupted, it returns 1. The\nduration may have a fractional part. If the argument is NULL or\nnegative, SLEEP() produces a warning, or an error in strict SQL mode.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[SMALLINT]
+declaration=M
+category=Data Types
+description=A small integer. The signed range is -32768 to 32767. The unsigned\nrange is 0 to 65535.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[SOUNDEX]
+declaration=str
+category=String Functions
+description=Returns a soundex string from str. Two strings that sound almost the\nsame should have identical soundex strings. A standard soundex string\nis four characters long, but the SOUNDEX() function returns an\narbitrarily long string. You can use SUBSTRING() on the result to get a\nstandard soundex string. All nonalphabetic characters in str are\nignored. All international alphabetic characters outside the A-Z range\nare treated as vowels.\n\n*Important*: When using SOUNDEX(), you should be aware of the following\nlimitations:\n\no This function, as currently implemented, is intended to work well\n with strings that are in the English language only. Strings in other\n languages may not produce reliable results.\n\no This function is not guaranteed to provide consistent results with\n strings that use multibyte character sets, including utf-8.\n\n We hope to remove these limitations in a future release. See Bug\n #22638 for more information.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SPACE]
+declaration=N
+category=String Functions
+description=Returns a string consisting of N space characters.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SQRT]
+declaration=X
+category=Numeric Functions
+description=Returns the square root of a nonnegative number X.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[SRID]
+declaration=g
+category=Geometry properties
+description=Returns an integer indicating the Spatial Reference System ID for the\ngeometry value g.\n\nIn MySQL, the SRID value is just an integer associated with the\ngeometry value. All calculations are done assuming Euclidean (planar)\ngeometry.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[STARTPOINT]
+declaration=ls
+category=LineString properties
+description=Returns the Point that is the start point of the LineString value ls.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[STD]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr. This is an extension\nto standard SQL. The standard SQL function STDDEV_POP() can be used\ninstead.\n\nThis function returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[STDDEV]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr. This function is\nprovided for compatibility with Oracle. The standard SQL function\nSTDDEV_POP() can be used instead.\n\nThis function returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[STDDEV_POP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard deviation of expr (the square root of\nVAR_POP()). You can also use STD() or STDDEV(), which are equivalent\nbut not standard SQL.\n\nSTDDEV_POP() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[STDDEV_SAMP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sample standard deviation of expr (the square root of\nVAR_SAMP().\n\nSTDDEV_SAMP() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[STRCMP]
+declaration=expr1,expr2
+category=String Functions
+description=STRCMP() returns 0 if the strings are the same, -1 if the first\nargument is smaller than the second according to the current sort\norder, and 1 otherwise.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-comparison-functions.html
+[STR_TO_DATE]
+declaration=str,format
+category=Date and Time Functions
+description=This is the inverse of the DATE_FORMAT() function. It takes a string\nstr and a format string format. STR_TO_DATE() returns a DATETIME value\nif the format string contains both date and time parts, or a DATE or\nTIME value if the string contains only date or time parts. If the date,\ntime, or datetime value extracted from str is illegal, STR_TO_DATE()\nreturns NULL and produces a warning.\n\nThe server scans str attempting to match format to it. The format\nstring can contain literal characters and format specifiers beginning\nwith %. Literal characters in format must match literally in str.\nFormat specifiers in format must match a date or time part in str. For\nthe specifiers that can be used in format, see the DATE_FORMAT()\nfunction description.\n\nmysql> SELECT STR_TO_DATE('01,5,2013','%d,%m,%Y');\n -> '2013-05-01'\nmysql> SELECT STR_TO_DATE('May 1, 2013','%M %d,%Y');\n -> '2013-05-01'\n\nScanning starts at the beginning of str and fails if format is found\nnot to match. Extra characters at the end of str are ignored.\n\nmysql> SELECT STR_TO_DATE('a09:30:17','a%h:%i:%s');\n -> '09:30:17'\nmysql> SELECT STR_TO_DATE('a09:30:17','%h:%i:%s');\n -> NULL\nmysql> SELECT STR_TO_DATE('09:30:17a','%h:%i:%s');\n -> '09:30:17'\n\nUnspecified date or time parts have a value of 0, so incompletely\nspecified values in str produce a result with some or all parts set to\n0:\n\nmysql> SELECT STR_TO_DATE('abc','abc');\n -> '0000-00-00'\nmysql> SELECT STR_TO_DATE('9','%m');\n -> '0000-09-00'\nmysql> SELECT STR_TO_DATE('9','%s');\n -> '00:00:09'\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[ST_AREA]
+declaration=poly
+category=Polygon properties
+description=Returns a double-precision number indicating the area of the argument,\nas measured in its spatial reference system. For arguments of dimension\n0 or 1, the result is 0.\n\nAdditionally, as of MySQL 5.7.5: The result is the sum of the area\nvalues of all components for a geometry collection. If a geometry\ncollection is empty, its area is returned as 0.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-polygon-property-functions.html
+[ST_ASGEOJSON]
+declaration=g [, max_dec_digits [, options]]
+category=MBR
+description=Generates a GeoJSON object from the geometry g. The object string has\nthe connection character set and collation.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geojson-functions.html
+[ST_CENTROID]
+declaration=mpoly
+category=Polygon properties
+description=Returns the mathematical centroid for the MultiPolygon value mpoly as a\nPoint. The result is not guaranteed to be on the MultiPolygon.\n\nAs of MySQL 5.7.5, this function processes geometry collections by\ncomputing the centroid point for components of highest dimension in the\ncollection. Such components are extracted and made into a single\nMultiPolygon, MultiLineString, or MultiPoint for centroid computation.\nIf the argument is an empty geometry collection, the return value is\nNULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-multipolygon-property-functions.html
+[ST_CONTAINS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 completely contains g2. This\ntests the opposite relationship as ST_Within().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_CONVEXHULL]
+declaration=g
+category=GeometryCollection properties
+description=Returns a geometry that represents the convex hull of the geometry\nvalue g.\n\nThis function computes a geometry's convex hull by first checking\nwhether its vertex points are colinear. The function returns a linear\nhull if so, a polygon hull otherwise. This function processes geometry\ncollections by extracting all vertex points of all components of the\ncollection, creating a MultiPoint value from them, and computing its\nconvex hull. If the argument is an empty geometry collection, the\nreturn value is NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[ST_CROSSES]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 if g1 spatially crosses g2. Returns NULL if g1 is a Polygon\nor a MultiPolygon, or if g2 is a Point or a MultiPoint. Otherwise,\nreturns 0.\n\nAs of MySQL 5.7.5, this function returns 0 if called with an\ninapplicable geometry argument type combination. For example, it\nreturns 0 if the first argument is a Polygon or MultiPolygon and/or the\nsecond argument is a Point or MultiPoint.\n\nThe term spatially crosses denotes a spatial relation between two given\ngeometries that has the following properties:\n\no The two geometries intersect\n\no Their intersection results in a geometry that has a dimension that is\n one less than the maximum dimension of the two given geometries\n\no Their intersection is not equal to either of the two given geometries\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_DIFFERENCE]
+declaration=g1, g2
+category=GeometryCollection properties
+description=Returns a geometry that represents the point set difference of the\ngeometry values g1 and g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[ST_DISJOINT]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially disjoint from (does\nnot intersect) g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_DISTANCE]
+declaration=g1,g2
+category=Geometry relations
+description=Returns the distance between g1 and g2.\n\nAs of MySQL 5.7.5, this function processes geometry collections by\nreturning the shortest distance among all combinations of the\ncomponents of the two geometry arguments. If either argument is an\nempty geometry collection, the return value is NULL.\n\nAs of MySQL 5.7.6, if an intermediate or final result produces NaN or a\nnegative number, this function produces a ER_GIS_INVALID_DATA error.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_DISTANCE_SPHERE]
+declaration=g1, g2 [, radius]
+category=MBR
+description=Returns the mimimum spherical distance between two points and/or\nmultipoints on a sphere, in meters, or NULL if any geometry argument is\nNULL or empty.\n\nCalculations use a spherical earth and a configurable radius. The\noptional radius argument should be given in meters. If omitted, the\ndefault radius is 6,370,986 meters. An ER_WRONG_ARGUMENTS error occurs\nif the radius argument is present but not positive.\n\nThe geometry arguments should consist of points that specify\n(longitude, latitude) coordinate values:\n\no Longitude and latitude are the first and second coordinates of the\n point, respectively.\n\no Both coordinates are in degrees.\n\no Longitude values must be in the range (-180, 180]. Positive values\n are east of the prime meridian.\n\no Latitude values must be in the range [-90, 90]. Positive values are\n north of the equator.\n\nSupported argument combinations are (Point, Point), (Point,\nMultiPoint), and (MultiPoint, Point). An ER_GIS_UNSUPPORTED_ARGUMENT\nerror occurs for other combinations.\n\nAn ER_GIS_INVALID_DATA error occurs if any geometry argument is not a\nvalid geometry byte string.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
+[ST_ENVELOPE]
+declaration=g
+category=Geometry properties
+description=Returns the minimum bounding rectangle (MBR) for the geometry value g.\nThe result is returned as a Polygon value that is defined by the corner\npoints of the bounding box:\n\nPOLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))\n\nAs of MySQL 5.7.6, if the argument is a point or a vertical or\nhorizontal line segment, ST_Envelope() returns the point or the line\nsegment as its MBR rather than returning an invalid polygon.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-general-property-functions.html
+[ST_EQUALS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially equal to g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_GEOHASH]
+declaration=longitude, latitude, max_length
+category=MBR
+description=max_length)\n\nReturns a geohash string in the connection character set and collation.\nThe result is NULL if any argument is NULL. An error occurs if any\nargument is invalid.\n\nFor the first syntax, the longitude must be a number in the range\n[-180, 180], and the latitude must be a number in the range [-90, 90].\nFor the second syntax, a POINT value is required, where the X and Y\ncoordinates are in the valid ranges for longitude and latitude,\nrespectively.\n\nThe resulting string is no longer than max_length characters, which has\nan upper limit of 100. The string might be shorter than max_length\ncharacters because the algorithm that creates the geohash value\ncontinues until it has created a string that is either an exact\nrepresentation of the location or max_length characters, whichever\ncomes first.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geohash-functions.html
+[ST_GEOMFROMGEOJSON]
+declaration=str [, options [, srid]]
+category=MBR
+description=Parses a string str representing a GeoJSON object and returns a\ngeometry.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geojson-functions.html
+[ST_INTERSECTION]
+declaration=g1, g2
+category=GeometryCollection properties
+description=Returns a geometry that represents the point set intersection of the\ngeometry values g1 and g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[ST_INTERSECTS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially intersects g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_ISVALID]
+declaration=g
+category=MBR
+description=Checks whether a geometry is valid, as defined by the OGC\nspecification. ST_IsValid() returns 1 if the argument is a valid\ngeometry byte string and is geometrically valid, 0 if the argument is\nnot a valid geometry byte string or is not geometrically valid, NULL if\nthe argument is NULL.\n\nThe only valid empty geometry is represented in the form of an empty\ngeometry collection value. ST_IsValid() returns 1 in this case.\n\nST_IsValid() works only for the cartesian coordinate system and\nrequires a geometry argument with an SRID of 0. An ER_WRONG_ARGUMENTS\nerror occurs otherwise.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
+[ST_LATFROMGEOHASH]
+declaration=geohash_str
+category=MBR
+description=Returns the latitude from a geohash string value, as a DOUBLE value in\nthe range [-90, 90]. The result is NULL if any argument is NULL. An\nerror occurs if the argument is invalid.\n\nThe ST_LatFromGeoHash() decoding function reads no more than 433\ncharacters from the geohash_str argument. That represents the upper\nlimit on information in the internal representation of coordinate\nvalues. Characters past the 433rd are ignored, even if they are\notherwise illegal and produce an error.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geohash-functions.html
+[ST_LENGTH]
+declaration=ls
+category=LineString properties
+description=Returns a double-precision number indicating the length of the\nLineString value ls in its associated spatial reference.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-linestring-property-functions.html
+[ST_LONGFROMGEOHASH]
+declaration=geohash_str
+category=MBR
+description=Returns the longitude from a geohash string value, as a DOUBLE value in\nthe range [-180, 180]. The result is NULL if any argument is NULL. An\nerror occurs if the argument is invalid.\n\nThe remarks in the description of ST_LatFromGeoHash() regarding the\nmaximum number of characters processed from the geohash_str argument\nalso apply to ST_LongFromGeoHash().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geohash-functions.html
+[ST_MAKEENVELOPE]
+declaration=pt1, pt2
+category=MBR
+description=Returns the rectangle that forms the envelope around two points. The\nreturned geometry is a Point, LineString, or Polygon, or NULL if any\nargument is NULL.\n\nCalculations are done using the cartesian coordinate system rather than\non a sphere, spheroid, or on earth.\n\nGiven two points pt1 and pt2, ST_MakeEnvelope() creates the result\ngeometry on an abstract plane like this:\n\no If pt1 and pt2 are equal, the result is the point pt1.\n\no Otherwise, if (pt1, pt2) is a vertical or horizontal line segment,\n the result is the line segment (pt1, pt2).\n\no Otherwise, the result is a polygon using pt1 and pt2 as diagonal\n points. Either or both of pt1 and pt2 can be vertex points.\n\nThe result geometry has an SRID of 0.\n\nST_MakeEnvelope() requires Point geometry arguments with an SRID of 0.\nAn ER_WRONG_ARGUMENTS error occurs otherwise.\n\nAn ER_GIS_INVALID_DATA occurs if any argument is not a valid geometry\nbyte string, or if any coordinate value of the two points is infinite\n(that is, NaN).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
+[ST_OVERLAPS]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially overlaps g2. The term\nspatially overlaps is used if two geometries intersect and their\nintersection results in a geometry of the same dimension but not equal\nto either of the given geometries.\n\nAs of MySQL 5.7.5, this function returns 0 if called with an\ninapplicable geometry argument type combination. For example, it\nreturns 0 if called with geometries of different dimensions or any\nargument is a Point.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_POINTFROMGEOHASH]
+declaration=geohash_str, srid
+category=MBR
+description=Returns a POINT value containing the decoded geohash value, given a\ngeohash string value. The X and Y coordinates of the point are the\nlongitude in the range [-180, 180] and the latitude in the range [-90,\n90], respectively. The srid value is an unsigned 32-bit integer. The\nresult is NULL if any argument is NULL. An error occurs if any argument\nis invalid.\n\nThe remarks in the description of ST_LatFromGeoHash() regarding the\nmaximum number of characters processed from the geohash_str argument\nalso apply to ST_PointFromGeoHash().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-geohash-functions.html
+[ST_SIMPLIFY]
+declaration=g, max_distance
+category=MBR
+description=Simplifies a geometry using the Douglas-Peucker algorithm and returns a\nsimplified value of the same type, or NULL if any argument is NULL.\n\nThe geometry may be any geometry type, although the Douglas-Peucker\nalgorithm may not actually process every type. A geometry collection is\nprocessed by giving its components one by one to the simplification\nalgorithm, and the returned geometries are put into a geometry\ncollection as result.\n\nThe max_distance argument is the distance (in units of the input\ncoordinates) of a vertex to other segments to be removed. Vertices\nwithin this distance of the simplified linestring are removed. An\nER_WRONG_ARGUMENTS error occurs if the max_distance argument is not\npositive, or is NaN.\n\nAccording to Boost.Geometry, geometries might become invalid as a\nresult of the simplification process, and the process might create\nself-intersections. If you want to check the validity of the result,\npass it to ST_IsValid().\n\nAn ER_GIS_INVALID_DATA error occurs if the geometry argument is not a\nvalid geometry byte string.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
+[ST_SYMDIFFERENCE]
+declaration=g1, g2
+category=GeometryCollection properties
+description=Returns a geometry that represents the point set symmetric difference\nof the geometry values g1 and g2, which is defined as:\n\ng1 symdifference g2 := (g1 union g2) difference (g1 intersection g2)\n\nOr, in function call notation:\n\nST_SymDifference(g1, g2) = ST_Difference(ST_Union(g1, g2), ST_Intersection(g1, g2))\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[ST_TOUCHES]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially touches g2. Two\ngeometries spatially touch if the interiors of the geometries do not\nintersect, but the boundary of one of the geometries intersects either\nthe boundary or the interior of the other.\n\nAs of MySQL 5.7.5, this function returns 0 if called with an\ninapplicable geometry argument type combination. For example, it\nreturns 0 if either of the arguments is a Point or MultiPoint.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[ST_UNION]
+declaration=g1, g2
+category=GeometryCollection properties
+description=Returns a geometry that represents the point set union of the geometry\nvalues g1 and g2.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-operator-functions.html
+[ST_VALIDATE]
+declaration=g
+category=MBR
+description=Validates a geometry according to the OGC specification. ST_Validate()\nreturns the geometry if it is a valid geometry byte string and is\ngeometrically valid, NULL if the argument is not a valid geometry byte\nstring or is not geometrically valid or is NULL.\n\nA geometry can be a valid geometry byte string (WKB value plus SRID)\nbut geometrically invalid. For example, this polygon is geometrically\ninvalid: POLYGON((0 0, 0 0, 0 0, 0 0, 0 0))\n\nST_Validate() can be used to filter out invalid geometry data, although\nat a cost. For applications that require more precise results not\ntainted by invalid data, this penalty may be worthwhile.\n\nIf the geometry argument is valid, it is returned as is, except that if\nan input Polygon or MultiPolygon has clockwise rings, those rings are\nreversed before checking for validity. If the geometry is valid, the\nvalue with the reversed rings is returned.\n\nThe only valid empty geometry is represented in the form of an empty\ngeometry collection value. ST_Validate() returns it directly without\nfurther checks in this case.\n\nST_Validate() works only for the cartesian coordinate system and\nrequires a geometry argument with an SRID of 0. An ER_WRONG_ARGUMENTS\nerror occurs otherwise.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html
+[ST_WITHIN]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially within g2. This\ntests the opposite relationship as ST_Contains().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-object-shapes.html
+[SUBDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, SUBDATE()\nis a synonym for DATE_SUB(). For information on the INTERVAL unit\nargument, see the discussion for DATE_ADD().\n\nmysql> SELECT DATE_SUB('2008-01-02', INTERVAL 31 DAY);\n -> '2007-12-02'\nmysql> SELECT SUBDATE('2008-01-02', INTERVAL 31 DAY);\n -> '2007-12-02'\n\nThe second form enables the use of an integer value for days. In such\ncases, it is interpreted as the number of days to be subtracted from\nthe date or datetime expression expr.\n\nmysql> SELECT SUBDATE('2008-01-02 12:00:00', 31);\n -> '2007-12-02 12:00:00'\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[SUBSTR]
+declaration=str,pos
+category=String Functions
+description=FROM pos FOR len)\n\nSUBSTR() is a synonym for SUBSTRING().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SUBSTRING]
+declaration=str,pos
+category=String Functions
+description=SUBSTRING(str FROM pos FOR len)\n\nThe forms without a len argument return a substring from string str\nstarting at position pos. The forms with a len argument return a\nsubstring len characters long from string str, starting at position\npos. The forms that use FROM are standard SQL syntax. It is also\npossible to use a negative value for pos. In this case, the beginning\nof the substring is pos characters from the end of the string, rather\nthan the beginning. A negative value may be used for pos in any of the\nforms of this function.\n\nFor all forms of SUBSTRING(), the position of the first character in\nthe string from which the substring is to be extracted is reckoned as\n1.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SUBSTRING_INDEX]
+declaration=str,delim,count
+category=String Functions
+description=Returns the substring from string str before count occurrences of the\ndelimiter delim. If count is positive, everything to the left of the\nfinal delimiter (counting from the left) is returned. If count is\nnegative, everything to the right of the final delimiter (counting from\nthe right) is returned. SUBSTRING_INDEX() performs a case-sensitive\nmatch when searching for delim.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[SUBTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=SUBTIME() returns expr1 - expr2 expressed as a value in the same format\nas expr1. expr1 is a time or datetime expression, and expr2 is a time\nexpression.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[SUM]
+declaration=[DISTINCT] expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sum of expr. If the return set has no rows, SUM() returns\nNULL. The DISTINCT keyword can be used to sum only the distinct values\nof expr.\n\nSUM() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[SYSDATE]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD HH:MM:SS'\nor YYYYMMDDHHMMSS format, depending on whether the function is used in\na string or numeric context.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits. Before 5.6.4, any argument is ignored.\n\nSYSDATE() returns the time at which it executes. This differs from the\nbehavior for NOW(), which returns a constant time that indicates the\ntime at which the statement began to execute. (Within a stored function\nor trigger, NOW() returns the time at which the function or triggering\nstatement began to execute.)\n\nmysql> SELECT NOW(), SLEEP(2), NOW();\n+---------------------+----------+---------------------+\n| NOW() | SLEEP(2) | NOW() |\n+---------------------+----------+---------------------+\n| 2006-04-12 13:47:36 | 0 | 2006-04-12 13:47:36 |\n+---------------------+----------+---------------------+\n\nmysql> SELECT SYSDATE(), SLEEP(2), SYSDATE();\n+---------------------+----------+---------------------+\n| SYSDATE() | SLEEP(2) | SYSDATE() |\n+---------------------+----------+---------------------+\n| 2006-04-12 13:47:44 | 0 | 2006-04-12 13:47:46 |\n+---------------------+----------+---------------------+\n\nIn addition, the SET TIMESTAMP statement affects the value returned by\nNOW() but not by SYSDATE(). This means that timestamp settings in the\nbinary log have no effect on invocations of SYSDATE().\n\nBecause SYSDATE() can return different values even within the same\nstatement, and is not affected by SET TIMESTAMP, it is nondeterministic\nand therefore unsafe for replication if statement-based binary logging\nis used. If that is a problem, you can use row-based logging.\n\nAlternatively, you can use the --sysdate-is-now option to cause\nSYSDATE() to be an alias for NOW(). This works if the option is used on\nboth the master and the slave.\n\nThe nondeterministic nature of SYSDATE() also means that indexes cannot\nbe used for evaluating expressions that refer to it.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[SYSTEM_USER]
+declaration=
+category=Information Functions
+description=SYSTEM_USER() is a synonym for USER().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[TAN]
+declaration=X
+category=Numeric Functions
+description=Returns the tangent of X, where X is given in radians.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[TEXT]
+declaration=M
+category=Data Types
+description=A TEXT column with a maximum length of 65,535 (216 - 1) characters. The\neffective maximum length is less if the value contains multibyte\ncharacters. Each TEXT value is stored using a 2-byte length prefix that\nindicates the number of bytes in the value.\n\nAn optional length M can be given for this type. If this is done, MySQL\ncreates the column as the smallest TEXT type large enough to hold\nvalues M characters long.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[TIME]
+declaration=fsp
+category=Data Types
+description=A time. The range is '-838:59:59.000000' to '838:59:59.000000'. MySQL\ndisplays TIME values in 'HH:MM:SS[.fraction]' format, but permits\nassignment of values to TIME columns using either strings or numbers.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-overview.html
+[TIMEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=TIMEDIFF() returns expr1 - expr2 expressed as a time value. expr1 and\nexpr2 are time or date-and-time expressions, but both must be of the\nsame type.\n\nThe result returned by TIMEDIFF() is limited to the range allowed for\nTIME values. Alternatively, you can use either of the functions\nTIMESTAMPDIFF() and UNIX_TIMESTAMP(), both of which return integers.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TIMESTAMP]
+declaration=fsp
+category=Data Types
+description=A timestamp. The range is '1970-01-01 00:00:01.000000' UTC to\n'2038-01-19 03:14:07.999999' UTC. TIMESTAMP values are stored as the\nnumber of seconds since the epoch ('1970-01-01 00:00:00' UTC). A\nTIMESTAMP cannot represent the value '1970-01-01 00:00:00' because that\nis equivalent to 0 seconds from the epoch and the value 0 is reserved\nfor representing '0000-00-00 00:00:00', the "zero" TIMESTAMP value.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nThe way the server handles TIMESTAMP definitions depends on the value\nof the explicit_defaults_for_timestamp system variable (see\nhttp://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html).\nBy default, explicit_defaults_for_timestamp is disabled and the server\nhandles TIMESTAMP as follows:\n\nUnless specified otherwise, the first TIMESTAMP column in a table is\ndefined to be automatically set to the date and time of the most recent\nmodification if not explicitly assigned a value. This makes TIMESTAMP\nuseful for recording the timestamp of an INSERT or UPDATE operation.\nYou can also set any TIMESTAMP column to the current date and time by\nassigning it a NULL value, unless it has been defined with the NULL\nattribute to permit NULL values.\n\nAutomatic initialization and updating to the current date and time can\nbe specified using DEFAULT CURRENT_TIMESTAMP and ON UPDATE\nCURRENT_TIMESTAMP column definition clauses. By default, the first\nTIMESTAMP column has these properties, as previously noted. However,\nany TIMESTAMP column in a table can be defined to have these\nproperties.\n\nIf explicit_defaults_for_timestamp is enabled, there is no automatic\nassignment of the DEFAULT CURRENT_TIMESTAMP or ON UPDATE\nCURRENT_TIMESTAMP attributes to any TIMESTAMP column. They must be\nincluded explicitly in the column definition. Also, any TIMESTAMP not\nexplicitly declared as NOT NULL permits NULL values.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-overview.html
+[TIMESTAMPADD]
+declaration=unit,interval,datetime_expr
+category=Date and Time Functions
+description=Adds the integer expression interval to the date or datetime expression\ndatetime_expr. The unit for interval is given by the unit argument,\nwhich should be one of the following values: MICROSECOND\n(microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or\nYEAR.\n\nThe unit value may be specified using one of keywords as shown, or with\na prefix of SQL_TSI_. For example, DAY and SQL_TSI_DAY both are legal.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TIMESTAMPDIFF]
+declaration=unit,datetime_expr1,datetime_expr2
+category=Date and Time Functions
+description=Returns datetime_expr2 - datetime_expr1, where datetime_expr1 and\ndatetime_expr2 are date or datetime expressions. One expression may be\na date and the other a datetime; a date value is treated as a datetime\nhaving the time part '00:00:00' where necessary. The unit for the\nresult (an integer) is given by the unit argument. The legal values for\nunit are the same as those listed in the description of the\nTIMESTAMPADD() function.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TIME_FORMAT]
+declaration=time,format
+category=Date and Time Functions
+description=This is used like the DATE_FORMAT() function, but the format string may\ncontain format specifiers only for hours, minutes, seconds, and\nmicroseconds. Other specifiers produce a NULL value or 0.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TIME_TO_SEC]
+declaration=time
+category=Date and Time Functions
+description=Returns the time argument, converted to seconds.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TINYINT]
+declaration=M
+category=Data Types
+description=A very small integer. The signed range is -128 to 127. The unsigned\nrange is 0 to 255.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
+[TOUCHES]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 spatially touches g2. Two\ngeometries spatially touch if the interiors of the geometries do not\nintersect, but the boundary of one of the geometries intersects either\nthe boundary or the interior of the other.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[TO_DAYS]
+declaration=date
+category=Date and Time Functions
+description=Given a date date, returns a day number (the number of days since year\n0).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TO_SECONDS]
+declaration=expr
+category=Date and Time Functions
+description=Given a date or datetime expr, returns the number of seconds since the\nyear 0. If expr is not a valid date or datetime value, returns NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[TRIM]
+declaration=[{BOTH | LEADING | TRAILING} [remstr] FROM] str
+category=String Functions
+description=FROM] str)\n\nReturns the string str with all remstr prefixes or suffixes removed. If\nnone of the specifiers BOTH, LEADING, or TRAILING is given, BOTH is\nassumed. remstr is optional and, if not specified, spaces are removed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[TRUNCATE]
+declaration=X,D
+category=Numeric Functions
+description=Returns the number X, truncated to D decimal places. If D is 0, the\nresult has no decimal point or fractional part. D can be negative to\ncause D digits left of the decimal point of the value X to become zero.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
+[UCASE]
+declaration=str
+category=String Functions
+description=UCASE() is a synonym for UPPER().\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[UNCOMPRESS]
+declaration=string_to_uncompress
+category=Encryption Functions
+description=Uncompresses a string compressed by the COMPRESS() function. If the\nargument is not a compressed value, the result is NULL. This function\nrequires MySQL to have been compiled with a compression library such as\nzlib. Otherwise, the return value is always NULL.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[UNCOMPRESSED_LENGTH]
+declaration=compressed_string
+category=Encryption Functions
+description=Returns the length that the compressed string had before being\ncompressed.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[UNHEX]
+declaration=str
+category=String Functions
+description=For a string argument str, UNHEX(str) interprets each pair of\ncharacters in the argument as a hexadecimal number and converts it to\nthe byte represented by the number. The return value is a binary\nstring.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[UNIX_TIMESTAMP]
+declaration=
+category=Date and Time Functions
+description=If called with no argument, returns a Unix timestamp (seconds since\n'1970-01-01 00:00:00' UTC) as an unsigned integer. If UNIX_TIMESTAMP()\nis called with a date argument, it returns the value of the argument as\nseconds since '1970-01-01 00:00:00' UTC. date may be a DATE string, a\nDATETIME string, a TIMESTAMP, or a number in the format YYMMDD or\nYYYYMMDD. The server interprets date as a value in the current time\nzone and converts it to an internal value in UTC. Clients can set their\ntime zone as described in\nhttp://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[UPDATEXML]
+declaration=xml_target, xpath_expr, new_xml
+category=String Functions
+description=This function replaces a single portion of a given fragment of XML\nmarkup xml_target with a new XML fragment new_xml, and then returns the\nchanged XML. The portion of xml_target that is replaced matches an\nXPath expression xpath_expr supplied by the user.\n\nIf no expression matching xpath_expr is found, or if multiple matches\nare found, the function returns the original xml_target XML fragment.\nAll three arguments should be strings.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/xml-functions.html
+[UPPER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to uppercase\naccording to the current character set mapping. The default is latin1\n(cp1252 West European).\n\nmysql> SELECT UPPER('Hej');\n -> 'HEJ'\n\nSee the description of LOWER() for information that also applies to\nUPPER(). This included information about how to perform lettercase\nconversion of binary strings (BINARY, VARBINARY, BLOB) for which these\nfunctions are ineffective, and information about case folding for\nUnicode character sets.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-functions.html
+[USER]
+declaration=
+category=Information Functions
+description=Returns the current MySQL user name and host name as a string in the\nutf8 character set.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[UTC_DATE]
+declaration=
+category=Date and Time Functions
+description=Returns the current UTC date as a value in 'YYYY-MM-DD' or YYYYMMDD\nformat, depending on whether the function is used in a string or\nnumeric context.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[UTC_TIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current UTC time as a value in 'HH:MM:SS' or HHMMSS format,\ndepending on whether the function is used in a string or numeric\ncontext.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[UTC_TIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current UTC date and time as a value in 'YYYY-MM-DD\nHH:MM:SS' or YYYYMMDDHHMMSS format, depending on whether the function\nis used in a string or numeric context.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[UUID]
+declaration=
+category=Miscellaneous Functions
+description=Returns a Universal Unique Identifier (UUID) generated according to\n"DCE 1.1: Remote Procedure Call" (Appendix A) CAE (Common Applications\nEnvironment) Specifications published by The Open Group in October 1997\n(Document Number C706,\nhttp://www.opengroup.org/public/pubs/catalog/c706.htm).\n\nA UUID is designed as a number that is globally unique in space and\ntime. Two calls to UUID() are expected to generate two different\nvalues, even if these calls are performed on two separate computers\nthat are not connected to each other.\n\nA UUID is a 128-bit number represented by a utf8 string of five\nhexadecimal numbers in aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee format:\n\no The first three numbers are generated from a timestamp.\n\no The fourth number preserves temporal uniqueness in case the timestamp\n value loses monotonicity (for example, due to daylight saving time).\n\no The fifth number is an IEEE 802 node number that provides spatial\n uniqueness. A random number is substituted if the latter is not\n available (for example, because the host computer has no Ethernet\n card, or we do not know how to find the hardware address of an\n interface on your operating system). In this case, spatial uniqueness\n cannot be guaranteed. Nevertheless, a collision should have very low\n probability.\n\n Currently, the MAC address of an interface is taken into account only\n on FreeBSD and Linux. On other operating systems, MySQL uses a\n randomly generated 48-bit number.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[UUID_SHORT]
+declaration=
+category=Miscellaneous Functions
+description=Returns a "short" universal identifier as a 64-bit unsigned integer\n(rather than a string-form 128-bit identifier as returned by the UUID()\nfunction).\n\nThe value of UUID_SHORT() is guaranteed to be unique if the following\nconditions hold:\n\no The server_id of the current host is unique among your set of master\n and slave servers\n\no server_id is between 0 and 255\n\no You do not set back your system time for your server between mysqld\n restarts\n\no You do not invoke UUID_SHORT() on average more than 16 million times\n per second between mysqld restarts\n\nThe UUID_SHORT() return value is constructed this way:\n\n (server_id & 255) << 56\n+ (server_startup_time_in_seconds << 24)\n+ incremented_variable++;\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[VALIDATE_PASSWORD_STRENGTH]
+declaration=str
+category=Encryption Functions
+description=Given an argument representing a cleartext password, this function\nreturns an integer to indicate how strong the password is. The return\nvalue ranges from 0 (weak) to 100 (strong).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html
+[VALUES]
+declaration=col_name
+category=Miscellaneous Functions
+description=In an INSERT ... ON DUPLICATE KEY UPDATE statement, you can use the\nVALUES(col_name) function in the UPDATE clause to refer to column\nvalues from the INSERT portion of the statement. In other words,\nVALUES(col_name) in the UPDATE clause refers to the value of col_name\nthat would be inserted, had no duplicate-key conflict occurred. This\nfunction is especially useful in multiple-row inserts. The VALUES()\nfunction is meaningful only in the ON DUPLICATE KEY UPDATE clause of\nINSERT statements and returns NULL otherwise. See\nhttp://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
+[VARBINARY]
+declaration=M
+category=Data Types
+description=The VARBINARY type is similar to the VARCHAR type, but stores binary\nbyte strings rather than nonbinary character strings. M represents the\nmaximum column length in bytes.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[VARCHAR]
+declaration=M
+category=Data Types
+description=collation_name]\n\nA variable-length string. M represents the maximum column length in\ncharacters. The range of M is 0 to 65,535. The effective maximum length\nof a VARCHAR is subject to the maximum row size (65,535 bytes, which is\nshared among all columns) and the character set used. For example, utf8\ncharacters can require up to three bytes per character, so a VARCHAR\ncolumn that uses the utf8 character set can be declared to be a maximum\nof 21,844 characters. See\nhttp://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html.\n\nMySQL stores VARCHAR values as a 1-byte or 2-byte length prefix plus\ndata. The length prefix indicates the number of bytes in the value. A\nVARCHAR column uses one length byte if values require no more than 255\nbytes, two length bytes if values may require more than 255 bytes.\n\n*Note*: MySQL 5.7 follows the standard SQL specification, and does not\nremove trailing spaces from VARCHAR values.\n\nVARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the\nstandard SQL way to define that a VARCHAR column should use some\npredefined character set. MySQL 4.1 and up uses utf8 as this predefined\ncharacter set.\nhttp://dev.mysql.com/doc/refman/5.7/en/charset-national.html. NVARCHAR\nis shorthand for NATIONAL VARCHAR.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
+[VARIANCE]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard variance of expr. This is an extension\nto standard SQL. The standard SQL function VAR_POP() can be used\ninstead.\n\nVARIANCE() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[VAR_POP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the population standard variance of expr. It considers rows as\nthe whole population, not as a sample, so it has the number of rows as\nthe denominator. You can also use VARIANCE(), which is equivalent but\nis not standard SQL.\n\nVAR_POP() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[VAR_SAMP]
+declaration=expr
+category=Functions and Modifiers for Use with GROUP BY
+description=Returns the sample variance of expr. That is, the denominator is the\nnumber of rows minus one.\n\nVAR_SAMP() returns NULL if there were no matching rows.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
+[VERSION]
+declaration=
+category=Information Functions
+description=Returns a string that indicates the MySQL server version. The string\nuses the utf8 character set. The value might have a suffix in addition\nto the version number. See the description of the version system\nvariable in\nhttp://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
+[WAIT_FOR_EXECUTED_GTID_SET]
+declaration=gtid_set[, timeout]
+category=MBR
+description=Introduced in MySQL 5.7.5, WAIT_FOR_EXECUTED_GTID_SET() is similar to\nWAIT_UNTIL_SQL_THREAD_AFTER_GTIDS() in that it waits until a server has\nexecuted all of the transactions whose global transaction identifiers\nare contained in gtid_set, or until timeout seconds have elapsed,\nwhichever occurs first. Unlike WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(),\nWAIT_FOR_EXECUTED_GTID_SET() does not take into account whether the\nslave is running or not, and an error is returned if GTID-based\nreplication is not enabled.\n\nIn addition, WAIT_FOR_EXECUTED_GTID_SET() returns only the state of the\nquery, where 0 represents success, 1 represents timeout, and any other\nfailures return the error message.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gtid-functions.html
+[WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS]
+declaration=gtid_set[, timeout][,channel]
+category=MBR
+description=Wait until the slave SQL thread has executed all of the transactions\nwhose global transaction identifiers are contained in gtid_set (see\nhttp://dev.mysql.com/doc/refman/5.7/en/replication-gtids-concepts.html,\nfor a definition of "GTID sets"), or until timeout seconds have\nelapsed, whichever occurs first. timeout is optional; the default\ntimeout is 0 seconds, in which case the function waits until all of the\ntransactions in the GTID set have been executed.\n\nFor more information, see\nhttp://dev.mysql.com/doc/refman/5.7/en/replication-gtids.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gtid-functions.html
+[WEEK]
+declaration=date[,mode]
+category=Date and Time Functions
+description=This function returns the week number for date. The two-argument form\nof WEEK() enables you to specify whether the week starts on Sunday or\nMonday and whether the return value should be in the range from 0 to 53\nor from 1 to 53. If the mode argument is omitted, the value of the\ndefault_week_format system variable is used. See\nhttp://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[WEEKDAY]
+declaration=date
+category=Date and Time Functions
+description=Returns the weekday index for date (0 = Monday, 1 = Tuesday, ... 6 =\nSunday).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[WEEKOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the calendar week of the date as a number in the range from 1\nto 53. WEEKOFYEAR() is a compatibility function that is equivalent to\nWEEK(date,3).\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[WEIGHT_STRING]
+declaration=str [AS {CHAR|BINARY}(N
+category=String Functions
+description=levels: N [ASC|DESC|REVERSE] [, N [ASC|DESC|REVERSE]] ...\n\nThis function returns the weight string for the input string. The\nreturn value is a binary string that represents the sorting and\ncomparison value of the string. It has these properties:\n\no If WEIGHT_STRING(str1) = WEIGHT_STRING(str2), then str1 = str2 (str1\n and str2 are considered equal)\n\no If WEIGHT_STRING(str1) < WEIGHT_STRING(str2), then str1 < str2 (str1\n sorts before str2)\n\nWEIGHT_STRING() can be used for testing and debugging of collations,\nespecially if you are adding a new collation. See\nhttp://dev.mysql.com/doc/refman/5.7/en/adding-collation.html.\n\nThe input string, str, is a string expression. If the input is a\nnonbinary (character) string such as a CHAR, VARCHAR, or TEXT value,\nthe return value contains the collation weights for the string. If the\ninput is a binary (byte) string such as a BINARY, VARBINARY, or BLOB\nvalue, the return value is the same as the input (the weight for each\nbyte in a binary string is the byte value). If the input is NULL,\nWEIGHT_STRING() returns NULL.\n\nExamples:\n\nmysql> SET @s = _latin1 'AB' COLLATE latin1_swedish_ci;\nmysql> SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));\n+------+---------+------------------------+\n| @s | HEX(@s) | HEX(WEIGHT_STRING(@s)) |\n+------+---------+------------------------+\n| AB | 4142 | 4142 |\n+------+---------+------------------------+\n\nmysql> SET @s = _latin1 'ab' COLLATE latin1_swedish_ci;\nmysql> SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));\n+------+---------+------------------------+\n| @s | HEX(@s) | HEX(WEIGHT_STRING(@s)) |\n+------+---------+------------------------+\n| ab | 6162 | 4142 |\n+------+---------+------------------------+\n\nmysql> SET @s = CAST('AB' AS BINARY);\nmysql> SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));\n+------+---------+------------------------+\n| @s | HEX(@s) | HEX(WEIGHT_STRING(@s)) |\n+------+---------+------------------------+\n| AB | 4142 | 4142 |\n+------+---------+------------------------+\n\n ...
+[WITHIN]
+declaration=g1,g2
+category=Geometry relations
+description=Returns 1 or 0 to indicate whether g1 is spatially within g2. This\ntests the opposite relationship as Contains().\n\nThis function is deprecated as of MySQL 5.7.6 and will be removed in a\nfuture MySQL release. Use MBRWithin() instead.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/spatial-relation-functions-mbr.html
+[X]
+declaration=p
+category=Point properties
+description=Returns the X-coordinate value for the Point object p as a\ndouble-precision number.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-point-property-functions.html
+[Y]
+declaration=p
+category=Point properties
+description=Returns the Y-coordinate value for the Point object p as a\ndouble-precision number.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/gis-point-property-functions.html
+[YEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the year for date, in the range 1000 to 9999, or 0 for the\n"zero" date.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
+[YEARWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns year and week for a date. The mode argument works exactly like\nthe mode argument to WEEK(). The year in the result may be different\nfrom the year in the date argument for the first and the last week of\nthe year.\n\nURL: http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
\ No newline at end of file
diff --git a/out/functions-mysql8.ini b/out/functions-mysql8.ini
new file mode 100644
index 00000000..1fbca778
--- /dev/null
+++ b/out/functions-mysql8.ini
@@ -0,0 +1,1572 @@
+[ABS]
+declaration=X
+category=Numeric Functions
+description=Returns the absolute value of X, or NULL if X is NULL.\n\nThe result type is derived from the argument type. An implication of\nthis is that ABS(-9223372036854775808) produces an error because the\nresult cannot be stored in a signed BIGINT value.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[ACOS]
+declaration=X
+category=Numeric Functions
+description=Returns the arc cosine of X, that is, the value whose cosine is X.\nReturns NULL if X is not in the range -1 to 1, or if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[ADDDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, ADDDATE()\nis a synonym for DATE_ADD(). The related function SUBDATE() is a\nsynonym for DATE_SUB(). For information on the INTERVAL unit argument,\nsee\nhttps://dev.mysql.com/doc/refman/8.3/en/expressions.html#temporal-inter\nvals.\n\nmysql> SELECT DATE_ADD('2008-01-02', INTERVAL 31 DAY);\n -> '2008-02-02'\nmysql> SELECT ADDDATE('2008-01-02', INTERVAL 31 DAY);\n -> '2008-02-02'\n\nWhen invoked with the days form of the second argument, MySQL treats it\nas an integer number of days to be added to expr.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[ADDTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=ADDTIME() adds expr2 to expr1 and returns the result. expr1 is a time\nor datetime expression, and expr2 is a time expression. Returns NULL if\nexpr1or expr2 is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[AES_DECRYPT]
+declaration=crypt_str,key_str[,init_vector][,kdf_name][,salt][,info | iterations]
+category=Encryption Functions
+description=This function decrypts data using the official AES (Advanced Encryption\nStandard) algorithm. For more information, see the description of\nAES_ENCRYPT().\n\nStatements that use AES_DECRYPT() are unsafe for statement-based\nreplication.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[AES_ENCRYPT]
+declaration=str,key_str[,init_vector][,kdf_name][,salt][,info | iterations]
+category=Encryption Functions
+description=AES_ENCRYPT() and AES_DECRYPT() implement encryption and decryption of\ndata using the official AES (Advanced Encryption Standard) algorithm,\npreviously known as "Rijndael." The AES standard permits various key\nlengths. By default these functions implement AES with a 128-bit key\nlength. Key lengths of 196 or 256 bits can be used, as described later.\nThe key length is a trade off between performance and security.\n\nAES_ENCRYPT() encrypts the string str using the key string key_str, and\nreturns a binary string containing the encrypted output. AES_DECRYPT()\ndecrypts the encrypted string crypt_str using the key string key_str,\nand returns the original (binary) string in hexadecimal format. (To\nobtain the string as plaintext, cast the result to CHAR. Alternatively,\nstart the mysql client with --skip-binary-as-hex to cause all binary\nvalues to be displayed as text.) If either function argument is NULL,\nthe function returns NULL. If AES_DECRYPT() detects invalid data or\nincorrect padding, it returns NULL. However, it is possible for\nAES_DECRYPT() to return a non-NULL value (possibly garbage) if the\ninput data or the key is invalid.\n\nThese functions support the use of a key derivation function (KDF) to\ncreate a cryptographically strong secret key from the information\npassed in key_str. The derived key is used to encrypt and decrypt the\ndata, and it remains in the MySQL Server instance and is not accessible\nto users. Using a KDF is highly recommended, as it provides better\nsecurity than specifying your own premade key or deriving it by a\nsimpler method as you use the function. The functions support HKDF\n(available from OpenSSL 1.1.0), for which you can specify an optional\nsalt and context-specific information to include in the keying\nmaterial, and PBKDF2 (available from OpenSSL 1.0.2), for which you can\nspecify an optional salt and set the number of iterations used to\nproduce the key.\n\nAES_ENCRYPT() and AES_DECRYPT() permit control of the block encryption\nmode. The block_encryption_mode system variable controls the mode for\nblock-based encryption algorithms. Its default value is aes-128-ecb,\nwhich signifies encryption using a key length of 128 bits and ECB mode.\nFor a description of the permitted values of this variable, see\nhttps://dev.mysql.com/doc/refman/8.3/en/server-system-variables.html.\nThe optional init_vector argument is used to provide an initialization\nvector for block encryption modes that require it.\n\nStatements that use AES_ENCRYPT() or AES_DECRYPT() are unsafe for\nstatement-based replication.\n\nIf AES_ENCRYPT() is invoked from within the mysql client, binary\nstrings display using hexadecimal notation, depending on the value of\nthe --binary-as-hex. For more information about that option, see\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql.html.\n\nThe arguments for the AES_ENCRYPT() and AES_DECRYPT() functions are as\n ...
+[ANY_VALUE]
+declaration=arg
+category=Miscellaneous Functions
+description=This function is useful for GROUP BY queries when the\nONLY_FULL_GROUP_BY SQL mode is enabled, for cases when MySQL rejects a\nquery that you know is valid for reasons that MySQL cannot determine.\nThe function return value and type are the same as the return value and\ntype of its argument, but the function result is not checked for the\nONLY_FULL_GROUP_BY SQL mode.\n\nFor example, if name is a nonindexed column, the following query fails\nwith ONLY_FULL_GROUP_BY enabled:\n\nmysql> SELECT name, address, MAX(age) FROM t GROUP BY name;\nERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP\nBY clause and contains nonaggregated column 'mydb.t.address' which\nis not functionally dependent on columns in GROUP BY clause; this\nis incompatible with sql_mode=only_full_group_by\n\nThe failure occurs because address is a nonaggregated column that is\nneither named among GROUP BY columns nor functionally dependent on\nthem. As a result, the address value for rows within each name group is\nnondeterministic. There are multiple ways to cause MySQL to accept the\nquery:\n\no Alter the table to make name a primary key or a unique NOT NULL\n column. This enables MySQL to determine that address is functionally\n dependent on name; that is, address is uniquely determined by name.\n (This technique is inapplicable if NULL must be permitted as a valid\n name value.)\n\no Use ANY_VALUE() to refer to address:\n\nSELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;\n\n In this case, MySQL ignores the nondeterminism of address values\n within each name group and accepts the query. This may be useful if\n you simply do not care which value of a nonaggregated column is\n chosen for each group. ANY_VALUE() is not an aggregate function,\n unlike functions such as SUM() or COUNT(). It simply acts to suppress\n the test for nondeterminism.\n\no Disable ONLY_FULL_GROUP_BY. This is equivalent to using ANY_VALUE()\n with ONLY_FULL_GROUP_BY enabled, as described in the previous item.\n\nANY_VALUE() is also useful if functional dependence exists between\ncolumns but MySQL cannot determine it. The following query is valid\nbecause age is functionally dependent on the grouping column age-1, but\nMySQL cannot tell that and rejects the query with ONLY_FULL_GROUP_BY\nenabled:\n\nSELECT age FROM t GROUP BY age-1;\n\n ...
+[ASCII]
+declaration=str
+category=String Functions
+description=Returns the numeric value of the leftmost character of the string str.\nReturns 0 if str is the empty string. Returns NULL if str is NULL.\nASCII() works for 8-bit characters.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[ASIN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc sine of X, that is, the value whose sine is X. Returns\nNULL if X is not in the range -1 to 1, or if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[ASYMMETRIC_DECRYPT]
+declaration=algorithm, data_str, priv_key_str
+category=Enterprise Encryption Functions
+description=Decrypts an encrypted string using the given algorithm and key string,\nand returns the resulting plaintext as a binary string. If decryption\nfails, the result is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nBy default, the component_enterprise_encryption function assumes that\nencrypted text uses the RSAES-OAEP padding scheme. The function\nsupports decryption for content encrypted by the legacy openssl_udf\nshared library functions if the system variable\nenterprise_encryption.rsa_support_legacy_padding is set to ON (the\ndefault is OFF). When ON is set, the function also supports the\nRSAES-PKCS1-v1_5 padding scheme, as used by the legacy openssl_udf\nshared library functions. When OFF is set, content encrypted by the\nlegacy functions cannot be decrypted, and the function returns null\noutput for such content.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\ndata_str is the encrypted string to decrypt, which was encrypted with\nasymmetric_encrypt().\n\npriv_key_str is a valid PEM encoded RSA private key. For successful\ndecryption, the key string must correspond to the public key string\nused with asymmetric_encrypt() to produce the encrypted string. The\nasymmetric_encrypt() component function only supports encryption using\na public key, so decryption takes place with the corresponding private\nkey.\n\nFor a usage example, see the description of asymmetric_encrypt().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[ASYMMETRIC_DERIVE]
+declaration=pub_key_str, priv_key_str
+category=Enterprise Encryption Functions
+description=Derives a symmetric key using the private key of one party and the\npublic key of another, and returns the resulting key as a binary\nstring. If key derivation fails, the result is NULL.\n\npub_key_str and priv_key_str are valid PEM encoded key strings that\nwere created using the DH algorithm.\n\nSuppose that you have two pairs of public and private keys:\n\nSET @dhp = create_dh_parameters(1024);\nSET @priv1 = create_asymmetric_priv_key('DH', @dhp);\nSET @pub1 = create_asymmetric_pub_key('DH', @priv1);\nSET @priv2 = create_asymmetric_priv_key('DH', @dhp);\nSET @pub2 = create_asymmetric_pub_key('DH', @priv2);\n\nSuppose further that you use the private key from one pair and the\npublic key from the other pair to create a symmetric key string. Then\nthis symmetric key identity relationship holds:\n\nasymmetric_derive(@pub1, @priv2) = asymmetric_derive(@pub2, @priv1)\n\nThis example requires DH private/public keys as inputs, created using a\nshared symmetric secret. Create the secret by passing the key length to\ncreate_dh_parameters(), then pass the secret as the "key length" to\ncreate_asymmetric_priv_key().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions-legacy.html
+[ASYMMETRIC_ENCRYPT]
+declaration=algorithm, data_str, pub_key_str
+category=Enterprise Encryption Functions
+description=Encrypts a string using the given algorithm and key string, and returns\nthe resulting ciphertext as a binary string. If encryption fails, the\nresult is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\ndata_str is the string to encrypt. The length of this string cannot be\ngreater than the key string length in bytes, minus 42 (to account for\nthe padding).\n\npub_key_str is a valid PEM encoded RSA public key. The\nasymmetric_encrypt() component function only supports encryption using\na public key.\n\nTo recover the original unencrypted string, pass the encrypted string\nto asymmetric_decrypt(), along with the other part of the key pair used\nfor encryption, as in the following example:\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[ASYMMETRIC_SIGN]
+declaration=algorithm, text, priv_key_str, digest_type
+category=Enterprise Encryption Functions
+description=Signs a digest string or data string using a private key, and returns\nthe signature as a binary string. If signing fails, the result is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\ntext is a data string or digest string. The function accepts digests\nbut does not require them, as it is also capable of handling data\nstrings of an arbitrary length. A digest string can be generated by\ncalling create_digest().\n\npriv_key_str is the private key string to use for signing the digest\nstring. It must be a valid PEM encoded RSA private key.\n\ndigest_type is the algorithm to be used to sign the data. The supported\ndigest_type values are 'SHA224', 'SHA256', 'SHA384', and 'SHA512' when\nOpenSSL 1.0.1 is in use. If OpenSSL 1.1.1 is in use, the additional\ndigest_type values 'SHA3-224', 'SHA3-256', 'SHA3-384', and 'SHA3-512'\nare available.\n\nFor a usage example, see the description of asymmetric_verify().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[ASYMMETRIC_VERIFY]
+declaration=algorithm, text, sig_str, pub_key_str, digest_type
+category=Enterprise Encryption Functions
+description=Verifies whether the signature string matches the digest string, and\nreturns 1 or 0 to indicate whether verification succeeded or failed. If\nverification fails, the result is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nBy default, the component_enterprise_encryption function assumes that\nsignatures use the RSASSA-PSS signature scheme. The function supports\nverification for signatures produced by the legacy openssl_udf shared\nlibrary functions if the system variable\nenterprise_encryption.rsa_support_legacy_padding is set to ON (the\ndefault is OFF). When ON is set, the function also supports the\nRSASSA-PKCS1-v1_5 signature scheme, as used by the legacy openssl_udf\nshared library functions. When OFF is set, signatures produced by the\nlegacy functions cannot be verified, and the function returns null\noutput for such content.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\ntext is a data string or digest string. The component function accepts\ndigests but does not require them, as it is also capable of handling\ndata strings of an arbitrary length. A digest string can be generated\nby calling create_digest().\n\nsig_str is the signature string to be verified. A signature string can\nbe generated by calling asymmetric_sign().\n\npub_key_str is the public key string of the signer. It corresponds to\nthe private key passed to asymmetric_sign() to generate the signature\nstring. It must be a valid PEM encoded RSA public key.\n\ndigest_type is the algorithm that was used to sign the data. The\nsupported digest_type values are 'SHA224', 'SHA256', 'SHA384', and\n'SHA512' when OpenSSL 1.0.1 is in use. If OpenSSL 1.1.1 is in use, the\nadditional digest_type values 'SHA3-224', 'SHA3-256', 'SHA3-384', and\n'SHA3-512' are available.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[ATAN]
+declaration=X
+category=Numeric Functions
+description=Returns the arc tangent of X, that is, the value whose tangent is X.\nReturns NULL if X is NULL\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[ATAN2]
+declaration=Y,X
+category=Numeric Functions
+description=Returns the arc tangent of the two variables X and Y. It is similar to\ncalculating the arc tangent of Y / X, except that the signs of both\narguments are used to determine the quadrant of the result. Returns\nNULL if X or Y is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[AVG]
+declaration=[DISTINCT] expr
+category=Aggregate Functions and Modifiers
+description=Returns the average value of expr. The DISTINCT option can be used to\nreturn the average of the distinct values of expr.\n\nIf there are no matching rows, AVG() returns NULL. The function also\nreturns NULL if expr is NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html; it\ncannot be used with DISTINCT.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[BENCHMARK]
+declaration=count,expr
+category=Information Functions
+description=The BENCHMARK() function executes the expression expr repeatedly count\ntimes. It may be used to time how quickly MySQL processes the\nexpression. The result value is 0, or NULL for inappropriate arguments\nsuch as a NULL or negative repeat count.\n\nThe intended use is from within the mysql client, which reports query\nexecution times:\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[BIGINT]
+declaration=M
+category=Data Types
+description=A large integer. The signed range is -9223372036854775808 to\n9223372036854775807. The unsigned range is 0 to 18446744073709551615.\n\nSERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[BIN]
+declaration=N
+category=String Functions
+description=Returns a string representation of the binary value of N, where N is a\nlonglong (BIGINT) number. This is equivalent to CONV(N,10,2). Returns\nNULL if N is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[BINARY]
+declaration=M
+category=Data Types
+description=The BINARY type is similar to the CHAR type, but stores binary byte\nstrings rather than nonbinary character strings. An optional length M\nrepresents the column length in bytes. If omitted, M defaults to 1.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[BIN_TO_UUID]
+declaration=binary_uuid
+category=Miscellaneous Functions
+description=BIN_TO_UUID() is the inverse of UUID_TO_BIN(). It converts a binary\nUUID to a string UUID and returns the result. The binary value should\nbe a UUID as a VARBINARY(16) value. The return value is a string of\nfive hexadecimal numbers separated by dashes. (For details about this\nformat, see the UUID() function description.) If the UUID argument is\nNULL, the return value is NULL. If any argument is invalid, an error\noccurs.\n\nBIN_TO_UUID() takes one or two arguments:\n\no The one-argument form takes a binary UUID value. The UUID value is\n assumed not to have its time-low and time-high parts swapped. The\n string result is in the same order as the binary argument.\n\no The two-argument form takes a binary UUID value and a swap-flag\n value:\n\n o If swap_flag is 0, the two-argument form is equivalent to the\n one-argument form. The string result is in the same order as the\n binary argument.\n\n o If swap_flag is 1, the UUID value is assumed to have its time-low\n and time-high parts swapped. These parts are swapped back to their\n original position in the result value.\n\nFor usage examples and information about time-part swapping, see the\nUUID_TO_BIN() function description.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[BIT]
+declaration=M
+category=Data Types
+description=A bit-value type. M indicates the number of bits per value, from 1 to\n64. The default is 1 if M is omitted.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[BIT_AND]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the bitwise AND of all bits in expr.\n\nThe result type depends on whether the function argument values are\nevaluated as binary strings or numbers:\n\no Binary-string evaluation occurs when the argument values have a\n binary string type, and the argument is not a hexadecimal literal,\n bit literal, or NULL literal. Numeric evaluation occurs otherwise,\n with argument value conversion to unsigned 64-bit integers as\n necessary.\n\no Binary-string evaluation produces a binary string of the same length\n as the argument values. If argument values have unequal lengths, an\n ER_INVALID_BITWISE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_operands_size) error occurs. If the\n argument size exceeds 511 bytes, an\n ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_aggregate_operands_size) error occurs.\n Numeric evaluation produces an unsigned 64-bit integer.\n\nIf there are no matching rows, BIT_AND() returns a neutral value (all\nbits set to 1) having the same length as the argument values.\n\nNULL values do not affect the result unless all values are NULL. In\nthat case, the result is a neutral value having the same length as the\nargument values.\n\nFor more information discussion about argument evaluation and result\ntypes, see the introductory discussion in\nhttps://dev.mysql.com/doc/refman/8.3/en/bit-functions.html.\n\nIf BIT_AND() is invoked from within the mysql client, binary string\nresults display using hexadecimal notation, depending on the value of\nthe --binary-as-hex. For more information about that option, see\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql.html.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[BIT_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str in bits. Returns NULL if str is\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[BIT_OR]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the bitwise OR of all bits in expr.\n\nThe result type depends on whether the function argument values are\nevaluated as binary strings or numbers:\n\no Binary-string evaluation occurs when the argument values have a\n binary string type, and the argument is not a hexadecimal literal,\n bit literal, or NULL literal. Numeric evaluation occurs otherwise,\n with argument value conversion to unsigned 64-bit integers as\n necessary.\n\no Binary-string evaluation produces a binary string of the same length\n as the argument values. If argument values have unequal lengths, an\n ER_INVALID_BITWISE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_operands_size) error occurs. If the\n argument size exceeds 511 bytes, an\n ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_aggregate_operands_size) error occurs.\n Numeric evaluation produces an unsigned 64-bit integer.\n\nIf there are no matching rows, BIT_OR() returns a neutral value (all\nbits set to 0) having the same length as the argument values.\n\nNULL values do not affect the result unless all values are NULL. In\nthat case, the result is a neutral value having the same length as the\nargument values.\n\nFor more information discussion about argument evaluation and result\ntypes, see the introductory discussion in\nhttps://dev.mysql.com/doc/refman/8.3/en/bit-functions.html.\n\nIf BIT_OR() is invoked from within the mysql client, binary string\nresults display using hexadecimal notation, depending on the value of\nthe --binary-as-hex. For more information about that option, see\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql.html.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[BIT_XOR]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the bitwise XOR of all bits in expr.\n\nThe result type depends on whether the function argument values are\nevaluated as binary strings or numbers:\n\no Binary-string evaluation occurs when the argument values have a\n binary string type, and the argument is not a hexadecimal literal,\n bit literal, or NULL literal. Numeric evaluation occurs otherwise,\n with argument value conversion to unsigned 64-bit integers as\n necessary.\n\no Binary-string evaluation produces a binary string of the same length\n as the argument values. If argument values have unequal lengths, an\n ER_INVALID_BITWISE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_operands_size) error occurs. If the\n argument size exceeds 511 bytes, an\n ER_INVALID_BITWISE_AGGREGATE_OPERANDS_SIZE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_invalid_bitwise_aggregate_operands_size) error occurs.\n Numeric evaluation produces an unsigned 64-bit integer.\n\nIf there are no matching rows, BIT_XOR() returns a neutral value (all\nbits set to 0) having the same length as the argument values.\n\nNULL values do not affect the result unless all values are NULL. In\nthat case, the result is a neutral value having the same length as the\nargument values.\n\nFor more information discussion about argument evaluation and result\ntypes, see the introductory discussion in\nhttps://dev.mysql.com/doc/refman/8.3/en/bit-functions.html.\n\nIf BIT_XOR() is invoked from within the mysql client, binary string\nresults display using hexadecimal notation, depending on the value of\nthe --binary-as-hex. For more information about that option, see\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql.html.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[BLOB]
+declaration=M
+category=Data Types
+description=A BLOB column with a maximum length of 65,535 (216 − 1) bytes. Each\nBLOB value is stored using a 2-byte length prefix that indicates the\nnumber of bytes in the value.\n\nAn optional length M can be given for this type. If this is done, MySQL\ncreates the column as the smallest BLOB type large enough to hold\nvalues M bytes long.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[CAST]
+declaration=expr AS type [ARRAY]
+category=Cast Functions and Operators
+description=CAST(timestamp_value AT TIME ZONE timezone_specifier AS\nDATETIME[(precision)])\n\ntimezone_specifier: [INTERVAL] '+00:00' | 'UTC'\n\nWith CAST(expr AS type syntax, the CAST() function takes an expression\nof any type and produces a result value of the specified type. This\noperation may also be expressed as CONVERT(expr, type), which is\nequivalent. If expr is NULL, CAST() returns NULL.\n\nThese type values are permitted:\n\no BINARY[(N)]\n\n Produces a string with the VARBINARY data type, except that when the\n expression expr is empty (zero length), the result type is BINARY(0).\n If the optional length N is given, BINARY(N) causes the cast to use\n no more than N bytes of the argument. Values shorter than N bytes are\n padded with 0x00 bytes to a length of N. If the optional length N is\n not given, MySQL calculates the maximum length from the expression.\n If the supplied or calculated length is greater than an internal\n threshold, the result type is BLOB. If the length is still too long,\n the result type is LONGBLOB.\n\n For a description of how casting to BINARY affects comparisons, see\n https://dev.mysql.com/doc/refman/8.3/en/binary-varbinary.html.\n\no CHAR[(N)] [charset_info]\n\n Produces a string with the VARCHAR data type, unless the expression\n expr is empty (zero length), in which case the result type is\n CHAR(0). If the optional length N is given, CHAR(N) causes the cast\n to use no more than N characters of the argument. No padding occurs\n for values shorter than N characters. If the optional length N is not\n given, MySQL calculates the maximum length from the expression. If\n the supplied or calculated length is greater than an internal\n threshold, the result type is TEXT. If the length is still too long,\n the result type is LONGTEXT.\n\n With no charset_info clause, CHAR produces a string with the default\n character set. To specify the character set explicitly, these\n charset_info values are permitted:\n\n o CHARACTER SET charset_name: Produces a string with the given\n character set.\n\n o ASCII: Shorthand for CHARACTER SET latin1.\n\n o UNICODE: Shorthand for CHARACTER SET ucs2.\n\n ...
+[CEIL]
+declaration=X
+category=Numeric Functions
+description=CEIL() is a synonym for CEILING().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[CEILING]
+declaration=X
+category=Numeric Functions
+description=Returns the smallest integer value not less than X. Returns NULL if X\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[CHAR]
+declaration=M
+category=Data Types
+description=collation_name]\n\nA fixed-length string that is always right-padded with spaces to the\nspecified length when stored. M represents the column length in\ncharacters. The range of M is 0 to 255. If M is omitted, the length is\n1.\n\n*Note*:\n\nTrailing spaces are removed when CHAR values are retrieved unless the\nPAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[CHARACTER_LENGTH]
+declaration=str
+category=String Functions
+description=CHARACTER_LENGTH() is a synonym for CHAR_LENGTH().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[CHARSET]
+declaration=str
+category=Information Functions
+description=Returns the character set of the string argument, or NULL if the\nargument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[CHAR_LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str, measured in code points. A\nmultibyte character counts as a single code point. This means that, for\na string containing two 3-byte characters, LENGTH() returns 6, whereas\nCHAR_LENGTH() returns 2, as shown here:\n\nmysql> SET @dolphin:='海豚';\nQuery OK, 0 rows affected (0.01 sec)\n\nmysql> SELECT LENGTH(@dolphin), CHAR_LENGTH(@dolphin);\n+------------------+-----------------------+\n| LENGTH(@dolphin) | CHAR_LENGTH(@dolphin) |\n+------------------+-----------------------+\n| 6 | 2 |\n+------------------+-----------------------+\n1 row in set (0.00 sec)\n\nCHAR_LENGTH() returns NULL if str is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[COALESCE]
+declaration=value,...
+category=Comparison Operators
+description=Returns the first non-NULL value in the list, or NULL if there are no\nnon-NULL values.\n\nThe return type of COALESCE() is the aggregated type of the argument\ntypes.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[COERCIBILITY]
+declaration=str
+category=Information Functions
+description=Returns the collation coercibility value of the string argument.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[COLLATION]
+declaration=str
+category=Information Functions
+description=Returns the collation of the string argument.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[COMPRESS]
+declaration=string_to_compress
+category=Encryption Functions
+description=Compresses a string and returns the result as a binary string. This\nfunction requires MySQL to have been compiled with a compression\nlibrary such as zlib. Otherwise, the return value is always NULL. The\nreturn value is also NULL if string_to_compress is NULL. The compressed\nstring can be uncompressed with UNCOMPRESS().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[CONCAT]
+declaration=str1,str2,...
+category=String Functions
+description=Returns the string that results from concatenating the arguments. May\nhave one or more arguments. If all arguments are nonbinary strings, the\nresult is a nonbinary string. If the arguments include any binary\nstrings, the result is a binary string. A numeric argument is converted\nto its equivalent nonbinary string form.\n\nCONCAT() returns NULL if any argument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[CONCAT_WS]
+declaration=separator,str1,str2,...
+category=String Functions
+description=CONCAT_WS() stands for Concatenate With Separator and is a special form\nof CONCAT(). The first argument is the separator for the rest of the\narguments. The separator is added between the strings to be\nconcatenated. The separator can be a string, as can the rest of the\narguments. If the separator is NULL, the result is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[CONNECTION_ID]
+declaration=
+category=Information Functions
+description=Returns the connection ID (thread ID) for the connection. Every\nconnection has an ID that is unique among the set of currently\nconnected clients.\n\nThe value returned by CONNECTION_ID() is the same type of value as\ndisplayed in the ID column of the Information Schema PROCESSLIST table,\nthe Id column of SHOW PROCESSLIST output, and the PROCESSLIST_ID column\nof the Performance Schema threads table.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[CONV]
+declaration=N,from_base,to_base
+category=Numeric Functions
+description=Converts numbers between different number bases. Returns a string\nrepresentation of the number N, converted from base from_base to base\nto_base. Returns NULL if any argument is NULL. The argument N is\ninterpreted as an integer, but may be specified as an integer or a\nstring. The minimum base is 2 and the maximum base is 36. If from_base\nis a negative number, N is regarded as a signed number. Otherwise, N is\ntreated as unsigned. CONV() works with 64-bit precision.\n\nCONV() returns NULL if any of its arguments are NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[CONVERT]
+declaration=expr USING transcoding_name
+category=Cast Functions and Operators
+description=CONVERT(expr,type)\n\nCONVERT(expr USING transcoding_name) is standard SQL syntax. The\nnon-USING form of CONVERT() is ODBC syntax. Regardless of the syntax\nused, the function returns NULL if expr is NULL.\n\nCONVERT(expr USING transcoding_name) converts data between different\ncharacter sets. In MySQL, transcoding names are the same as the\ncorresponding character set names. For example, this statement converts\nthe string 'abc' in the default character set to the corresponding\nstring in the utf8mb4 character set:\n\nSELECT CONVERT('abc' USING utf8mb4);\n\nCONVERT(expr, type) syntax (without USING) takes an expression and a\ntype value specifying a result type, and produces a result value of the\nspecified type. This operation may also be expressed as CAST(expr AS\ntype), which is equivalent. For more information, see the description\nof CAST().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/cast-functions.html
+[CONVERT_TZ]
+declaration=dt,from_tz,to_tz
+category=Date and Time Functions
+description=CONVERT_TZ() converts a datetime value dt from the time zone given by\nfrom_tz to the time zone given by to_tz and returns the resulting\nvalue. Time zones are specified as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/time-zone-support.html. This\nfunction returns NULL if any of the arguments are invalid, or if any of\nthem are NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[COS]
+declaration=X
+category=Numeric Functions
+description=Returns the cosine of X, where X is given in radians. Returns NULL if X\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[COT]
+declaration=X
+category=Numeric Functions
+description=Returns the cotangent of X. Returns NULL if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[COUNT]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns a count of the number of non-NULL values of expr in the rows\nretrieved by a SELECT statement. The result is a BIGINT value.\n\nIf there are no matching rows, COUNT() returns 0. COUNT(NULL) returns\n0.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[CRC32]
+declaration=expr
+category=Numeric Functions
+description=Computes a cyclic redundancy check value and returns a 32-bit unsigned\nvalue. The result is NULL if the argument is NULL. The argument is\nexpected to be a string and (if possible) is treated as one if it is\nnot.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[CREATE_ASYMMETRIC_PRIV_KEY]
+declaration=algorithm, key_length
+category=Enterprise Encryption Functions
+description=Creates a private key using the given algorithm and key length, and\nreturns the key as a binary string in PEM format. The key is in PKCS #8\nformat. If key generation fails, the result is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\nkey_length is the key length in bits. If you exceed the maximum allowed\nkey length or specify less than the minimum, key generation fails and\nthe result is null output. The minimum allowed key length in bits is\n2048. The maximum allowed key length is the value of the\nenterprise_encryption.maximum_rsa_key_size system variable, which\ndefaults to 4096. It has a maximum setting of 16384, which is the\nmaximum key length allowed for the RSA algorithm. See\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-configuri\nng.html.\n\n*Note*:\n\nGenerating longer keys can consume significant CPU resources. Limiting\nthe key length using the enterprise_encryption.maximum_rsa_key_size\nsystem variable lets you provide adequate security for your\nrequirements while balancing this with resource usage.\n\nThis example creates a 2048-bit RSA private key, then derives a public\nkey from the private key:\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[CREATE_ASYMMETRIC_PUB_KEY]
+declaration=algorithm, priv_key_str
+category=Enterprise Encryption Functions
+description=Derives a public key from the given private key using the given\nalgorithm, and returns the key as a binary string in PEM format. The\nkey is in PKCS #8 format. If key derivation fails, the result is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nalgorithm is the encryption algorithm used to create the key. The\nsupported algorithm value is 'RSA'.\n\npriv_key_str is a valid PEM encoded RSA private key.\n\nFor a usage example, see the description of\ncreate_asymmetric_priv_key().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[CREATE_DH_PARAMETERS]
+declaration=key_len
+category=Enterprise Encryption Functions
+description=Creates a shared secret for generating a DH private/public key pair and\nreturns a binary string that can be passed to\ncreate_asymmetric_priv_key(). If secret generation fails, the result is\nNULL.\n\nkey_len is the key length. The minimum and maximum key lengths in bits\nare 1,024 and 10,000. These key-length limits are constraints imposed\nby OpenSSL. Server administrators can impose additional limits on\nmaximum key length by setting the MYSQL_OPENSSL_UDF_RSA_BITS_THRESHOLD,\nMYSQL_OPENSSL_UDF_DSA_BITS_THRESHOLD, and\nMYSQL_OPENSSL_UDF_DH_BITS_THRESHOLD environment variables. See\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-configuri\nng.html.\n\nFor an example showing how to use the return value for generating\nsymmetric keys, see the description of asymmetric_derive().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions-legacy.html
+[CREATE_DIGEST]
+declaration=digest_type, str
+category=Enterprise Encryption Functions
+description=Creates a digest from the given string using the given digest type, and\nreturns the digest as a binary string. If digest generation fails, the\nresult is NULL.\n\nFor the legacy version of this function in use before MySQL 8.0.29, see\nhttps://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions\n-legacy.html.\n\nThe resulting digest string is suitable for use with asymmetric_sign()\nand asymmetric_verify(). The component versions of these functions\naccept digests but do not require them, as they are capable of handling\ndata of an arbitrary length.\n\ndigest_type is the digest algorithm to be used to generate the digest\nstring. The supported digest_type values are 'SHA224', 'SHA256',\n'SHA384', and 'SHA512' when OpenSSL 1.0.1 is in use. If OpenSSL 1.1.1\nis in use, the additional digest_type values 'SHA3-224', 'SHA3-256',\n'SHA3-384', and 'SHA3-512' are available.\n\nstr is the non-null data string for which the digest is to be\ngenerated.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/enterprise-encryption-functions.html
+[CUME_DIST]
+declaration=
+category=Window Functions
+description=Returns the cumulative distribution of a value within a group of\nvalues; that is, the percentage of partition values less than or equal\nto the value in the current row. This represents the number of rows\npreceding or peer with the current row in the window ordering of the\nwindow partition divided by the total number of rows in the window\npartition. Return values range from 0 to 1.\n\nThis function should be used with ORDER BY to sort partition rows into\nthe desired order. Without ORDER BY, all rows are peers and have value\nN/N = 1, where N is the partition size.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[CURDATE]
+declaration=
+category=Date and Time Functions
+description=Returns the current date as a value in 'YYYY-MM-DD' or YYYYMMDD format,\ndepending on whether the function is used in string or numeric context.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[CURRENT_DATE]
+declaration=
+category=Date and Time Functions
+description=CURRENT_DATE and CURRENT_DATE() are synonyms for CURDATE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[CURRENT_ROLE]
+declaration=
+category=Information Functions
+description=Returns a utf8mb3 string containing the current active roles for the\ncurrent session, separated by commas, or NONE if there are none. The\nvalue reflects the setting of the sql_quote_show_create system\nvariable.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[CURRENT_TIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=CURRENT_TIME and CURRENT_TIME() are synonyms for CURTIME().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[CURRENT_TIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=CURRENT_TIMESTAMP and CURRENT_TIMESTAMP() are synonyms for NOW().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[CURRENT_USER]
+declaration=
+category=Information Functions
+description=Returns the user name and host name combination for the MySQL account\nthat the server used to authenticate the current client. This account\ndetermines your access privileges. The return value is a string in the\nutf8mb3 character set.\n\nThe value of CURRENT_USER() can differ from the value of USER().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[CURTIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current time as a value in 'hh:mm:ss' or hhmmss format,\ndepending on whether the function is used in string or numeric context.\nThe value is expressed in the session time zone.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DATABASE]
+declaration=
+category=Information Functions
+description=Returns the default (current) database name as a string in the utf8mb3\ncharacter set. If there is no default database, DATABASE() returns\nNULL. Within a stored routine, the default database is the database\nthat the routine is associated with, which is not necessarily the same\nas the database that is the default in the calling context.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[DATEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=DATEDIFF() returns expr1 − expr2 expressed as a value in days from\none date to the other. expr1 and expr2 are date or date-and-time\nexpressions. Only the date parts of the values are used in the\ncalculation.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DATETIME]
+declaration=fsp
+category=Data Types
+description=A date and time combination. The supported range is '1000-01-01\n00:00:00.000000' to '9999-12-31 23:59:59.499999'. MySQL displays\nDATETIME values in 'YYYY-MM-DD hh:mm:ss[.fraction]' format, but permits\nassignment of values to DATETIME columns using either strings or\nnumbers.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nAutomatic initialization and updating to the current date and time for\nDATETIME columns can be specified using DEFAULT and ON UPDATE column\ndefinition clauses, as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/timestamp-initialization.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-type-syntax.html
+[DATE_ADD]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=These functions perform date arithmetic. The date argument specifies\nthe starting date or datetime value. expr is an expression specifying\nthe interval value to be added or subtracted from the starting date.\nexpr is evaluated as a string; it may start with a - for negative\nintervals. unit is a keyword indicating the units in which the\nexpression should be interpreted.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DATE_FORMAT]
+declaration=date,format
+category=Date and Time Functions
+description=Formats the date value according to the format string. If either\nargument is NULL, the function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DATE_SUB]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=See the description for DATE_ADD().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DAY]
+declaration=date
+category=Date and Time Functions
+description=DAY() is a synonym for DAYOFMONTH().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DAYNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the name of the weekday for date. The language used for the\nname is controlled by the value of the lc_time_names system variable\n(see https://dev.mysql.com/doc/refman/8.3/en/locale-support.html).\nReturns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DAYOFMONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the month for date, in the range 1 to 31, or 0 for\ndates such as '0000-00-00' or '2008-00-00' that have a zero day part.\nReturns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DAYOFWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns the weekday index for date (1 = Sunday, 2 = Monday, ..., 7 =\nSaturday). These index values correspond to the ODBC standard. Returns\nNULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DAYOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the day of the year for date, in the range 1 to 366. Returns\nNULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[DEC]
+declaration=M[,D]
+category=Data Types
+description=[ZEROFILL], FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]\n\nThese types are synonyms for DECIMAL. The FIXED synonym is available\nfor compatibility with other database systems.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[DECIMAL]
+declaration=M[,D]
+category=Data Types
+description=A packed "exact" fixed-point number. M is the total number of digits\n(the precision) and D is the number of digits after the decimal point\n(the scale). The decimal point and (for negative numbers) the - sign\nare not counted in M. If D is 0, values have no decimal point or\nfractional part. The maximum number of digits (M) for DECIMAL is 65.\nThe maximum number of supported decimals (D) is 30. If D is omitted,\nthe default is 0. If M is omitted, the default is 10. (There is also a\nlimit on how long the text of DECIMAL literals can be; see\nhttps://dev.mysql.com/doc/refman/8.3/en/precision-math-expressions.html\n.)\n\nUNSIGNED, if specified, disallows negative values. The UNSIGNED\nattribute is deprecated for columns of type DECIMAL (and any synonyms);\nyou should expect support for it to be removed in a future version of\nMySQL. Consider using a simple CHECK constraint instead for such\ncolumns.\n\nAll basic calculations (+, -, *, /) with DECIMAL columns are done with\na precision of 65 digits.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[DEFAULT]
+declaration=col_name
+category=Miscellaneous Functions
+description=Returns the default value for a table column. An error results if the\ncolumn has no default value.\n\nThe use of DEFAULT(col_name) to specify the default value for a named\ncolumn is permitted only for columns that have a literal default value,\nnot for columns that have an expression default value.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[DEGREES]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from radians to degrees. Returns NULL\nif X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[DENSE_RANK]
+declaration=
+category=Window Functions
+description=Returns the rank of the current row within its partition, without gaps.\nPeers are considered ties and receive the same rank. This function\nassigns consecutive ranks to peer groups; the result is that groups of\nsize greater than one do not produce noncontiguous rank numbers. For an\nexample, see the RANK() function description.\n\nThis function should be used with ORDER BY to sort partition rows into\nthe desired order. Without ORDER BY, all rows are peers.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[DOUBLE]
+declaration=M,D
+category=Data Types
+description=A normal-size (double-precision) floating-point number. Permissible\nvalues are -1.7976931348623157E+308 to -2.2250738585072014E-308, 0, and\n2.2250738585072014E-308 to 1.7976931348623157E+308. These are the\ntheoretical limits, based on the IEEE standard. The actual range might\nbe slightly smaller depending on your hardware or operating system.\n\nM is the total number of digits and D is the number of digits following\nthe decimal point. If M and D are omitted, values are stored to the\nlimits permitted by the hardware. A double-precision floating-point\nnumber is accurate to approximately 15 decimal places.\n\nDOUBLE(M,D) is a nonstandard MySQL extension; and is deprecated. You\nshould expect support for this syntax to be removed in a future version\nof MySQL.\n\nUNSIGNED, if specified, disallows negative values. The UNSIGNED\nattribute is deprecated for columns of type DOUBLE (and any synonyms)\nand you should expect support for it to be removed in a future version\nof MySQL. Consider using a simple CHECK constraint instead for such\ncolumns.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[ELT]
+declaration=N,str1,str2,str3,...
+category=String Functions
+description=ELT() returns the Nth element of the list of strings: str1 if N = 1,\nstr2 if N = 2, and so on. Returns NULL if N is less than 1, greater\nthan the number of arguments, or NULL. ELT() is the complement of\nFIELD().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[ENUM]
+declaration='value1','value2',...
+category=Data Types
+description=collation_name]\n\nAn enumeration. A string object that can have only one value, chosen\nfrom the list of values 'value1', 'value2', ..., NULL or the special ''\nerror value. ENUM values are represented internally as integers.\n\nAn ENUM column can have a maximum of 65,535 distinct elements.\n\nThe maximum supported length of an individual ENUM element is M <= 255\nand (M x w) <= 1020, where M is the element literal length and w is the\nnumber of bytes required for the maximum-length character in the\ncharacter set.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[EXP]
+declaration=X
+category=Numeric Functions
+description=Returns the value of e (the base of natural logarithms) raised to the\npower of X. The inverse of this function is LOG() (using a single\nargument only) or LN().\n\nIf X is NULL, this function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[EXPORT_SET]
+declaration=bits,on,off[,separator[,number_of_bits]]
+category=String Functions
+description=Returns a string such that for every bit set in the value bits, you get\nan on string and for every bit not set in the value, you get an off\nstring. Bits in bits are examined from right to left (from low-order to\nhigh-order bits). Strings are added to the result from left to right,\nseparated by the separator string (the default being the comma\ncharacter ,). The number of bits examined is given by number_of_bits,\nwhich has a default of 64 if not specified. number_of_bits is silently\nclipped to 64 if larger than 64. It is treated as an unsigned integer,\nso a value of −1 is effectively the same as 64.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[EXTRACT]
+declaration=unit FROM date
+category=Date and Time Functions
+description=The EXTRACT() function uses the same kinds of unit specifiers as\nDATE_ADD() or DATE_SUB(), but extracts parts from the date rather than\nperforming date arithmetic. For information on the unit argument, see\nhttps://dev.mysql.com/doc/refman/8.3/en/expressions.html#temporal-inter\nvals. Returns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[EXTRACTVALUE]
+declaration=xml_frag, xpath_expr
+category=XML
+description=ExtractValue() takes two string arguments, a fragment of XML markup\nxml_frag and an XPath expression xpath_expr (also known as a locator);\nit returns the text (CDATA) of the first text node which is a child of\nthe element or elements matched by the XPath expression.\n\nUsing this function is the equivalent of performing a match using the\nxpath_expr after appending /text(). In other words,\nExtractValue('Sakila', '/a/b') and\nExtractValue('Sakila', '/a/b/text()') produce the same\nresult. If xml_frag or xpath_expr is NULL, the function returns NULL.\n\nIf multiple matches are found, the content of the first child text node\nof each matching element is returned (in the order matched) as a\nsingle, space-delimited string.\n\nIf no matching text node is found for the expression (including the\nimplicit /text())---for whatever reason, as long as xpath_expr is\nvalid, and xml_frag consists of elements which are properly nested and\nclosed---an empty string is returned. No distinction is made between a\nmatch on an empty element and no match at all. This is by design.\n\nIf you need to determine whether no matching element was found in\nxml_frag or such an element was found but contained no child text\nnodes, you should test the result of an expression that uses the XPath\ncount() function. For example, both of these statements return an empty\nstring, as shown here:\n\nmysql> SELECT ExtractValue('', '/a/b');\n+-------------------------------------+\n| ExtractValue('', '/a/b') |\n+-------------------------------------+\n| |\n+-------------------------------------+\n1 row in set (0.00 sec)\n\nmysql> SELECT ExtractValue('', '/a/b');\n+-------------------------------------+\n| ExtractValue('', '/a/b') |\n+-------------------------------------+\n| |\n+-------------------------------------+\n1 row in set (0.00 sec)\n\nHowever, you can determine whether there was actually a matching\nelement using the following:\n\nmysql> SELECT ExtractValue('', 'count(/a/b)');\n+-------------------------------------+\n| ExtractValue('', 'count(/a/b)') |\n+-------------------------------------+\n ...
+[FIELD]
+declaration=str,str1,str2,str3,...
+category=String Functions
+description=Returns the index (position) of str in the str1, str2, str3, ... list.\nReturns 0 if str is not found.\n\nIf all arguments to FIELD() are strings, all arguments are compared as\nstrings. If all arguments are numbers, they are compared as numbers.\nOtherwise, the arguments are compared as double.\n\nIf str is NULL, the return value is 0 because NULL fails equality\ncomparison with any value. FIELD() is the complement of ELT().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[FIND_IN_SET]
+declaration=str,strlist
+category=String Functions
+description=Returns a value in the range of 1 to N if the string str is in the\nstring list strlist consisting of N substrings. A string list is a\nstring composed of substrings separated by , characters. If the first\nargument is a constant string and the second is a column of type SET,\nthe FIND_IN_SET() function is optimized to use bit arithmetic. Returns\n0 if str is not in strlist or if strlist is the empty string. Returns\nNULL if either argument is NULL. This function does not work properly\nif the first argument contains a comma (,) character.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[FIRST_VALUE]
+declaration=expr
+category=Window Functions
+description=Returns the value of expr from the first row of the window frame.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\nnull_treatment is as described in the section introduction.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[FLOAT]
+declaration=M,D
+category=Data Types
+description=A small (single-precision) floating-point number. Permissible values\nare -3.402823466E+38 to -1.175494351E-38, 0, and 1.175494351E-38 to\n3.402823466E+38. These are the theoretical limits, based on the IEEE\nstandard. The actual range might be slightly smaller depending on your\nhardware or operating system.\n\nM is the total number of digits and D is the number of digits following\nthe decimal point. If M and D are omitted, values are stored to the\nlimits permitted by the hardware. A single-precision floating-point\nnumber is accurate to approximately 7 decimal places.\n\nFLOAT(M,D) is a nonstandard MySQL extension. This syntax is deprecated,\nand you should expect support for it to be removed in a future version\nof MySQL.\n\nUNSIGNED, if specified, disallows negative values. The UNSIGNED\nattribute is deprecated for columns of type FLOAT (and any synonyms)\nand you should expect support for it to be removed in a future version\nof MySQL. Consider using a simple CHECK constraint instead for such\ncolumns.\n\nUsing FLOAT might give you some unexpected problems because all\ncalculations in MySQL are done with double precision. See\nhttps://dev.mysql.com/doc/refman/8.3/en/no-matching-rows.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[FLOOR]
+declaration=X
+category=Numeric Functions
+description=Returns the largest integer value not greater than X. Returns NULL if X\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[FORMAT]
+declaration=X,D[,locale]
+category=String Functions
+description=Formats the number X to a format like '#,###,###.##', rounded to D\ndecimal places, and returns the result as a string. If D is 0, the\nresult has no decimal point or fractional part. If X or D is NULL, the\nfunction returns NULL.\n\nThe optional third parameter enables a locale to be specified to be\nused for the result number's decimal point, thousands separator, and\ngrouping between separators. Permissible locale values are the same as\nthe legal values for the lc_time_names system variable (see\nhttps://dev.mysql.com/doc/refman/8.3/en/locale-support.html). If the\nlocale is NULL or not specified, the default locale is 'en_US'.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[FORMAT_BYTES]
+declaration=count
+category=Performance Schema Functions
+description=Given a numeric byte count, converts it to human-readable format and\nreturns a string consisting of a value and a units indicator. The\nstring contains the number of bytes rounded to 2 decimal places and a\nminimum of 3 significant digits. Numbers less than 1024 bytes are\nrepresented as whole numbers and are not rounded. Returns NULL if count\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/performance-schema-functions.html
+[FORMAT_PICO_TIME]
+declaration=time_val
+category=Performance Schema Functions
+description=Given a numeric Performance Schema latency or wait time in picoseconds,\nconverts it to human-readable format and returns a string consisting of\na value and a units indicator. The string contains the decimal time\nrounded to 2 decimal places and a minimum of 3 significant digits.\nTimes under 1 nanosecond are represented as whole numbers and are not\nrounded.\n\nIf time_val is NULL, this function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/performance-schema-functions.html
+[FOUND_ROWS]
+declaration=
+category=Information Functions
+description=*Note*:\n\nThe SQL_CALC_FOUND_ROWS query modifier and accompanying FOUND_ROWS()\nfunction are deprecated; expect them to be removed in a future version\nof MySQL. Execute the query with LIMIT, and then a second query with\nCOUNT(*) and without LIMIT to determine whether there are additional\nrows. For example, instead of these queries:\n\nSELECT SQL_CALC_FOUND_ROWS * FROM tbl_name WHERE id > 100 LIMIT 10;\nSELECT FOUND_ROWS();\n\nUse these queries instead:\n\nSELECT * FROM tbl_name WHERE id > 100 LIMIT 10;\nSELECT COUNT(*) FROM tbl_name WHERE id > 100;\n\nCOUNT(*) is subject to certain optimizations. SQL_CALC_FOUND_ROWS\ncauses some optimizations to be disabled.\n\nA SELECT statement may include a LIMIT clause to restrict the number of\nrows the server returns to the client. In some cases, it is desirable\nto know how many rows the statement would have returned without the\nLIMIT, but without running the statement again. To obtain this row\ncount, include an SQL_CALC_FOUND_ROWS option in the SELECT statement,\nand then invoke FOUND_ROWS() afterward:\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[FROM_BASE64]
+declaration=str
+category=String Functions
+description=Takes a string encoded with the base-64 encoded rules used by\nTO_BASE64() and returns the decoded result as a binary string. The\nresult is NULL if the argument is NULL or not a valid base-64 string.\nSee the description of TO_BASE64() for details about the encoding and\ndecoding rules.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[FROM_DAYS]
+declaration=N
+category=Date and Time Functions
+description=Given a day number N, returns a DATE value. Returns NULL if N is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[FROM_UNIXTIME]
+declaration=unix_timestamp[,format]
+category=Date and Time Functions
+description=Returns a representation of unix_timestamp as a datetime or character\nstring value. The value returned is expressed using the session time\nzone. (Clients can set the session time zone as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/time-zone-support.html.)\nunix_timestamp is an internal timestamp value representing seconds\nsince '1970-01-01 00:00:00' UTC, such as produced by the\nUNIX_TIMESTAMP() function.\n\nIf format is omitted, this function returns a DATETIME value.\n\nIf unix_timestamp or format is NULL, this function returns NULL.\n\nIf unix_timestamp is an integer, the fractional seconds precision of\nthe DATETIME is zero. When unix_timestamp is a decimal value, the\nfractional seconds precision of the DATETIME is the same as the\nprecision of the decimal value, up to a maximum of 6. When\nunix_timestamp is a floating point number, the fractional seconds\nprecision of the datetime is 6.\n\nOn 32-bit platforms, the maximum useful value for unix_timestamp is\n2147483647.999999, which returns '2038-01-19 03:14:07.999999' UTC. On\n64-bit platforms, the effective maximum is 32536771199.999999, which\nreturns '3001-01-18 23:59:59.999999' UTC. Regardless of platform or\nversion, a greater value for unix_timestamp than the effective maximum\nreturns 0.\n\nformat is used to format the result in the same way as the format\nstring used for the DATE_FORMAT() function. If format is supplied, the\nvalue returned is a VARCHAR.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[GEOMCOLLECTION]
+declaration=g [, g] ...
+category=Geometry Constructors
+description=Constructs a GeomCollection value from the geometry arguments.\n\nGeomCollection() returns all the proper geometries contained in the\narguments even if a nonsupported geometry is present.\n\nGeomCollection() with no arguments is permitted as a way to create an\nempty geometry. Also, functions such as ST_GeomFromText() that accept\nWKT geometry collection arguments understand both OpenGIS\n'GEOMETRYCOLLECTION EMPTY' standard syntax and MySQL\n'GEOMETRYCOLLECTION()' nonstandard syntax.\n\nGeomCollection() and GeometryCollection() are synonymous, with\nGeomCollection() the preferred function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[GEOMETRYCOLLECTION]
+declaration=g [, g] ...
+category=Geometry Constructors
+description=Constructs a GeomCollection value from the geometry arguments.\n\nGeometryCollection() returns all the proper geometries contained in the\narguments even if a nonsupported geometry is present.\n\nGeometryCollection() with no arguments is permitted as a way to create\nan empty geometry. Also, functions such as ST_GeomFromText() that\naccept WKT geometry collection arguments understand both OpenGIS\n'GEOMETRYCOLLECTION EMPTY' standard syntax and MySQL\n'GEOMETRYCOLLECTION()' nonstandard syntax.\n\nGeomCollection() and GeometryCollection() are synonymous, with\nGeomCollection() the preferred function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[GET_FORMAT]
+declaration={DATE|TIME|DATETIME}, {'EUR'|'USA'|'JIS'|'ISO'|'INTERNAL'}
+category=Date and Time Functions
+description=Returns a format string. This function is useful in combination with\nthe DATE_FORMAT() and the STR_TO_DATE() functions.\n\nIf format is NULL, this function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[GET_LOCK]
+declaration=str,timeout
+category=Locking Functions
+description=Tries to obtain a lock with a name given by the string str, using a\ntimeout of timeout seconds. A negative timeout value means infinite\ntimeout. The lock is exclusive. While held by one session, other\nsessions cannot obtain a lock of the same name.\n\nReturns 1 if the lock was obtained successfully, 0 if the attempt timed\nout (for example, because another client has previously locked the\nname), or NULL if an error occurred (such as running out of memory or\nthe thread was killed with mysqladmin kill).\n\nA lock obtained with GET_LOCK() is released explicitly by executing\nRELEASE_LOCK() or implicitly when your session terminates (either\nnormally or abnormally). Locks obtained with GET_LOCK() are not\nreleased when transactions commit or roll back.\n\nGET_LOCK() is implemented using the metadata locking (MDL) subsystem.\nMultiple simultaneous locks can be acquired and GET_LOCK() does not\nrelease any existing locks. For example, suppose that you execute these\nstatements:\n\nSELECT GET_LOCK('lock1',10);\nSELECT GET_LOCK('lock2',10);\nSELECT RELEASE_LOCK('lock2');\nSELECT RELEASE_LOCK('lock1');\n\nThe second GET_LOCK() acquires a second lock and both RELEASE_LOCK()\ncalls return 1 (success).\n\nIt is even possible for a given session to acquire multiple locks for\nthe same name. Other sessions cannot acquire a lock with that name\nuntil the acquiring session releases all its locks for the name.\n\nUniquely named locks acquired with GET_LOCK() appear in the Performance\nSchema metadata_locks table. The OBJECT_TYPE column says USER LEVEL\nLOCK and the OBJECT_NAME column indicates the lock name. In the case\nthat multiple locks are acquired for the same name, only the first lock\nfor the name registers a row in the metadata_locks table. Subsequent\nlocks for the name increment a counter in the lock but do not acquire\nadditional metadata locks. The metadata_locks row for the lock is\ndeleted when the last lock instance on the name is released.\n\nThe capability of acquiring multiple locks means there is the\npossibility of deadlock among clients. When this happens, the server\nchooses a caller and terminates its lock-acquisition request with an\nER_USER_LOCK_DEADLOCK\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_user_lock_deadlock) error. This error does not cause\ntransactions to roll back.\n\nMySQL enforces a maximum length on lock names of 64 characters.\n ...
+[GREATEST]
+declaration=value1,value2,...
+category=Comparison Operators
+description=With two or more arguments, returns the largest (maximum-valued)\nargument. The arguments are compared using the same rules as for\nLEAST().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[GROUPING]
+declaration=expr [, expr] ...
+category=Miscellaneous Functions
+description=For GROUP BY queries that include a WITH ROLLUP modifier, the ROLLUP\noperation produces super-aggregate output rows where NULL represents\nthe set of all values. The GROUPING() function enables you to\ndistinguish NULL values for super-aggregate rows from NULL values in\nregular grouped rows.\n\nGROUPING() is permitted in the select list, HAVING clause, and ORDER BY\nclause.\n\nEach argument to GROUPING() must be an expression that exactly matches\nan expression in the GROUP BY clause. The expression cannot be a\npositional specifier. For each expression, GROUPING() produces 1 if the\nexpression value in the current row is a NULL representing a\nsuper-aggregate value. Otherwise, GROUPING() produces 0, indicating\nthat the expression value is a NULL for a regular result row or is not\nNULL.\n\nSuppose that table t1 contains these rows, where NULL indicates\nsomething like "other" or "unknown":\n\nmysql> SELECT * FROM t1;\n+------+-------+----------+\n| name | size | quantity |\n+------+-------+----------+\n| ball | small | 10 |\n| ball | large | 20 |\n| ball | NULL | 5 |\n| hoop | small | 15 |\n| hoop | large | 5 |\n| hoop | NULL | 3 |\n+------+-------+----------+\n\nA summary of the table without WITH ROLLUP looks like this:\n\nmysql> SELECT name, size, SUM(quantity) AS quantity\n FROM t1\n GROUP BY name, size;\n+------+-------+----------+\n| name | size | quantity |\n+------+-------+----------+\n| ball | small | 10 |\n| ball | large | 20 |\n| ball | NULL | 5 |\n| hoop | small | 15 |\n| hoop | large | 5 |\n| hoop | NULL | 3 |\n+------+-------+----------+\n\nThe result contains NULL values, but those do not represent\nsuper-aggregate rows because the query does not include WITH ROLLUP.\n ...
+[GROUP_CONCAT]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=This function returns a string result with the concatenated non-NULL\nvalues from a group. It returns NULL if there are no non-NULL values.\nThe full syntax is as follows:\n\nGROUP_CONCAT([DISTINCT] expr [,expr ...]\n [ORDER BY {unsigned_integer | col_name | expr}\n [ASC | DESC] [,col_name ...]]\n [SEPARATOR str_val])\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[GTID_SUBSET]
+declaration=set1,set2
+category=GTID
+description=Given two sets of global transaction identifiers set1 and set2, returns\ntrue if all GTIDs in set1 are also in set2. Returns NULL if set1 or\nset2 is NULL. Returns false otherwise.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gtid-functions.html
+[GTID_SUBTRACT]
+declaration=set1,set2
+category=GTID
+description=Given two sets of global transaction identifiers set1 and set2, returns\nonly those GTIDs from set1 that are not in set2. Returns NULL if set1\nor set2 is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gtid-functions.html
+[HEX]
+declaration=str
+category=String Functions
+description=For a string argument str, HEX() returns a hexadecimal string\nrepresentation of str where each byte of each character in str is\nconverted to two hexadecimal digits. (Multibyte characters therefore\nbecome more than two digits.) The inverse of this operation is\nperformed by the UNHEX() function.\n\nFor a numeric argument N, HEX() returns a hexadecimal string\nrepresentation of the value of N treated as a longlong (BIGINT) number.\nThis is equivalent to CONV(N,10,16). The inverse of this operation is\nperformed by CONV(HEX(N),16,10).\n\nFor a NULL argument, this function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[HOUR]
+declaration=time
+category=Date and Time Functions
+description=Returns the hour for time. The range of the return value is 0 to 23 for\ntime-of-day values. However, the range of TIME values actually is much\nlarger, so HOUR can return values greater than 23. Returns NULL if time\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[ICU_VERSION]
+declaration=
+category=Information Functions
+description=The version of the International Components for Unicode (ICU) library\nused to support regular expression operations (see\nhttps://dev.mysql.com/doc/refman/8.3/en/regexp.html). This function is\nprimarily intended for use in test cases.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[IFNULL]
+declaration=expr1,expr2
+category=Flow Control Functions
+description=If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns\nexpr2.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/flow-control-functions.html
+[IN]
+declaration=value,...
+category=Comparison Operators
+description=Returns 1 (true) if expr is equal to any of the values in the IN()\nlist, else returns 0 (false).\n\nType conversion takes place according to the rules described in\nhttps://dev.mysql.com/doc/refman/8.3/en/type-conversion.html, applied\nto all the arguments. If no type conversion is needed for the values in\nthe IN() list, they are all non-JSON constants of the same type, and\nexpr can be compared to each of them as a value of the same type\n(possibly after type conversion), an optimization takes place. The\nvalues the list are sorted and the search for expr is done using a\nbinary search, which makes the IN() operation very quick.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[INET6_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address as a string, returns a binary\nstring that represents the numeric value of the address in network byte\norder (big endian). Because numeric-format IPv6 addresses require more\nbytes than the largest integer type, the representation returned by\nthis function has the VARBINARY data type: VARBINARY(16) for IPv6\naddresses and VARBINARY(4) for IPv4 addresses. If the argument is not a\nvalid address, or if it is NULL, INET6_ATON() returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[INET6_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given an IPv6 or IPv4 network address represented in numeric form as a\nbinary string, returns the string representation of the address as a\nstring in the connection character set. If the argument is not a valid\naddress, or if it is NULL, INET6_NTOA() returns NULL.\n\nINET6_NTOA() has these properties:\n\no It does not use operating system functions to perform conversions,\n thus the output string is platform independent.\n\no The return string has a maximum length of 39 (4 x 8 + 7). Given this\n statement:\n\nCREATE TABLE t AS SELECT INET6_NTOA(expr) AS c1;\n\n The resulting table would have this definition:\n\nCREATE TABLE t (c1 VARCHAR(39) CHARACTER SET utf8mb3 DEFAULT NULL);\n\no The return string uses lowercase letters for IPv6 addresses.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[INET_ATON]
+declaration=expr
+category=Miscellaneous Functions
+description=Given the dotted-quad representation of an IPv4 network address as a\nstring, returns an integer that represents the numeric value of the\naddress in network byte order (big endian). INET_ATON() returns NULL if\nit does not understand its argument, or if expr is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[INET_NTOA]
+declaration=expr
+category=Miscellaneous Functions
+description=Given a numeric IPv4 network address in network byte order, returns the\ndotted-quad string representation of the address as a string in the\nconnection character set. INET_NTOA() returns NULL if it does not\nunderstand its argument.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[INSTR]
+declaration=str,substr
+category=String Functions
+description=Returns the position of the first occurrence of substring substr in\nstring str. This is the same as the two-argument form of LOCATE(),\nexcept that the order of the arguments is reversed.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[INT]
+declaration=M
+category=Data Types
+description=A normal-size integer. The signed range is -2147483648 to 2147483647.\nThe unsigned range is 0 to 4294967295.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[INTEGER]
+declaration=M
+category=Data Types
+description=This type is a synonym for INT.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[INTERVAL]
+declaration=N,N1,N2,N3,...
+category=Comparison Operators
+description=Returns 0 if N <= N1, 1 if N <= N2 and so on, or -1 if N is NULL. All\narguments are treated as integers. It is required that N1 <= N2 <= N3\n<= ... <= Nn for this function to work correctly. This is because a\nbinary search is used (very fast).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[ISNULL]
+declaration=expr
+category=Comparison Operators
+description=If expr is NULL, ISNULL() returns 1, otherwise it returns 0.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[IS_FREE_LOCK]
+declaration=str
+category=Locking Functions
+description=Checks whether the lock named str is free to use (that is, not locked).\nReturns 1 if the lock is free (no one is using the lock), 0 if the lock\nis in use, and NULL if an error occurs (such as an incorrect argument).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/locking-functions.html
+[IS_IPV4]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if the argument is a valid IPv4 address specified as a\nstring, 0 otherwise. Returns NULL if expr is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[IS_IPV4_COMPAT]
+declaration=expr
+category=Miscellaneous Functions
+description=This function takes an IPv6 address represented in numeric form as a\nbinary string, as returned by INET6_ATON(). It returns 1 if the\nargument is a valid IPv4-compatible IPv6 address, 0 otherwise (unless\nexpr is NULL, in which case the function returns NULL). IPv4-compatible\naddresses have the form ::ipv4_address.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[IS_IPV4_MAPPED]
+declaration=expr
+category=Miscellaneous Functions
+description=This function takes an IPv6 address represented in numeric form as a\nbinary string, as returned by INET6_ATON(). It returns 1 if the\nargument is a valid IPv4-mapped IPv6 address, 0 otherwise, unless expr\nis NULL, in which case the function returns NULL. IPv4-mapped addresses\nhave the form ::ffff:ipv4_address.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[IS_IPV6]
+declaration=expr
+category=Miscellaneous Functions
+description=Returns 1 if the argument is a valid IPv6 address specified as a\nstring, 0 otherwise, unless expr is NULL, in which case the function\nreturns NULL. This function does not consider IPv4 addresses to be\nvalid IPv6 addresses.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[IS_USED_LOCK]
+declaration=str
+category=Locking Functions
+description=Checks whether the lock named str is in use (that is, locked). If so,\nit returns the connection identifier of the client session that holds\nthe lock. Otherwise, it returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/locking-functions.html
+[IS_UUID]
+declaration=string_uuid
+category=Miscellaneous Functions
+description=Returns 1 if the argument is a valid string-format UUID, 0 if the\nargument is not a valid UUID, and NULL if the argument is NULL.\n\n"Valid" means that the value is in a format that can be parsed. That\nis, it has the correct length and contains only the permitted\ncharacters (hexadecimal digits in any lettercase and, optionally,\ndashes and curly braces). This format is most common:\n\naaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\n\nThese other formats are also permitted:\n\naaaaaaaabbbbccccddddeeeeeeeeeeee\n{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\n\nFor the meanings of fields within the value, see the UUID() function\ndescription.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[JOIN]
+declaration=t2, t3, t4
+category=Data Manipulation
+description=ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)\n\nis equivalent to:\n\nSELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4)\n ON (t2.a = t1.a AND t3.b = t1.b AND t4.c = t1.c)\n\nIn MySQL, JOIN, CROSS JOIN, and INNER JOIN are syntactic equivalents\n(they can replace each other). In standard SQL, they are not\nequivalent. INNER JOIN is used with an ON clause, CROSS JOIN is used\notherwise.\n\nIn general, parentheses can be ignored in join expressions containing\nonly inner join operations. MySQL also supports nested joins. See\nhttps://dev.mysql.com/doc/refman/8.3/en/nested-join-optimization.html.\n\nIndex hints can be specified to affect how the MySQL optimizer makes\nuse of indexes. For more information, see\nhttps://dev.mysql.com/doc/refman/8.3/en/index-hints.html. Optimizer\nhints and the optimizer_switch system variable are other ways to\ninfluence optimizer use of indexes. See\nhttps://dev.mysql.com/doc/refman/8.3/en/optimizer-hints.html, and\nhttps://dev.mysql.com/doc/refman/8.3/en/switchable-optimizations.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/join.html
+[JSON_ARRAY]
+declaration=[val[, val] ...]
+category=MBR Functions
+description=Evaluates a (possibly empty) list of values and returns a JSON array\ncontaining those values.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-creation-functions.html
+[JSON_ARRAYAGG]
+declaration=col_or_expr
+category=Aggregate Functions and Modifiers
+description=Aggregates a result set as a single JSON array whose elements consist\nof the rows. The order of elements in this array is undefined. The\nfunction acts on a column or an expression that evaluates to a single\nvalue. Returns NULL if the result contains no rows, or in the event of\nan error. If col_or_expr is NULL, the function returns an array of JSON\n[null] elements.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[JSON_ARRAY_APPEND]
+declaration=json_doc, path, val[, path, val] ...
+category=MBR Functions
+description=Appends values to the end of the indicated arrays within a JSON\ndocument and returns the result. Returns NULL if any argument is NULL.\nAn error occurs if the json_doc argument is not a valid JSON document\nor any path argument is not a valid path expression or contains a * or\n** wildcard.\n\nThe path-value pairs are evaluated left to right. The document produced\nby evaluating one pair becomes the new value against which the next\npair is evaluated.\n\nIf a path selects a scalar or object value, that value is autowrapped\nwithin an array and the new value is added to that array. Pairs for\nwhich the path does not identify any value in the JSON document are\nignored.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_ARRAY_INSERT]
+declaration=json_doc, path, val[, path, val] ...
+category=MBR Functions
+description=Updates a JSON document, inserting into an array within the document\nand returning the modified document. Returns NULL if any argument is\nNULL. An error occurs if the json_doc argument is not a valid JSON\ndocument or any path argument is not a valid path expression or\ncontains a * or ** wildcard or does not end with an array element\nidentifier.\n\nThe path-value pairs are evaluated left to right. The document produced\nby evaluating one pair becomes the new value against which the next\npair is evaluated.\n\nPairs for which the path does not identify any array in the JSON\ndocument are ignored. If a path identifies an array element, the\ncorresponding value is inserted at that element position, shifting any\nfollowing values to the right. If a path identifies an array position\npast the end of an array, the value is inserted at the end of the\narray.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_CONTAINS]
+declaration=target, candidate[, path]
+category=MBR Functions
+description=Indicates by returning 1 or 0 whether a given candidate JSON document\nis contained within a target JSON document, or---if a path argument was\nsupplied---whether the candidate is found at a specific path within the\ntarget. Returns NULL if any argument is NULL, or if the path argument\ndoes not identify a section of the target document. An error occurs if\ntarget or candidate is not a valid JSON document, or if the path\nargument is not a valid path expression or contains a * or ** wildcard.\n\nTo check only whether any data exists at the path, use\nJSON_CONTAINS_PATH() instead.\n\nThe following rules define containment:\n\no A candidate scalar is contained in a target scalar if and only if\n they are comparable and are equal. Two scalar values are comparable\n if they have the same JSON_TYPE() types, with the exception that\n values of types INTEGER and DECIMAL are also comparable to each\n other.\n\no A candidate array is contained in a target array if and only if every\n element in the candidate is contained in some element of the target.\n\no A candidate nonarray is contained in a target array if and only if\n the candidate is contained in some element of the target.\n\no A candidate object is contained in a target object if and only if for\n each key in the candidate there is a key with the same name in the\n target and the value associated with the candidate key is contained\n in the value associated with the target key.\n\nOtherwise, the candidate value is not contained in the target document.\n\nQueries using JSON_CONTAINS() on InnoDB tables can be optimized using\nmulti-valued indexes; see\nhttps://dev.mysql.com/doc/refman/8.3/en/create-index.html#create-index-\nmulti-valued, for more information.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_CONTAINS_PATH]
+declaration=json_doc, one_or_all, path[, path] ...
+category=MBR Functions
+description=Returns 0 or 1 to indicate whether a JSON document contains data at a\ngiven path or paths. Returns NULL if any argument is NULL. An error\noccurs if the json_doc argument is not a valid JSON document, any path\nargument is not a valid path expression, or one_or_all is not 'one' or\n'all'.\n\nTo check for a specific value at a path, use JSON_CONTAINS() instead.\n\nThe return value is 0 if no specified path exists within the document.\nOtherwise, the return value depends on the one_or_all argument:\n\no 'one': 1 if at least one path exists within the document, 0\n otherwise.\n\no 'all': 1 if all paths exist within the document, 0 otherwise.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_DEPTH]
+declaration=json_doc
+category=MBR Functions
+description=Returns the maximum depth of a JSON document. Returns NULL if the\nargument is NULL. An error occurs if the argument is not a valid JSON\ndocument.\n\nAn empty array, empty object, or scalar value has depth 1. A nonempty\narray containing only elements of depth 1 or nonempty object containing\nonly member values of depth 1 has depth 2. Otherwise, a JSON document\nhas depth greater than 2.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-attribute-functions.html
+[JSON_EXTRACT]
+declaration=json_doc, path[, path] ...
+category=MBR Functions
+description=Returns data from a JSON document, selected from the parts of the\ndocument matched by the path arguments. Returns NULL if any argument is\nNULL or no paths locate a value in the document. An error occurs if the\njson_doc argument is not a valid JSON document or any path argument is\nnot a valid path expression.\n\nThe return value consists of all values matched by the path arguments.\nIf it is possible that those arguments could return multiple values,\nthe matched values are autowrapped as an array, in the order\ncorresponding to the paths that produced them. Otherwise, the return\nvalue is the single matched value.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_INSERT]
+declaration=json_doc, path, val[, path, val] ...
+category=MBR Functions
+description=Inserts data into a JSON document and returns the result. Returns NULL\nif any argument is NULL. An error occurs if the json_doc argument is\nnot a valid JSON document or any path argument is not a valid path\nexpression or contains a * or ** wildcard.\n\nThe path-value pairs are evaluated left to right. The document produced\nby evaluating one pair becomes the new value against which the next\npair is evaluated.\n\nA path-value pair for an existing path in the document is ignored and\ndoes not overwrite the existing document value. A path-value pair for a\nnonexisting path in the document adds the value to the document if the\npath identifies one of these types of values:\n\no A member not present in an existing object. The member is added to\n the object and associated with the new value.\n\no A position past the end of an existing array. The array is extended\n with the new value. If the existing value is not an array, it is\n autowrapped as an array, then extended with the new value.\n\nOtherwise, a path-value pair for a nonexisting path in the document is\nignored and has no effect.\n\nFor a comparison of JSON_INSERT(), JSON_REPLACE(), and JSON_SET(), see\nthe discussion of JSON_SET().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_KEYS]
+declaration=json_doc[, path]
+category=MBR Functions
+description=Returns the keys from the top-level value of a JSON object as a JSON\narray, or, if a path argument is given, the top-level keys from the\nselected path. Returns NULL if any argument is NULL, the json_doc\nargument is not an object, or path, if given, does not locate an\nobject. An error occurs if the json_doc argument is not a valid JSON\ndocument or the path argument is not a valid path expression or\ncontains a * or ** wildcard.\n\nThe result array is empty if the selected object is empty. If the\ntop-level value has nested subobjects, the return value does not\ninclude keys from those subobjects.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_LENGTH]
+declaration=json_doc[, path]
+category=MBR Functions
+description=Returns the length of a JSON document, or, if a path argument is given,\nthe length of the value within the document identified by the path.\nReturns NULL if any argument is NULL or the path argument does not\nidentify a value in the document. An error occurs if the json_doc\nargument is not a valid JSON document or the path argument is not a\nvalid path expression.\n\nThe length of a document is determined as follows:\n\no The length of a scalar is 1.\n\no The length of an array is the number of array elements.\n\no The length of an object is the number of object members.\n\no The length does not count the length of nested arrays or objects.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-attribute-functions.html
+[JSON_MERGE]
+declaration=json_doc, json_doc[, json_doc] ...
+category=MBR Functions
+description=Deprecated synonym for JSON_MERGE_PRESERVE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_OBJECT]
+declaration=[key, val[, key, val] ...]
+category=MBR Functions
+description=Evaluates a (possibly empty) list of key-value pairs and returns a JSON\nobject containing those pairs. An error occurs if any key name is NULL\nor the number of arguments is odd.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-creation-functions.html
+[JSON_OBJECTAGG]
+declaration=key, value
+category=Aggregate Functions and Modifiers
+description=Takes two column names or expressions as arguments, the first of these\nbeing used as a key and the second as a value, and returns a JSON\nobject containing key-value pairs. Returns NULL if the result contains\nno rows, or in the event of an error. An error occurs if any key name\nis NULL or the number of arguments is not equal to 2.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[JSON_OVERLAPS]
+declaration=json_doc1, json_doc2
+category=MBR Functions
+description=Compares two JSON documents. Returns true (1) if the two document have\nany key-value pairs or array elements in common. If both arguments are\nscalars, the function performs a simple equality test. If either\nargument is NULL, the function returns NULL.\n\nThis function serves as counterpart to JSON_CONTAINS(), which requires\nall elements of the array searched for to be present in the array\nsearched in. Thus, JSON_CONTAINS() performs an AND operation on search\nkeys, while JSON_OVERLAPS() performs an OR operation.\n\nQueries on JSON columns of InnoDB tables using JSON_OVERLAPS() in the\nWHERE clause can be optimized using multi-valued indexes.\nhttps://dev.mysql.com/doc/refman/8.3/en/create-index.html#create-index-\nmulti-valued, provides detailed information and examples.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_PRETTY]
+declaration=json_val
+category=MBR Functions
+description=Provides pretty-printing of JSON values similar to that implemented in\nPHP and by other languages and database systems. The value supplied\nmust be a JSON value or a valid string representation of a JSON value.\nExtraneous whitespaces and newlines present in this value have no\neffect on the output. For a NULL value, the function returns NULL. If\nthe value is not a JSON document, or if it cannot be parsed as one, the\nfunction fails with an error.\n\nFormatting of the output from this function adheres to the following\nrules:\n\no Each array element or object member appears on a separate line,\n indented by one additional level as compared to its parent.\n\no Each level of indentation adds two leading spaces.\n\no A comma separating individual array elements or object members is\n printed before the newline that separates the two elements or\n members.\n\no The key and the value of an object member are separated by a colon\n followed by a space (': ').\n\no An empty object or array is printed on a single line. No space is\n printed between the opening and closing brace.\n\no Special characters in string scalars and key names are escaped\n employing the same rules used by the JSON_QUOTE() function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-utility-functions.html
+[JSON_QUOTE]
+declaration=string
+category=MBR Functions
+description=Quotes a string as a JSON value by wrapping it with double quote\ncharacters and escaping interior quote and other characters, then\nreturning the result as a utf8mb4 string. Returns NULL if the argument\nis NULL.\n\nThis function is typically used to produce a valid JSON string literal\nfor inclusion within a JSON document.\n\nCertain special characters are escaped with backslashes per the escape\nsequences shown in\nhttps://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html\n#json-unquote-character-escape-sequences.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-creation-functions.html
+[JSON_REMOVE]
+declaration=json_doc, path[, path] ...
+category=MBR Functions
+description=Removes data from a JSON document and returns the result. Returns NULL\nif any argument is NULL. An error occurs if the json_doc argument is\nnot a valid JSON document or any path argument is not a valid path\nexpression or is $ or contains a * or ** wildcard.\n\nThe path arguments are evaluated left to right. The document produced\nby evaluating one path becomes the new value against which the next\npath is evaluated.\n\nIt is not an error if the element to be removed does not exist in the\ndocument; in that case, the path does not affect the document.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_REPLACE]
+declaration=json_doc, path, val[, path, val] ...
+category=MBR Functions
+description=Replaces existing values in a JSON document and returns the result.\nReturns NULL if any argument is NULL. An error occurs if the json_doc\nargument is not a valid JSON document or any path argument is not a\nvalid path expression or contains a * or ** wildcard.\n\nThe path-value pairs are evaluated left to right. The document produced\nby evaluating one pair becomes the new value against which the next\npair is evaluated.\n\nA path-value pair for an existing path in the document overwrites the\nexisting document value with the new value. A path-value pair for a\nnonexisting path in the document is ignored and has no effect.\n\nThe optimizer can perform a partial, in-place update of a JSON column\ninstead of removing the old document and writing the new document in\nits entirety to the column. This optimization can be performed for an\nupdate statement that uses the JSON_REPLACE() function and meets the\nconditions outlined in\nhttps://dev.mysql.com/doc/refman/8.3/en/json.html#json-partial-updates.\n\nFor a comparison of JSON_INSERT(), JSON_REPLACE(), and JSON_SET(), see\nthe discussion of JSON_SET().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_SCHEMA_VALID]
+declaration=schema,document
+category=MBR Functions
+description=Validates a JSON document against a JSON schema. Both schema and\ndocument are required. The schema must be a valid JSON object; the\ndocument must be a valid JSON document. Provided that these conditions\nare met: If the document validates against the schema, the function\nreturns true (1); otherwise, it returns false (0).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-validation-functions.html
+[JSON_SCHEMA_VALIDATION_REPORT]
+declaration=schema,document
+category=MBR Functions
+description=Validates a JSON document against a JSON schema. Both schema and\ndocument are required. As with JSON_VALID_SCHEMA(), the schema must be\na valid JSON object, and the document must be a valid JSON document.\nProvided that these conditions are met, the function returns a report,\nas a JSON document, on the outcome of the validation. If the JSON\ndocument is considered valid according to the JSON Schema, the function\nreturns a JSON object with one property valid having the value "true".\nIf the JSON document fails validation, the function returns a JSON\nobject which includes the properties listed here:\n\no valid: Always "false" for a failed schema validation\n\no reason: A human-readable string containing the reason for the failure\n\no schema-location: A JSON pointer URI fragment identifier indicating\n where in the JSON schema the validation failed (see Note following\n this list)\n\no document-location: A JSON pointer URI fragment identifier indicating\n where in the JSON document the validation failed (see Note following\n this list)\n\no schema-failed-keyword: A string containing the name of the keyword or\n property in the JSON schema that was violated\n\n*Note*:\n\nJSON pointer URI fragment identifiers are defined in RFC 6901 -\nJavaScript Object Notation (JSON) Pointer\n(https://tools.ietf.org/html/rfc6901#page-5). (These are not the same\nas the JSON path notation used by JSON_EXTRACT() and other MySQL JSON\nfunctions.) In this notation, # represents the entire document, and\n#/myprop represents the portion of the document included in the\ntop-level property named myprop. See the specification just cited and\nthe examples shown later in this section for more information.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-validation-functions.html
+[JSON_SEARCH]
+declaration=json_doc, one_or_all, search_str[, escape_char[, path] ...]
+category=MBR Functions
+description=Returns the path to the given string within a JSON document. Returns\nNULL if any of the json_doc, search_str, or path arguments are NULL; no\npath exists within the document; or search_str is not found. An error\noccurs if the json_doc argument is not a valid JSON document, any path\nargument is not a valid path expression, one_or_all is not 'one' or\n'all', or escape_char is not a constant expression.\n\nThe one_or_all argument affects the search as follows:\n\no 'one': The search terminates after the first match and returns one\n path string. It is undefined which match is considered first.\n\no 'all': The search returns all matching path strings such that no\n duplicate paths are included. If there are multiple strings, they are\n autowrapped as an array. The order of the array elements is\n undefined.\n\nWithin the search_str search string argument, the % and _ characters\nwork as for the LIKE operator: % matches any number of characters\n(including zero characters), and _ matches exactly one character.\n\nTo specify a literal % or _ character in the search string, precede it\nby the escape character. The default is \ if the escape_char argument\nis missing or NULL. Otherwise, escape_char must be a constant that is\nempty or one character.\n\nFor more information about matching and escape character behavior, see\nthe description of LIKE in\nhttps://dev.mysql.com/doc/refman/8.3/en/string-comparison-functions.html\n. For escape character handling, a difference from the LIKE behavior\nis that the escape character for JSON_SEARCH() must evaluate to a\nconstant at compile time, not just at execution time. For example, if\nJSON_SEARCH() is used in a prepared statement and the escape_char\nargument is supplied using a ? parameter, the parameter value might be\nconstant at execution time, but is not at compile time.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-search-functions.html
+[JSON_SET]
+declaration=json_doc, path, val[, path, val] ...
+category=MBR Functions
+description=Inserts or updates data in a JSON document and returns the result.\nReturns NULL if json_doc or path is NULL, or if path, when given, does\nnot locate an object. Otherwise, an error occurs if the json_doc\nargument is not a valid JSON document or any path argument is not a\nvalid path expression or contains a * or ** wildcard.\n\nThe path-value pairs are evaluated left to right. The document produced\nby evaluating one pair becomes the new value against which the next\npair is evaluated.\n\nA path-value pair for an existing path in the document overwrites the\nexisting document value with the new value. A path-value pair for a\nnonexisting path in the document adds the value to the document if the\npath identifies one of these types of values:\n\no A member not present in an existing object. The member is added to\n the object and associated with the new value.\n\no A position past the end of an existing array. The array is extended\n with the new value. If the existing value is not an array, it is\n autowrapped as an array, then extended with the new value.\n\nOtherwise, a path-value pair for a nonexisting path in the document is\nignored and has no effect.\n\nThe optimizer can perform a partial, in-place update of a JSON column\ninstead of removing the old document and writing the new document in\nits entirety to the column. This optimization can be performed for an\nupdate statement that uses the JSON_SET() function and meets the\nconditions outlined in\nhttps://dev.mysql.com/doc/refman/8.3/en/json.html#json-partial-updates.\n\nThe JSON_SET(), JSON_INSERT(), and JSON_REPLACE() functions are\nrelated:\n\no JSON_SET() replaces existing values and adds nonexisting values.\n\no JSON_INSERT() inserts values without replacing existing values.\n\no JSON_REPLACE() replaces only existing values.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_STORAGE_FREE]
+declaration=json_val
+category=MBR Functions
+description=For a JSON column value, this function shows how much storage space was\nfreed in its binary representation after it was updated in place using\nJSON_SET(), JSON_REPLACE(), or JSON_REMOVE(). The argument can also be\na valid JSON document or a string which can be parsed as one---either\nas a literal value or as the value of a user variable---in which case\nthe function returns 0. It returns a positive, nonzero value if the\nargument is a JSON column value which has been updated as described\npreviously, such that its binary representation takes up less space\nthan it did prior to the update. For a JSON column which has been\nupdated such that its binary representation is the same as or larger\nthan before, or if the update was not able to take advantage of a\npartial update, it returns 0; it returns NULL if the argument is NULL.\n\nIf json_val is not NULL, and neither is a valid JSON document nor can\nbe successfully parsed as one, an error results.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-utility-functions.html
+[JSON_STORAGE_SIZE]
+declaration=json_val
+category=MBR Functions
+description=This function returns the number of bytes used to store the binary\nrepresentation of a JSON document. When the argument is a JSON column,\nthis is the space used to store the JSON document as it was inserted\ninto the column, prior to any partial updates that may have been\nperformed on it afterwards. json_val must be a valid JSON document or a\nstring which can be parsed as one. In the case where it is string, the\nfunction returns the amount of storage space in the JSON binary\nrepresentation that is created by parsing the string as JSON and\nconverting it to binary. It returns NULL if the argument is NULL.\n\nAn error results when json_val is not NULL, and is not---or cannot be\nsuccessfully parsed as---a JSON document.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-utility-functions.html
+[JSON_TABLE]
+declaration=expr, path COLUMNS (column_list
+category=MBR Functions
+description=Extracts data from a JSON document and returns it as a relational table\nhaving the specified columns. The complete syntax for this function is\nshown here:\n\nJSON_TABLE(\n expr,\n path COLUMNS (column_list)\n) [AS] alias\n\ncolumn_list:\n column[, column][, ...]\n\ncolumn:\n name FOR ORDINALITY\n | name type PATH string path [on_empty] [on_error]\n | name type EXISTS PATH string path\n | NESTED [PATH] path COLUMNS (column_list)\n\non_empty:\n {NULL | DEFAULT json_string | ERROR} ON EMPTY\n\non_error:\n {NULL | DEFAULT json_string | ERROR} ON ERROR\n\nexpr: This is an expression that returns JSON data. This can be a\nconstant ('{"a":1}'), a column (t1.json_data, given table t1 specified\nprior to JSON_TABLE() in the FROM clause), or a function call\n(JSON_EXTRACT(t1.json_data,'$.post.comments')).\n\npath: A JSON path expression, which is applied to the data source. We\nrefer to the JSON value matching the path as the row source; this is\nused to generate a row of relational data. The COLUMNS clause evaluates\nthe row source, finds specific JSON values within the row source, and\nreturns those JSON values as SQL values in individual columns of a row\nof relational data.\n\nThe alias is required. The usual rules for table aliases apply (see\nhttps://dev.mysql.com/doc/refman/8.3/en/identifiers.html).\n\nThis function compares column names in case-insensitive fashion.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-table-functions.html
+[JSON_TYPE]
+declaration=json_val
+category=MBR Functions
+description=Returns a utf8mb4 string indicating the type of a JSON value. This can\nbe an object, an array, or a scalar type, as shown here:\n\nmysql> SET @j = '{"a": [10, true]}';\nmysql> SELECT JSON_TYPE(@j);\n+---------------+\n| JSON_TYPE(@j) |\n+---------------+\n| OBJECT |\n+---------------+\nmysql> SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a'));\n+------------------------------------+\n| JSON_TYPE(JSON_EXTRACT(@j, '$.a')) |\n+------------------------------------+\n| ARRAY |\n+------------------------------------+\nmysql> SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[0]'));\n+---------------------------------------+\n| JSON_TYPE(JSON_EXTRACT(@j, '$.a[0]')) |\n+---------------------------------------+\n| INTEGER |\n+---------------------------------------+\nmysql> SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[1]'));\n+---------------------------------------+\n| JSON_TYPE(JSON_EXTRACT(@j, '$.a[1]')) |\n+---------------------------------------+\n| BOOLEAN |\n+---------------------------------------+\n\nJSON_TYPE() returns NULL if the argument is NULL:\n\nmysql> SELECT JSON_TYPE(NULL);\n+-----------------+\n| JSON_TYPE(NULL) |\n+-----------------+\n| NULL |\n+-----------------+\n\nAn error occurs if the argument is not a valid JSON value:\n\nmysql> SELECT JSON_TYPE(1);\nERROR 3146 (22032): Invalid data type for JSON data in argument 1\nto function json_type; a JSON string or JSON type is required.\n\nFor a non-NULL, non-error result, the following list describes the\npossible JSON_TYPE() return values:\n\no Purely JSON types:\n\n o OBJECT: JSON objects\n ...
+[JSON_UNQUOTE]
+declaration=json_val
+category=MBR Functions
+description=Unquotes JSON value and returns the result as a utf8mb4 string. Returns\nNULL if the argument is NULL. An error occurs if the value starts and\nends with double quotes but is not a valid JSON string literal.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-modification-functions.html
+[JSON_VALID]
+declaration=val
+category=MBR Functions
+description=Returns 0 or 1 to indicate whether a value is valid JSON. Returns NULL\nif the argument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/json-attribute-functions.html
+[JSON_VALUE]
+declaration=json_doc, path
+category=MBR Functions
+description=Extracts a value from a JSON document at the path given in the\nspecified document, and returns the extracted value, optionally\nconverting it to a desired type. The complete syntax is shown here:\n\nJSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error])\n\non_empty:\n {NULL | ERROR | DEFAULT value} ON EMPTY\n\non_error:\n {NULL | ERROR | DEFAULT value} ON ERROR\n\njson_doc is a valid JSON document. If this is NULL, the function\nreturns NULL.\n\npath is a JSON path pointing to a location in the document. This must\nbe a string literal value.\n\ntype is one of the following data types:\n\no FLOAT\n\no DOUBLE\n\no DECIMAL\n\no SIGNED\n\no UNSIGNED\n\no DATE\n\no TIME\n\no DATETIME\n\no YEAR\n\n YEAR values of one or two digits are not supported.\n\no CHAR\n\no JSON\n\nThe types just listed are the same as the (non-array) types supported\nby the CAST() function.\n\nIf not specified by a RETURNING clause, the JSON_VALUE() function's\nreturn type is VARCHAR(512). When no character set is specified for the\nreturn type, JSON_VALUE() uses utf8mb4 with the binary collation, which\n ...
+[LAG]
+declaration=expr [, N[, default]]
+category=Window Functions
+description=Returns the value of expr from the row that lags (precedes) the current\nrow by N rows within its partition. If there is no such row, the return\nvalue is default. For example, if N is 3, the return value is default\nfor the first three rows. If N or default are missing, the defaults are\n1 and NULL, respectively.\n\nN must be a literal nonnegative integer. If N is 0, expr is evaluated\nfor the current row.\n\nN cannot be NULL, and must be an integer in the range 0 to 263,\ninclusive, in any of the following forms:\n\no an unsigned integer constant literal\n\no a positional parameter marker (?)\n\no a user-defined variable\n\no a local variable in a stored routine\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\nnull_treatment is as described in the section introduction.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[LAST_DAY]
+declaration=date
+category=Date and Time Functions
+description=Takes a date or datetime value and returns the corresponding value for\nthe last day of the month. Returns NULL if the argument is invalid or\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[LAST_INSERT_ID]
+declaration=
+category=Information Functions
+description=With no argument, LAST_INSERT_ID() returns a BIGINT UNSIGNED (64-bit)\nvalue representing the first automatically generated value successfully\ninserted for an AUTO_INCREMENT column as a result of the most recently\nexecuted INSERT statement. The value of LAST_INSERT_ID() remains\nunchanged if no rows are successfully inserted.\n\nWith an argument, LAST_INSERT_ID() returns an unsigned integer, or NULL\nif the argument is NULL.\n\nFor example, after inserting a row that generates an AUTO_INCREMENT\nvalue, you can get the value like this:\n\nmysql> SELECT LAST_INSERT_ID();\n -> 195\n\nThe currently executing statement does not affect the value of\nLAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value\nwith one statement, and then refer to LAST_INSERT_ID() in a\nmultiple-row INSERT statement that inserts rows into a table with its\nown AUTO_INCREMENT column. The value of LAST_INSERT_ID() remains stable\nin the second statement; its value for the second and later rows is not\naffected by the earlier row insertions. (You should be aware that, if\nyou mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the\neffect is undefined.)\n\nIf the previous statement returned an error, the value of\nLAST_INSERT_ID() is undefined. For transactional tables, if the\nstatement is rolled back due to an error, the value of LAST_INSERT_ID()\nis left undefined. For manual ROLLBACK, the value of LAST_INSERT_ID()\nis not restored to that before the transaction; it remains as it was at\nthe point of the ROLLBACK.\n\nWithin the body of a stored routine (procedure or function) or a\ntrigger, the value of LAST_INSERT_ID() changes the same way as for\nstatements executed outside the body of these kinds of objects. The\neffect of a stored routine or trigger upon the value of\nLAST_INSERT_ID() that is seen by following statements depends on the\nkind of routine:\n\no If a stored procedure executes statements that change the value of\n LAST_INSERT_ID(), the changed value is seen by statements that follow\n the procedure call.\n\no For stored functions and triggers that change the value, the value is\n restored when the function or trigger ends, so statements coming\n after it do not see a changed value.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[LAST_VALUE]
+declaration=expr
+category=Window Functions
+description=Returns the value of expr from the last row of the window frame.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\nnull_treatment is as described in the section introduction.\n\nFor an example, see the FIRST_VALUE() function description.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[LCASE]
+declaration=str
+category=String Functions
+description=LCASE() is a synonym for LOWER().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LEAD]
+declaration=expr [, N[, default]]
+category=Window Functions
+description=Returns the value of expr from the row that leads (follows) the current\nrow by N rows within its partition. If there is no such row, the return\nvalue is default. For example, if N is 3, the return value is default\nfor the last three rows. If N or default are missing, the defaults are\n1 and NULL, respectively.\n\nN must be a literal nonnegative integer. If N is 0, expr is evaluated\nfor the current row.\n\nN cannot be NULL, and must be an integer in the range 0 to 263,\ninclusive, in any of the following forms:\n\no an unsigned integer constant literal\n\no a positional parameter marker (?)\n\no a user-defined variable\n\no a local variable in a stored routine\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\nnull_treatment is as described in the section introduction.\n\nFor an example, see the LAG() function description.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[LEAST]
+declaration=value1,value2,...
+category=Comparison Operators
+description=With two or more arguments, returns the smallest (minimum-valued)\nargument. The arguments are compared using the following rules:\n\no If any argument is NULL, the result is NULL. No comparison is needed.\n\no If all arguments are integer-valued, they are compared as integers.\n\no If at least one argument is double precision, they are compared as\n double-precision values. Otherwise, if at least one argument is a\n DECIMAL value, they are compared as DECIMAL values.\n\no If the arguments comprise a mix of numbers and strings, they are\n compared as strings.\n\no If any argument is a nonbinary (character) string, the arguments are\n compared as nonbinary strings.\n\no In all other cases, the arguments are compared as binary strings.\n\nThe return type of LEAST() is the aggregated type of the comparison\nargument types.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/comparison-operators.html
+[LEFT]
+declaration=str,len
+category=String Functions
+description=Returns the leftmost len characters from the string str, or NULL if any\nargument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LENGTH]
+declaration=str
+category=String Functions
+description=Returns the length of the string str, measured in bytes. A multibyte\ncharacter counts as multiple bytes. This means that for a string\ncontaining five 2-byte characters, LENGTH() returns 10, whereas\nCHAR_LENGTH() returns 5. Returns NULL if str is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LINESTRING]
+declaration=pt [, pt] ...
+category=Geometry Constructors
+description=Constructs a LineString value from a number of Point or WKB Point\narguments. If the number of arguments is less than two, the return\nvalue is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[LN]
+declaration=X
+category=Numeric Functions
+description=Returns the natural logarithm of X; that is, the base-e logarithm of X.\nIf X is less than or equal to 0.0E0, the function returns NULL and a\nwarning "Invalid argument for logarithm" is reported. Returns NULL if X\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[LOAD_FILE]
+declaration=file_name
+category=String Functions
+description=Reads the file and returns the file contents as a string. To use this\nfunction, the file must be located on the server host, you must specify\nthe full path name to the file, and you must have the FILE privilege.\nThe file must be readable by the server and its size less than\nmax_allowed_packet bytes. If the secure_file_priv system variable is\nset to a nonempty directory name, the file to be loaded must be located\nin that directory.\n\nIf the file does not exist or cannot be read because one of the\npreceding conditions is not satisfied, the function returns NULL.\n\nThe character_set_filesystem system variable controls interpretation of\nfile names that are given as literal strings.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LOCALTIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=LOCALTIME and LOCALTIME() are synonyms for NOW().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[LOCALTIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=LOCALTIMESTAMP and LOCALTIMESTAMP() are synonyms for NOW().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[LOCATE]
+declaration=substr,str
+category=String Functions
+description=The first syntax returns the position of the first occurrence of\nsubstring substr in string str. The second syntax returns the position\nof the first occurrence of substring substr in string str, starting at\nposition pos. Returns 0 if substr is not in str. Returns NULL if any\nargument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LOG]
+declaration=X
+category=Numeric Functions
+description=If called with one parameter, this function returns the natural\nlogarithm of X. If X is less than or equal to 0.0E0, the function\nreturns NULL and a warning "Invalid argument for logarithm" is\nreported. Returns NULL if X or B is NULL.\n\nThe inverse of this function (when called with a single argument) is\nthe EXP() function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[LOG10]
+declaration=X
+category=Numeric Functions
+description=Returns the base-10 logarithm of X. If X is less than or equal to\n0.0E0, the function returns NULL and a warning "Invalid argument for\nlogarithm" is reported. Returns NULL if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[LOG2]
+declaration=X
+category=Numeric Functions
+description=Returns the base-2 logarithm of X. If X is less than or equal to 0.0E0,\nthe function returns NULL and a warning "Invalid argument for\nlogarithm" is reported. Returns NULL if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[LOWER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to lowercase\naccording to the current character set mapping, or NULL if str is NULL.\nThe default character set is utf8mb4.\n\nmysql> SELECT LOWER('QUADRATICALLY');\n -> 'quadratically'\n\nLOWER() (and UPPER()) are ineffective when applied to binary strings\n(BINARY, VARBINARY, BLOB). To perform lettercase conversion of a binary\nstring, first convert it to a nonbinary string using a character set\nappropriate for the data stored in the string:\n\nmysql> SET @str = BINARY 'New York';\nmysql> SELECT LOWER(@str), LOWER(CONVERT(@str USING utf8mb4));\n+-------------+------------------------------------+\n| LOWER(@str) | LOWER(CONVERT(@str USING utf8mb4)) |\n+-------------+------------------------------------+\n| New York | new york |\n+-------------+------------------------------------+\n\nFor collations of Unicode character sets, LOWER() and UPPER() work\naccording to the Unicode Collation Algorithm (UCA) version in the\ncollation name, if there is one, and UCA 4.0.0 if no version is\nspecified. For example, utf8mb4_0900_ai_ci and utf8mb3_unicode_520_ci\nwork according to UCA 9.0.0 and 5.2.0, respectively, whereas\nutf8mb3_unicode_ci works according to UCA 4.0.0. See\nhttps://dev.mysql.com/doc/refman/8.3/en/charset-unicode-sets.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LPAD]
+declaration=str,len,padstr
+category=String Functions
+description=Returns the string str, left-padded with the string padstr to a length\nof len characters. If str is longer than len, the return value is\nshortened to len characters.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[LTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with leading space characters removed. Returns\nNULL if str is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[MAKEDATE]
+declaration=year,dayofyear
+category=Date and Time Functions
+description=Returns a date, given year and day-of-year values. dayofyear must be\ngreater than 0 or the result is NULL. The result is also NULL if either\nargument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MAKETIME]
+declaration=hour,minute,second
+category=Date and Time Functions
+description=Returns a time value calculated from the hour, minute, and second\narguments. Returns NULL if any of its arguments are NULL.\n\nThe second argument can have a fractional part.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MAKE_SET]
+declaration=bits,str1,str2,...
+category=String Functions
+description=Returns a set value (a string containing substrings separated by ,\ncharacters) consisting of the strings that have the corresponding bit\nin bits set. str1 corresponds to bit 0, str2 to bit 1, and so on. NULL\nvalues in str1, str2, ... are not appended to the result.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[MASTER_POS_WAIT]
+declaration=log_name,log_pos[,timeout][,channel]
+category=GTID
+description=Deprecated alias for SOURCE_POS_WAIT().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/replication-functions-synchronization.html
+[MAX]
+declaration=[DISTINCT] expr
+category=Aggregate Functions and Modifiers
+description=Returns the maximum value of expr. MAX() may take a string argument; in\nsuch cases, it returns the maximum string value. See\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql-indexes.html. The\nDISTINCT keyword can be used to find the maximum of the distinct values\nof expr, however, this produces the same result as omitting DISTINCT.\n\nIf there are no matching rows, or if expr is NULL, MAX() returns NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html; it\ncannot be used with DISTINCT.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[MBRCONTAINS]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\ncontains the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRWithin().\n\nMBRContains() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRCOVEREDBY]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\nis covered by the minimum bounding rectangle of g2. This tests the\nopposite relationship as MBRCovers().\n\nMBRCoveredBy() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRCOVERS]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\ncovers the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRCoveredBy(). See the description of MBRCoveredBy()\nfor examples.\n\nMBRCovers() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRDISJOINT]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 are disjoint (do not intersect).\n\nMBRDisjoint() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBREQUALS]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 are the same.\n\nMBREquals() handles its arguments as described in the introduction to\nthis section, except that it does not return NULL for empty geometry\narguments.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRINTERSECTS]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangles of\nthe two geometries g1 and g2 intersect.\n\nMBRIntersects() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBROVERLAPS]
+declaration=g1, g2
+category=MBR Functions
+description=Two geometries spatially overlap if they intersect and their\nintersection results in a geometry of the same dimension but not equal\nto either of the given geometries.\n\nThis function returns 1 or 0 to indicate whether the minimum bounding\nrectangles of the two geometries g1 and g2 overlap.\n\nMBROverlaps() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRTOUCHES]
+declaration=g1, g2
+category=MBR Functions
+description=Two geometries spatially touch if their interiors do not intersect, but\nthe boundary of one of the geometries intersects either the boundary or\nthe interior of the other.\n\nThis function returns 1 or 0 to indicate whether the minimum bounding\nrectangles of the two geometries g1 and g2 touch.\n\nMBRTouches() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MBRWITHIN]
+declaration=g1, g2
+category=MBR Functions
+description=Returns 1 or 0 to indicate whether the minimum bounding rectangle of g1\nis within the minimum bounding rectangle of g2. This tests the opposite\nrelationship as MBRContains().\n\nMBRWithin() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-mbr.html
+[MD5]
+declaration=str
+category=Encryption Functions
+description=Calculates an MD5 128-bit checksum for the string. The value is\nreturned as a string of 32 hexadecimal digits, or NULL if the argument\nwas NULL. The return value can, for example, be used as a hash key. See\nthe notes at the beginning of this section about storing hash values\nefficiently.\n\nThe return value is a string in the connection character set.\n\nIf FIPS mode is enabled, MD5() returns NULL. See\nhttps://dev.mysql.com/doc/refman/8.3/en/fips-mode.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[MEDIUMINT]
+declaration=M
+category=Data Types
+description=A medium-sized integer. The signed range is -8388608 to 8388607. The\nunsigned range is 0 to 16777215.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[MICROSECOND]
+declaration=expr
+category=Date and Time Functions
+description=Returns the microseconds from the time or datetime expression expr as a\nnumber in the range from 0 to 999999. Returns NULL if expr is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MID]
+declaration=str,pos,len
+category=String Functions
+description=MID(str,pos,len) is a synonym for SUBSTRING(str,pos,len).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[MIN]
+declaration=[DISTINCT] expr
+category=Aggregate Functions and Modifiers
+description=Returns the minimum value of expr. MIN() may take a string argument; in\nsuch cases, it returns the minimum string value. See\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql-indexes.html. The\nDISTINCT keyword can be used to find the minimum of the distinct values\nof expr, however, this produces the same result as omitting DISTINCT.\n\nIf there are no matching rows, or if expr is NULL, MIN() returns NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html; it\ncannot be used with DISTINCT.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[MINUTE]
+declaration=time
+category=Date and Time Functions
+description=Returns the minute for time, in the range 0 to 59, or NULL if time is\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MOD]
+declaration=N,M
+category=Numeric Functions
+description=Modulo operation. Returns the remainder of N divided by M. Returns NULL\nif M or N is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[MONTH]
+declaration=date
+category=Date and Time Functions
+description=Returns the month for date, in the range 1 to 12 for January to\nDecember, or 0 for dates such as '0000-00-00' or '2008-00-00' that have\na zero month part. Returns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MONTHNAME]
+declaration=date
+category=Date and Time Functions
+description=Returns the full name of the month for date. The language used for the\nname is controlled by the value of the lc_time_names system variable\n(https://dev.mysql.com/doc/refman/8.3/en/locale-support.html). Returns\nNULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[MULTILINESTRING]
+declaration=ls [, ls] ...
+category=Geometry Constructors
+description=Constructs a MultiLineString value using LineString or WKB LineString\narguments.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[MULTIPOINT]
+declaration=pt [, pt2] ...
+category=Geometry Constructors
+description=Constructs a MultiPoint value using Point or WKB Point arguments.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[MULTIPOLYGON]
+declaration=poly [, poly] ...
+category=Geometry Constructors
+description=Constructs a MultiPolygon value from a set of Polygon or WKB Polygon\narguments.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[NAME_CONST]
+declaration=name,value
+category=Miscellaneous Functions
+description=Returns the given value. When used to produce a result set column,\nNAME_CONST() causes the column to have the given name. The arguments\nshould be constants.\n\nmysql> SELECT NAME_CONST('myname', 14);\n+--------+\n| myname |\n+--------+\n| 14 |\n+--------+\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[NOW]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD hh:mm:ss'\nor YYYYMMDDhhmmss format, depending on whether the function is used in\nstring or numeric context. The value is expressed in the session time\nzone.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[NTH_VALUE]
+declaration=expr, N
+category=Window Functions
+description=Returns the value of expr from the N-th row of the window frame. If\nthere is no such row, the return value is NULL.\n\nN must be a literal positive integer.\n\nfrom_first_last is part of the SQL standard, but the MySQL\nimplementation permits only FROM FIRST (which is also the default).\nThis means that calculations begin at the first row of the window. FROM\nLAST is parsed, but produces an error. To obtain the same effect as\nFROM LAST (begin calculations at the last row of the window), use ORDER\nBY to sort in reverse order.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\nnull_treatment is as described in the section introduction.\n\nFor an example, see the FIRST_VALUE() function description.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[NTILE]
+declaration=N
+category=Window Functions
+description=Divides a partition into N groups (buckets), assigns each row in the\npartition its bucket number, and returns the bucket number of the\ncurrent row within its partition. For example, if N is 4, NTILE()\ndivides rows into four buckets. If N is 100, NTILE() divides rows into\n100 buckets.\n\nN must be a literal positive integer. Bucket number return values range\nfrom 1 to N.\n\nN cannot be NULL, and must be an integer in the range 0 to 263,\ninclusive, in any of the following forms:\n\no an unsigned integer constant literal\n\no a positional parameter marker (?)\n\no a user-defined variable\n\no a local variable in a stored routine\n\nThis function should be used with ORDER BY to sort partition rows into\nthe desired order.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[NULLIF]
+declaration=expr1,expr2
+category=Flow Control Functions
+description=Returns NULL if expr1 = expr2 is true, otherwise returns expr1. This is\nthe same as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.\n\nThe return value has the same type as the first argument.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/flow-control-functions.html
+[OCT]
+declaration=N
+category=String Functions
+description=Returns a string representation of the octal value of N, where N is a\nlonglong (BIGINT) number. This is equivalent to CONV(N,10,8). Returns\nNULL if N is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[OCTET_LENGTH]
+declaration=str
+category=String Functions
+description=OCTET_LENGTH() is a synonym for LENGTH().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[ORD]
+declaration=str
+category=String Functions
+description=If the leftmost character of the string str is a multibyte character,\nreturns the code for that character, calculated from the numeric values\nof its constituent bytes using this formula:\n\n (1st byte code)\n+ (2nd byte code * 256)\n+ (3rd byte code * 256^2) ...\n\nIf the leftmost character is not a multibyte character, ORD() returns\nthe same value as the ASCII() function. The function returns NULL if\nstr is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[PERCENT_RANK]
+declaration=
+category=Window Functions
+description=Returns the percentage of partition values less than the value in the\ncurrent row, excluding the highest value. Return values range from 0 to\n1 and represent the row relative rank, calculated as the result of this\nformula, where rank is the row rank and rows is the number of partition\nrows:\n\n(rank - 1) / (rows - 1)\n\nThis function should be used with ORDER BY to sort partition rows into\nthe desired order. Without ORDER BY, all rows are peers.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nFor an example, see the CUME_DIST() function description.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[PERIOD_ADD]
+declaration=P,N
+category=Date and Time Functions
+description=Adds N months to period P (in the format YYMM or YYYYMM). Returns a\nvalue in the format YYYYMM.\n\n*Note*:\n\nThe period argument P is not a date value.\n\nThis function returns NULL if P or N is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[PERIOD_DIFF]
+declaration=P1,P2
+category=Date and Time Functions
+description=Returns the number of months between periods P1 and P2. P1 and P2\nshould be in the format YYMM or YYYYMM. Note that the period arguments\nP1 and P2 are not date values.\n\nThis function returns NULL if P1 or P2 is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[PI]
+declaration=
+category=Numeric Functions
+description=Returns the value of π (pi). The default number of decimal places\ndisplayed is seven, but MySQL uses the full double-precision value\ninternally.\n\nBecause the return value of this function is a double-precision value,\nits exact representation may vary between platforms or implementations.\nThis also applies to any expressions making use of PI(). See\nhttps://dev.mysql.com/doc/refman/8.3/en/floating-point-types.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[POINT]
+declaration=x, y
+category=Geometry Constructors
+description=Constructs a Point using its coordinates.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[POLYGON]
+declaration=ls [, ls] ...
+category=Geometry Constructors
+description=Constructs a Polygon value from a number of LineString or WKB\nLineString arguments. If any argument does not represent a LinearRing\n(that is, not a closed and simple LineString), the return value is\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-mysql-specific-functions.html
+[POSITION]
+declaration=substr IN str
+category=String Functions
+description=POSITION(substr IN str) is a synonym for LOCATE(substr,str).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[POW]
+declaration=X,Y
+category=Numeric Functions
+description=Returns the value of X raised to the power of Y. Returns NULL if X or Y\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[POWER]
+declaration=X,Y
+category=Numeric Functions
+description=This is a synonym for POW().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[PS_CURRENT_THREAD_ID]
+declaration=
+category=Performance Schema Functions
+description=Returns a BIGINT UNSIGNED value representing the Performance Schema\nthread ID assigned to the current connection.\n\nThe thread ID return value is a value of the type given in the\nTHREAD_ID column of Performance Schema tables.\n\nPerformance Schema configuration affects PS_CURRENT_THREAD_ID() the\nsame way as for PS_THREAD_ID(). For details, see the description of\nthat function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/performance-schema-functions.html
+[PS_THREAD_ID]
+declaration=connection_id
+category=Performance Schema Functions
+description=Given a connection ID, returns a BIGINT UNSIGNED value representing the\nPerformance Schema thread ID assigned to the connection ID, or NULL if\nno thread ID exists for the connection ID. The latter can occur for\nthreads that are not instrumented, or if connection_id is NULL.\n\nThe connection ID argument is a value of the type given in the\nPROCESSLIST_ID column of the Performance Schema threads table or the Id\ncolumn of SHOW PROCESSLIST output.\n\nThe thread ID return value is a value of the type given in the\nTHREAD_ID column of Performance Schema tables.\n\nPerformance Schema configuration affects PS_THREAD_ID() operation as\nfollows. (These remarks also apply to PS_CURRENT_THREAD_ID().)\n\no Disabling the thread_instrumentation consumer disables statistics\n from being collected and aggregated at the thread level, but has no\n effect on PS_THREAD_ID().\n\no If performance_schema_max_thread_instances is not 0, the Performance\n Schema allocates memory for thread statistics and assigns an internal\n ID to each thread for which instance memory is available. If there\n are threads for which instance memory is not available,\n PS_THREAD_ID() returns NULL; in this case,\n Performance_schema_thread_instances_lost is nonzero.\n\no If performance_schema_max_thread_instances is 0, the Performance\n Schema allocates no thread memory and PS_THREAD_ID() returns NULL.\n\no If the Performance Schema itself is disabled, PS_THREAD_ID() produces\n an error.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/performance-schema-functions.html
+[QUARTER]
+declaration=date
+category=Date and Time Functions
+description=Returns the quarter of the year for date, in the range 1 to 4, or NULL\nif date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[QUOTE]
+declaration=str
+category=String Functions
+description=Quotes a string to produce a result that can be used as a properly\nescaped data value in an SQL statement. The string is returned enclosed\nby single quotation marks and with each instance of backslash (\),\nsingle quote ('), ASCII NUL, and Control+Z preceded by a backslash. If\nthe argument is NULL, the return value is the word "NULL" without\nenclosing single quotation marks.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[RADIANS]
+declaration=X
+category=Numeric Functions
+description=Returns the argument X, converted from degrees to radians. (Note that\nπ radians equals 180 degrees.) Returns NULL if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[RAND]
+declaration=[N]
+category=Numeric Functions
+description=Returns a random floating-point value v in the range 0 <= v < 1.0. To\nobtain a random integer R in the range i <= R < j, use the expression\nFLOOR(i + RAND() * (j − i)). For example, to obtain a random integer\nin the range the range 7 <= R < 12, use the following statement:\n\nSELECT FLOOR(7 + (RAND() * 5));\n\nIf an integer argument N is specified, it is used as the seed value:\n\no With a constant initializer argument, the seed is initialized once\n when the statement is prepared, prior to execution.\n\no With a nonconstant initializer argument (such as a column name), the\n seed is initialized with the value for each invocation of RAND().\n\nOne implication of this behavior is that for equal argument values,\nRAND(N) returns the same value each time, and thus produces a\nrepeatable sequence of column values. In the following example, the\nsequence of values produced by RAND(3) is the same both places it\noccurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[RANDOM_BYTES]
+declaration=len
+category=Encryption Functions
+description=This function returns a binary string of len random bytes generated\nusing the random number generator of the SSL library. Permitted values\nof len range from 1 to 1024. For values outside that range, an error\noccurs. Returns NULL if len is NULL.\n\nRANDOM_BYTES() can be used to provide the initialization vector for the\nAES_DECRYPT() and AES_ENCRYPT() functions. For use in that context, len\nmust be at least 16. Larger values are permitted, but bytes in excess\nof 16 are ignored.\n\nRANDOM_BYTES() generates a random value, which makes its result\nnondeterministic. Consequently, statements that use this function are\nunsafe for statement-based replication.\n\nIf RANDOM_BYTES() is invoked from within the mysql client, binary\nstrings display using hexadecimal notation, depending on the value of\nthe --binary-as-hex. For more information about that option, see\nhttps://dev.mysql.com/doc/refman/8.3/en/mysql.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[RANK]
+declaration=
+category=Window Functions
+description=Returns the rank of the current row within its partition, with gaps.\nPeers are considered ties and receive the same rank. This function does\nnot assign consecutive ranks to peer groups if groups of size greater\nthan one exist; the result is noncontiguous rank numbers.\n\nThis function should be used with ORDER BY to sort partition rows into\nthe desired order. Without ORDER BY, all rows are peers.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[REGEXP_INSTR]
+declaration=expr, pat[, pos[, occurrence[, return_option[, match_type]]]]
+category=String Functions
+description=Returns the starting index of the substring of the string expr that\nmatches the regular expression specified by the pattern pat, 0 if there\nis no match. If expr or pat is NULL, the return value is NULL.\nCharacter indexes begin at 1.\n\nREGEXP_INSTR() takes these optional arguments:\n\no pos: The position in expr at which to start the search. If omitted,\n the default is 1.\n\no occurrence: Which occurrence of a match to search for. If omitted,\n the default is 1.\n\no return_option: Which type of position to return. If this value is 0,\n REGEXP_INSTR() returns the position of the matched substring's first\n character. If this value is 1, REGEXP_INSTR() returns the position\n following the matched substring. If omitted, the default is 0.\n\no match_type: A string that specifies how to perform matching. The\n meaning is as described for REGEXP_LIKE().\n\nFor additional information about how matching occurs, see the\ndescription for REGEXP_LIKE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/regexp.html
+[REGEXP_LIKE]
+declaration=expr, pat[, match_type]
+category=String Functions
+description=Returns 1 if the string expr matches the regular expression specified\nby the pattern pat, 0 otherwise. If expr or pat is NULL, the return\nvalue is NULL.\n\nThe pattern can be an extended regular expression, the syntax for which\nis discussed in\nhttps://dev.mysql.com/doc/refman/8.3/en/regexp.html#regexp-syntax. The\npattern need not be a literal string. For example, it can be specified\nas a string expression or table column.\n\nThe optional match_type argument is a string that may contain any or\nall the following characters specifying how to perform matching:\n\no c: Case-sensitive matching.\n\no i: Case-insensitive matching.\n\no m: Multiple-line mode. Recognize line terminators within the string.\n The default behavior is to match line terminators only at the start\n and end of the string expression.\n\no n: The . character matches line terminators. The default is for .\n matching to stop at the end of a line.\n\no u: Unix-only line endings. Only the newline character is recognized\n as a line ending by the ., ^, and $ match operators.\n\nIf characters specifying contradictory options are specified within\nmatch_type, the rightmost one takes precedence.\n\nBy default, regular expression operations use the character set and\ncollation of the expr and pat arguments when deciding the type of a\ncharacter and performing the comparison. If the arguments have\ndifferent character sets or collations, coercibility rules apply as\ndescribed in\nhttps://dev.mysql.com/doc/refman/8.3/en/charset-collation-coercibility.\nhtml. Arguments may be specified with explicit collation indicators to\nchange comparison behavior.\n\nmysql> SELECT REGEXP_LIKE('CamelCase', 'CAMELCASE');\n+---------------------------------------+\n| REGEXP_LIKE('CamelCase', 'CAMELCASE') |\n+---------------------------------------+\n| 1 |\n+---------------------------------------+\nmysql> SELECT REGEXP_LIKE('CamelCase', 'CAMELCASE' COLLATE utf8mb4_0900_as_cs);\n+------------------------------------------------------------------+\n| REGEXP_LIKE('CamelCase', 'CAMELCASE' COLLATE utf8mb4_0900_as_cs) |\n+------------------------------------------------------------------+\n| 0 |\n ...
+[REGEXP_REPLACE]
+declaration=expr, pat, repl[, pos[, occurrence[, match_type]]]
+category=String Functions
+description=Replaces occurrences in the string expr that match the regular\nexpression specified by the pattern pat with the replacement string\nrepl, and returns the resulting string. If expr, pat, or repl is NULL,\nthe return value is NULL.\n\nREGEXP_REPLACE() takes these optional arguments:\n\no pos: The position in expr at which to start the search. If omitted,\n the default is 1.\n\no occurrence: Which occurrence of a match to replace. If omitted, the\n default is 0 (which means "replace all occurrences").\n\no match_type: A string that specifies how to perform matching. The\n meaning is as described for REGEXP_LIKE().\n\nThe result returned by this function uses the character set and\ncollation of the expression searched for matches.\n\nFor additional information about how matching occurs, see the\ndescription for REGEXP_LIKE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/regexp.html
+[REGEXP_SUBSTR]
+declaration=expr, pat[, pos[, occurrence[, match_type]]]
+category=String Functions
+description=Returns the substring of the string expr that matches the regular\nexpression specified by the pattern pat, NULL if there is no match. If\nexpr or pat is NULL, the return value is NULL.\n\nREGEXP_SUBSTR() takes these optional arguments:\n\no pos: The position in expr at which to start the search. If omitted,\n the default is 1.\n\no occurrence: Which occurrence of a match to search for. If omitted,\n the default is 1.\n\no match_type: A string that specifies how to perform matching. The\n meaning is as described for REGEXP_LIKE().\n\nThe result returned by this function uses the character set and\ncollation of the expression searched for matches.\n\nFor additional information about how matching occurs, see the\ndescription for REGEXP_LIKE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/regexp.html
+[RELEASE_ALL_LOCKS]
+declaration=
+category=Locking Functions
+description=Releases all named locks held by the current session and returns the\nnumber of locks released (0 if there were none)\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/locking-functions.html
+[RELEASE_LOCK]
+declaration=str
+category=Locking Functions
+description=Releases the lock named by the string str that was obtained with\nGET_LOCK(). Returns 1 if the lock was released, 0 if the lock was not\nestablished by this thread (in which case the lock is not released),\nand NULL if the named lock did not exist. The lock does not exist if it\nwas never obtained by a call to GET_LOCK() or if it has previously been\nreleased.\n\nThe DO statement is convenient to use with RELEASE_LOCK(). See [HELP\nDO].\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/locking-functions.html
+[REVERSE]
+declaration=str
+category=String Functions
+description=Returns the string str with the order of the characters reversed, or\nNULL if str is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[RIGHT]
+declaration=str,len
+category=String Functions
+description=Returns the rightmost len characters from the string str, or NULL if\nany argument is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[ROLES_GRAPHML]
+declaration=
+category=Information Functions
+description=Returns a utf8mb3 string containing a GraphML document representing\nmemory role subgraphs. The ROLE_ADMIN privilege (or the deprecated\nSUPER privilege) is required to see content in the element.\nOtherwise, the result shows only an empty element:\n\nmysql> SELECT ROLES_GRAPHML();\n+---------------------------------------------------+\n| ROLES_GRAPHML() |\n+---------------------------------------------------+\n| |\n+---------------------------------------------------+\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[ROUND]
+declaration=X
+category=Numeric Functions
+description=Rounds the argument X to D decimal places. The rounding algorithm\ndepends on the data type of X. D defaults to 0 if not specified. D can\nbe negative to cause D digits left of the decimal point of the value X\nto become zero. The maximum absolute value for D is 30; any digits in\nexcess of 30 (or -30) are truncated. If X or D is NULL, the function\nreturns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[ROW_COUNT]
+declaration=
+category=Information Functions
+description=ROW_COUNT() returns a value as follows:\n\no DDL statements: 0. This applies to statements such as CREATE TABLE or\n DROP TABLE.\n\no DML statements other than SELECT: The number of affected rows. This\n applies to statements such as UPDATE, INSERT, or DELETE (as before),\n but now also to statements such as ALTER TABLE and LOAD DATA.\n\no SELECT: -1 if the statement returns a result set, or the number of\n rows "affected" if it does not. For example, for SELECT * FROM t1,\n ROW_COUNT() returns -1. For SELECT * FROM t1 INTO OUTFILE\n 'file_name', ROW_COUNT() returns the number of rows written to the\n file.\n\no SIGNAL statements: 0.\n\nFor UPDATE statements, the affected-rows value by default is the number\nof rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to\nmysql_real_connect()\n(https://dev.mysql.com/doc/c-api/8.2/en/mysql-real-connect.html) when\nconnecting to mysqld, the affected-rows value is the number of rows\n"found"; that is, matched by the WHERE clause.\n\nFor REPLACE statements, the affected-rows value is 2 if the new row\nreplaced an old row, because in this case, one row was inserted after\nthe duplicate was deleted.\n\nFor INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows\nvalue per row is 1 if the row is inserted as a new row, 2 if an\nexisting row is updated, and 0 if an existing row is set to its current\nvalues. If you specify the CLIENT_FOUND_ROWS flag, the affected-rows\nvalue is 1 (not 0) if an existing row is set to its current values.\n\nThe ROW_COUNT() value is similar to the value from the\nmysql_affected_rows()\n(https://dev.mysql.com/doc/c-api/8.2/en/mysql-affected-rows.html) C API\nfunction and the row count that the mysql client displays following\nstatement execution.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[ROW_NUMBER]
+declaration=
+category=Window Functions
+description=Returns the number of the current row within its partition. Rows\nnumbers range from 1 to the number of partition rows.\n\nORDER BY affects the order in which rows are numbered. Without ORDER\nBY, row numbering is nondeterministic.\n\nROW_NUMBER() assigns peers different row numbers. To assign peers the\nsame value, use RANK() or DENSE_RANK(). For an example, see the RANK()\nfunction description.\n\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/window-function-descriptions.html
+[RPAD]
+declaration=str,len,padstr
+category=String Functions
+description=Returns the string str, right-padded with the string padstr to a length\nof len characters. If str is longer than len, the return value is\nshortened to len characters. If str, padstr, or len is NULL, the\nfunction returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[RTRIM]
+declaration=str
+category=String Functions
+description=Returns the string str with trailing space characters removed.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SCHEMA]
+declaration=
+category=Information Functions
+description=This function is a synonym for DATABASE().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[SECOND]
+declaration=time
+category=Date and Time Functions
+description=Returns the second for time, in the range 0 to 59, or NULL if time is\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[SEC_TO_TIME]
+declaration=seconds
+category=Date and Time Functions
+description=Returns the seconds argument, converted to hours, minutes, and seconds,\nas a TIME value. The range of the result is constrained to that of the\nTIME data type. A warning occurs if the argument corresponds to a value\noutside that range.\n\nThe function returns NULL if seconds is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[SESSION_USER]
+declaration=
+category=Information Functions
+description=SESSION_USER() is a synonym for USER().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[SHA1]
+declaration=str
+category=Encryption Functions
+description=Calculates an SHA-1 160-bit checksum for the string, as described in\nRFC 3174 (Secure Hash Algorithm). The value is returned as a string of\n40 hexadecimal digits, or NULL if the argument is NULL. One of the\npossible uses for this function is as a hash key. See the notes at the\nbeginning of this section about storing hash values efficiently. SHA()\nis synonymous with SHA1().\n\nThe return value is a string in the connection character set.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[SHA2]
+declaration=str, hash_length
+category=Encryption Functions
+description=Calculates the SHA-2 family of hash functions (SHA-224, SHA-256,\nSHA-384, and SHA-512). The first argument is the plaintext string to be\nhashed. The second argument indicates the desired bit length of the\nresult, which must have a value of 224, 256, 384, 512, or 0 (which is\nequivalent to 256). If either argument is NULL or the hash length is\nnot one of the permitted values, the return value is NULL. Otherwise,\nthe function result is a hash value containing the desired number of\nbits. See the notes at the beginning of this section about storing hash\nvalues efficiently.\n\nThe return value is a string in the connection character set.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[SIGN]
+declaration=X
+category=Numeric Functions
+description=Returns the sign of the argument as -1, 0, or 1, depending on whether X\nis negative, zero, or positive. Returns NULL if X is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[SIN]
+declaration=X
+category=Numeric Functions
+description=Returns the sine of X, where X is given in radians. Returns NULL if X\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[SLEEP]
+declaration=duration
+category=Miscellaneous Functions
+description=Sleeps (pauses) for the number of seconds given by the duration\nargument, then returns 0. The duration may have a fractional part. If\nthe argument is NULL or negative, SLEEP() produces a warning, or an\nerror in strict SQL mode.\n\nWhen sleep returns normally (without interruption), it returns 0:\n\nmysql> SELECT SLEEP(1000);\n+-------------+\n| SLEEP(1000) |\n+-------------+\n| 0 |\n+-------------+\n\nWhen SLEEP() is the only thing invoked by a query that is interrupted,\nit returns 1 and the query itself returns no error. This is true\nwhether the query is killed or times out:\n\no This statement is interrupted using KILL QUERY from another session:\n\nmysql> SELECT SLEEP(1000);\n+-------------+\n| SLEEP(1000) |\n+-------------+\n| 1 |\n+-------------+\n\no This statement is interrupted by timing out:\n\nmysql> SELECT /*+ MAX_EXECUTION_TIME(1) */ SLEEP(1000);\n+-------------+\n| SLEEP(1000) |\n+-------------+\n| 1 |\n+-------------+\n\nWhen SLEEP() is only part of a query that is interrupted, the query\nreturns an error:\n\no This statement is interrupted using KILL QUERY from another session:\n\nmysql> SELECT 1 FROM t1 WHERE SLEEP(1000);\nERROR 1317 (70100): Query execution was interrupted\n\no This statement is interrupted by timing out:\n\nmysql> SELECT /*+ MAX_EXECUTION_TIME(1000) */ 1 FROM t1 WHERE SLEEP(1000);\nERROR 3024 (HY000): Query execution was interrupted, maximum statement\nexecution time exceeded\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[SMALLINT]
+declaration=M
+category=Data Types
+description=A small integer. The signed range is -32768 to 32767. The unsigned\nrange is 0 to 65535.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[SOUNDEX]
+declaration=str
+category=String Functions
+description=Returns a soundex string from str, or NULL if str is NULL. Two strings\nthat sound almost the same should have identical soundex strings. A\nstandard soundex string is four characters long, but the SOUNDEX()\nfunction returns an arbitrarily long string. You can use SUBSTRING() on\nthe result to get a standard soundex string. All nonalphabetic\ncharacters in str are ignored. All international alphabetic characters\noutside the A-Z range are treated as vowels.\n\n*Important*:\n\nWhen using SOUNDEX(), you should be aware of the following limitations:\n\no This function, as currently implemented, is intended to work well\n with strings that are in the English language only. Strings in other\n languages may not produce reliable results.\n\no This function is not guaranteed to provide consistent results with\n strings that use multibyte character sets, including utf-8. See Bug\n #22638 for more information.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SOURCE_POS_WAIT]
+declaration=log_name,log_pos[,timeout][,channel]
+category=GTID
+description=This function is for control of source-replica synchronization. It\nblocks until the replica has read and applied all updates up to the\nspecified position in the source's binary log.\n\nThe return value is the number of log events the replica had to wait\nfor to advance to the specified position. The function returns NULL if\nthe replication SQL thread is not started, the replica's source\ninformation is not initialized, the arguments are incorrect, or an\nerror occurs. It returns -1 if the timeout has been exceeded. If the\nreplication SQL thread stops while SOURCE_POS_WAIT() is waiting, the\nfunction returns NULL. If the replica is past the specified position,\nthe function returns immediately.\n\nIf the binary log file position has been marked as invalid, the\nfunction waits until a valid file position is known. The binary log\nfile position can be marked as invalid when the CHANGE REPLICATION\nSOURCE TO option GTID_ONLY is set for the replication channel, and the\nserver is restarted or replication is stopped. The file position\nbecomes valid after a transaction is successfully applied past the\ngiven file position. If the applier does not reach the stated position,\nthe function waits until the timeout. Use a SHOW REPLICA STATUS\nstatement to check if the binary log file position has been marked as\ninvalid.\n\nOn a multithreaded replica, the function waits until expiry of the\nlimit set by the replica_checkpoint_group or replica_checkpoint_period\nsystem variable, when the checkpoint operation is called to update the\nstatus of the replica. Depending on the setting for the system\nvariables, the function might therefore return some time after the\nspecified position was reached.\n\nIf binary log transaction compression is in use and the transaction\npayload at the specified position is compressed (as a\nTransaction_payload_event), the function waits until the whole\ntransaction has been read and applied, and the positions have updated.\n\nIf a timeout value is specified, SOURCE_POS_WAIT() stops waiting when\ntimeout seconds have elapsed. timeout must be greater than or equal to\n0. (When the server is running in strict SQL mode, a negative timeout\nvalue is immediately rejected with ER_WRONG_ARGUMENTS\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_wrong_arguments); otherwise the function returns NULL, and\nraises a warning.)\n\nThe optional channel value enables you to name which replication\nchannel the function applies to. See\nhttps://dev.mysql.com/doc/refman/8.3/en/replication-channels.html for\nmore information.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/replication-functions-synchronization.html
+[SPACE]
+declaration=N
+category=String Functions
+description=Returns a string consisting of N space characters, or NULL if N is\nNULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SQRT]
+declaration=X
+category=Numeric Functions
+description=Returns the square root of a nonnegative number X. If X is NULL, the\nfunction returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[STATEMENT_DIGEST]
+declaration=statement
+category=Encryption Functions
+description=Given an SQL statement as a string, returns the statement digest hash\nvalue as a string in the connection character set, or NULL if the\nargument is NULL. The related STATEMENT_DIGEST_TEXT() function returns\nthe normalized statement digest. For information about statement\ndigesting, see\nhttps://dev.mysql.com/doc/refman/8.3/en/performance-schema-statement-di\ngests.html.\n\nBoth functions use the MySQL parser to parse the statement. If parsing\nfails, an error occurs. The error message includes the parse error only\nif the statement is provided as a literal string.\n\nThe max_digest_length system variable determines the maximum number of\nbytes available to these functions for computing normalized statement\ndigests.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[STATEMENT_DIGEST_TEXT]
+declaration=statement
+category=Encryption Functions
+description=Given an SQL statement as a string, returns the normalized statement\ndigest as a string in the connection character set, or NULL if the\nargument is NULL. For additional discussion and examples, see the\ndescription of the related STATEMENT_DIGEST() function.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[STD]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the population standard deviation of expr. STD() is a synonym\nfor the standard SQL function STDDEV_POP(), provided as a MySQL\nextension.\n\nIf there are no matching rows, or if expr is NULL, STD() returns NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[STDDEV]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the population standard deviation of expr. STDDEV() is a\nsynonym for the standard SQL function STDDEV_POP(), provided for\ncompatibility with Oracle.\n\nIf there are no matching rows, or if expr is NULL, STDDEV() returns\nNULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[STDDEV_POP]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the population standard deviation of expr (the square root of\nVAR_POP()). You can also use STD() or STDDEV(), which are equivalent\nbut not standard SQL.\n\nIf there are no matching rows, or if expr is NULL, STDDEV_POP() returns\nNULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[STDDEV_SAMP]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the sample standard deviation of expr (the square root of\nVAR_SAMP().\n\nIf there are no matching rows, or if expr is NULL, STDDEV_SAMP()\nreturns NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[STRCMP]
+declaration=expr1,expr2
+category=String Functions
+description=STRCMP() returns 0 if the strings are the same, -1 if the first\nargument is smaller than the second according to the current sort\norder, and NULL if either argument is NULL. It returns 1 otherwise.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-comparison-functions.html
+[STR_TO_DATE]
+declaration=str,format
+category=Date and Time Functions
+description=This is the inverse of the DATE_FORMAT() function. It takes a string\nstr and a format string format. STR_TO_DATE() returns a DATETIME value\nif the format string contains both date and time parts, or a DATE or\nTIME value if the string contains only date or time parts. If str or\nformat is NULL, the function returns NULL. If the date, time, or\ndatetime value extracted from str cannot be parsed according to the\nrules followed by the server, STR_TO_DATE() returns NULL and produces a\nwarning.\n\nThe server scans str attempting to match format to it. The format\nstring can contain literal characters and format specifiers beginning\nwith %. Literal characters in format must match literally in str.\nFormat specifiers in format must match a date or time part in str. For\nthe specifiers that can be used in format, see the DATE_FORMAT()\nfunction description.\n\nmysql> SELECT STR_TO_DATE('01,5,2013','%d,%m,%Y');\n -> '2013-05-01'\nmysql> SELECT STR_TO_DATE('May 1, 2013','%M %d,%Y');\n -> '2013-05-01'\n\nScanning starts at the beginning of str and fails if format is found\nnot to match. Extra characters at the end of str are ignored.\n\nmysql> SELECT STR_TO_DATE('a09:30:17','a%h:%i:%s');\n -> '09:30:17'\nmysql> SELECT STR_TO_DATE('a09:30:17','%h:%i:%s');\n -> NULL\nmysql> SELECT STR_TO_DATE('09:30:17a','%h:%i:%s');\n -> '09:30:17'\n\nUnspecified date or time parts have a value of 0, so incompletely\nspecified values in str produce a result with some or all parts set to\n0:\n\nmysql> SELECT STR_TO_DATE('abc','abc');\n -> '0000-00-00'\nmysql> SELECT STR_TO_DATE('9','%m');\n -> '0000-09-00'\nmysql> SELECT STR_TO_DATE('9','%s');\n -> '00:00:09'\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[ST_AREA]
+declaration={poly|mpoly}
+category=Polygon Property Functions
+description=Returns a double-precision number indicating the area of the Polygon or\nMultiPolygon argument, as measured in its spatial reference system.\n\nST_Area() handles its arguments as described in the introduction to\nthis section, with these exceptions:\n\no If the geometry is geometrically invalid, either the result is an\n undefined area (that is, it can be any number), or an error occurs.\n\no If the geometry is valid but is not a Polygon or MultiPolygon object,\n an ER_UNEXPECTED_GEOMETRY_TYPE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_unexpected_geometry_type) error occurs.\n\no If the geometry is a valid Polygon in a Cartesian SRS, the result is\n the Cartesian area of the polygon.\n\no If the geometry is a valid MultiPolygon in a Cartesian SRS, the\n result is the sum of the Cartesian area of the polygons.\n\no If the geometry is a valid Polygon in a geographic SRS, the result is\n the geodetic area of the polygon in that SRS, in square meters.\n\no If the geometry is a valid MultiPolygon in a geographic SRS, the\n result is the sum of geodetic area of the polygons in that SRS, in\n square meters.\n\no If an area computation results in +inf, an ER_DATA_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_data_out_of_range) error occurs.\n\no If the geometry has a geographic SRS with a longitude or latitude\n that is out of range, an error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n occurs.\n\n Ranges shown are in degrees. The exact range limits deviate slightly\n due to floating-point arithmetic.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-polygon-property-functions.html
+[ST_ASBINARY]
+declaration=g [, options]
+category=WKB Functions
+description=Converts a value in internal geometry format to its WKB representation\nand returns the binary result.\n\nThe function return value has geographic coordinates (latitude,\nlongitude) in the order specified by the spatial reference system that\napplies to the geometry argument. An optional options argument may be\ngiven to override the default axis order.\n\nST_AsBinary() and ST_AsWKB() handle their arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-format-conversion-functions.html
+[ST_ASGEOJSON]
+declaration=g [, max_dec_digits [, options]]
+category=MBR Functions
+description=Generates a GeoJSON object from the geometry g. The object string has\nthe connection character set and collation.\n\nIf any argument is NULL, the return value is NULL. If any non-NULL\nargument is invalid, an error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geojson-functions.html
+[ST_ASTEXT]
+declaration=g [, options]
+category=WKB Functions
+description=Converts a value in internal geometry format to its WKT representation\nand returns the string result.\n\nThe function return value has geographic coordinates (latitude,\nlongitude) in the order specified by the spatial reference system that\napplies to the geometry argument. An optional options argument may be\ngiven to override the default axis order.\n\nST_AsText() and ST_AsWKT() handle their arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-format-conversion-functions.html
+[ST_BUFFER]
+declaration=g, d [, strategy1 [, strategy2 [, strategy3]]]
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents all points whose distance from the\ngeometry value g is less than or equal to a distance of d. The result\nis in the same SRS as the geometry argument.\n\nIf the geometry argument is empty, ST_Buffer() returns an empty\ngeometry.\n\nIf the distance is 0, ST_Buffer() returns the geometry argument\nunchanged:\n\nmysql> SET @pt = ST_GeomFromText('POINT(0 0)');\nmysql> SELECT ST_AsText(ST_Buffer(@pt, 0));\n+------------------------------+\n| ST_AsText(ST_Buffer(@pt, 0)) |\n+------------------------------+\n| POINT(0 0) |\n+------------------------------+\n\nIf the geometry argument is in a Cartesian SRS:\n\no ST_Buffer() supports negative distances for Polygon and MultiPolygon\n values, and for geometry collections containing Polygon or\n MultiPolygon values.\n\no If the result is reduced so much that it disappears, the result is an\n empty geometry.\n\no An ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs for ST_Buffer() with a\n negative distance for Point, MultiPoint, LineString, and\n MultiLineString values, and for geometry collections not containing\n any Polygon or MultiPolygon values.\n\nPoint geometries in a geographic SRS are permitted, subject to the\nfollowing conditions:\n\no If the distance is not negative and no strategies are specified, the\n function returns the geographic buffer of the Point in its SRS. The\n distance argument must be in the SRS distance unit (currently always\n meters).\n\no If the distance is negative or any strategy (except NULL) is\n specified, an ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs.\n\nFor non-Point geometries, an ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_not_implemented_for_geographic_srs) error occurs.\n ...
+[ST_BUFFER_STRATEGY]
+declaration=strategy [, points_per_circle]
+category=GeometryCollection Property Functions
+description=This function returns a strategy byte string for use with ST_Buffer()\nto influence buffer computation.\n\nInformation about strategies is available at Boost.org\n(http://www.boost.org).\n\nThe first argument must be a string indicating a strategy option:\n\no For point strategies, permitted values are 'point_circle' and\n 'point_square'.\n\no For join strategies, permitted values are 'join_round' and\n 'join_miter'.\n\no For end strategies, permitted values are 'end_round' and 'end_flat'.\n\nIf the first argument is 'point_circle', 'join_round', 'join_miter', or\n'end_round', the points_per_circle argument must be given as a positive\nnumeric value. The maximum points_per_circle value is the value of the\nmax_points_in_geometry system variable.\n\nFor examples, see the description of ST_Buffer().\n\nST_Buffer_Strategy() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no If any argument is invalid, an ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs.\n\no If the first argument is 'point_square' or 'end_flat', the\n points_per_circle argument must not be given or an ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_CENTROID]
+declaration={poly|mpoly}
+category=Polygon Property Functions
+description=Returns the mathematical centroid for the Polygon or MultiPolygon\nargument as a Point. The result is not guaranteed to be on the\nMultiPolygon.\n\nThis function processes geometry collections by computing the centroid\npoint for components of highest dimension in the collection. Such\ncomponents are extracted and made into a single MultiPolygon,\nMultiLineString, or MultiPoint for centroid computation.\n\nST_Centroid() handles its arguments as described in the introduction to\nthis section, with these exceptions:\n\no The return value is NULL for the additional condition that the\n argument is an empty geometry collection.\n\no If the geometry has an SRID value for a geographic spatial reference\n system (SRS), an ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-polygon-property-functions.html
+[ST_COLLECT]
+declaration=[DISTINCT] g
+category=MBR Functions
+description=Aggregates geometry values and returns a single geometry collection\nvalue. With the DISTINCT option, returns the aggregation of the\ndistinct geometry arguments.\n\nAs with other aggregate functions, GROUP BY may be used to group\narguments into subsets. ST_Collect() returns an aggregate value for\neach subset.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html. In\ncontrast to most aggregate functions that support windowing,\nST_Collect() permits use of over_clause together with DISTINCT.\n\nST_Collect() handles its arguments as follows:\n\no NULL arguments are ignored.\n\no If all arguments are NULL or the aggregate result is empty, the\n return value is NULL.\n\no If any geometry argument is not a syntactically well-formed geometry,\n an ER_GIS_INVALID_DATA\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_gis_invalid_data) error occurs.\n\no If any geometry argument is a syntactically well-formed geometry in\n an undefined spatial reference system (SRS), an ER_SRS_NOT_FOUND\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_srs_not_found) error occurs.\n\no If there are multiple geometry arguments and those arguments are in\n the same SRS, the return value is in that SRS. If those arguments are\n not in the same SRS, an ER_GIS_DIFFERENT_SRIDS_AGGREGATION\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_gis_different_srids_aggregation) error occurs.\n\no The result is the narrowest MultiXxx or GeometryCollection value\n possible, with the result type determined from the non-NULL geometry\n arguments as follows:\n\n o If all arguments are Point values, the result is a MultiPoint\n value.\n\n o If all arguments are LineString values, the result is a\n MultiLineString value.\n\n o If all arguments are Polygon values, the result is a MultiPolygon\n value.\n\n ...
+[ST_CONTAINS]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Returns 1 or 0 to indicate whether g1 completely contains g2. This\ntests the opposite relationship as ST_Within().\n\nST_Contains() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_CONVEXHULL]
+declaration=g
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents the convex hull of the geometry\nvalue g.\n\nThis function computes a geometry's convex hull by first checking\nwhether its vertex points are colinear. The function returns a linear\nhull if so, a polygon hull otherwise. This function processes geometry\ncollections by extracting all vertex points of all components of the\ncollection, creating a MultiPoint value from them, and computing its\nconvex hull.\n\nST_ConvexHull() handles its arguments as described in the introduction\nto this section, with this exception:\n\no The return value is NULL for the additional condition that the\n argument is an empty geometry collection.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_CROSSES]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Two geometries spatially cross if their spatial relation has the\nfollowing properties:\n\no Unless g1 and g2 are both of dimension 1: g1 crosses g2 if the\n interior of g2 has points in common with the interior of g1, but g2\n does not cover the entire interior of g1.\n\no If both g1 and g2 are of dimension 1: If the lines cross each other\n in a finite number of points (that is, no common line segments, only\n single points in common).\n\nThis function returns 1 or 0 to indicate whether g1 spatially crosses\ng2.\n\nST_Crosses() handles its arguments as described in the introduction to\nthis section except that the return value is NULL for these additional\nconditions:\n\no g1 is of dimension 2 (Polygon or MultiPolygon).\n\no g2 is of dimension 1 (Point or MultiPoint).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_DIFFERENCE]
+declaration=g1, g2
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents the point set difference of the\ngeometry values g1 and g2. The result is in the same SRS as the\ngeometry arguments.\n\nST_Difference() permits arguments in either a Cartesian or a geographic\nSRS, and handles its arguments as described in the introduction to this\nsection.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_DIMENSION]
+declaration=g
+category=Geometry Property Functions
+description=Returns the inherent dimension of the geometry value g. The dimension\ncan be −1, 0, 1, or 2. The meaning of these values is given in\nhttps://dev.mysql.com/doc/refman/8.3/en/gis-class-geometry.html.\n\nST_Dimension() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_DISJOINT]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Returns 1 or 0 to indicate whether g1 is spatially disjoint from (does\nnot intersect) g2.\n\nST_Disjoint() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_DISTANCE]
+declaration=g1, g2 [, unit]
+category=Geometry Relation Functions
+description=Returns the distance between g1 and g2, measured in the length unit of\nthe spatial reference system (SRS) of the geometry arguments, or in the\nunit of the optional unit argument if that is specified.\n\nThis function processes geometry collections by returning the shortest\ndistance among all combinations of the components of the two geometry\narguments.\n\nST_Distance() handles its geometry arguments as described in the\nintroduction to this section, with these exceptions:\n\no ST_Distance() detects arguments in a geographic (ellipsoidal) spatial\n reference system and returns the geodetic distance on the ellipsoid.\n ST_Distance() supports distance calculations for geographic SRS\n arguments of all geometry types.\n\no If any argument is geometrically invalid, either the result is an\n undefined distance (that is, it can be any number), or an error\n occurs.\n\no If an intermediate or final result produces NaN or a negative number,\n an ER_GIS_INVALID_DATA\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_gis_invalid_data) error occurs.\n\nST_Distance() permits specifying the linear unit for the returned\ndistance value with an optional unit argument which ST_Distance()\nhandles as described in the introduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_DISTANCE_SPHERE]
+declaration=g1, g2 [, radius]
+category=MBR Functions
+description=Returns the minimum spherical distance between Point or MultiPoint\narguments on a sphere, in meters. (For general-purpose distance\ncalculations, see the ST_Distance() function.) The optional radius\nargument should be given in meters.\n\nIf both geometry parameters are valid Cartesian Point or MultiPoint\nvalues in SRID 0, the return value is shortest distance between the two\ngeometries on a sphere with the provided radius. If omitted, the\ndefault radius is 6,370,986 meters, Point X and Y coordinates are\ninterpreted as longitude and latitude, respectively, in degrees.\n\nIf both geometry parameters are valid Point or MultiPoint values in a\ngeographic spatial reference system (SRS), the return value is the\nshortest distance between the two geometries on a sphere with the\nprovided radius. If omitted, the default radius is equal to the mean\nradius, defined as (2a+b)/3, where a is the semi-major axis and b is\nthe semi-minor axis of the SRS.\n\nST_Distance_Sphere() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no Supported geometry argument combinations are Point and Point, or\n Point and MultiPoint (in any argument order). If at least one of the\n geometries is neither Point nor MultiPoint, and its SRID is 0, an\n ER_NOT_IMPLEMENTED_FOR_CARTESIAN_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_cartesian_srs) error occurs. If at\n least one of the geometries is neither Point nor MultiPoint, and its\n SRID refers to a geographic SRS, an\n ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs. If\n any geometry refers to a projected SRS, an\n ER_NOT_IMPLEMENTED_FOR_PROJECTED_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_projected_srs) error occurs.\n\no If any argument has a longitude or latitude that is out of range, an\n error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n ...
+[ST_ENDPOINT]
+declaration=ls
+category=LineString Property Functions
+description=Returns the Point that is the endpoint of the LineString value ls.\n\nST_EndPoint() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-linestring-property-functions.html
+[ST_ENVELOPE]
+declaration=g
+category=Geometry Property Functions
+description=Returns the minimum bounding rectangle (MBR) for the geometry value g.\nThe result is returned as a Polygon value that is defined by the corner\npoints of the bounding box:\n\nPOLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))\n\nmysql> SELECT ST_AsText(ST_Envelope(ST_GeomFromText('LineString(1 1,2 2)')));\n+----------------------------------------------------------------+\n| ST_AsText(ST_Envelope(ST_GeomFromText('LineString(1 1,2 2)'))) |\n+----------------------------------------------------------------+\n| POLYGON((1 1,2 1,2 2,1 2,1 1)) |\n+----------------------------------------------------------------+\n\nIf the argument is a point or a vertical or horizontal line segment,\nST_Envelope() returns the point or the line segment as its MBR rather\nthan returning an invalid polygon:\n\nmysql> SELECT ST_AsText(ST_Envelope(ST_GeomFromText('LineString(1 1,1 2)')));\n+----------------------------------------------------------------+\n| ST_AsText(ST_Envelope(ST_GeomFromText('LineString(1 1,1 2)'))) |\n+----------------------------------------------------------------+\n| LINESTRING(1 1,1 2) |\n+----------------------------------------------------------------+\n\nST_Envelope() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no If the geometry has an SRID value for a geographic spatial reference\n system (SRS), an ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_EQUALS]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Returns 1 or 0 to indicate whether g1 is spatially equal to g2.\n\nST_Equals() handles its arguments as described in the introduction to\nthis section, except that it does not return NULL for empty geometry\narguments.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_EXTERIORRING]
+declaration=poly
+category=Polygon Property Functions
+description=Returns the exterior ring of the Polygon value poly as a LineString.\n\nST_ExteriorRing() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-polygon-property-functions.html
+[ST_FRECHETDISTANCE]
+declaration=g1, g2 [, unit]
+category=Geometry Relation Functions
+description=Returns the discrete Fréchet distance between two geometries,\nreflecting how similar the geometries are. The result is a\ndouble-precision number measured in the length unit of the spatial\nreference system (SRS) of the geometry arguments, or in the length unit\nof the unit argument if that argument is given.\n\nThis function implements the discrete Fréchet distance, which means it\nis restricted to distances between the points of the geometries. For\nexample, given two LineString arguments, only the points explicitly\nmentioned in the geometries are considered. Points on the line segments\nbetween these points are not considered.\n\nST_FrechetDistance() handles its geometry arguments as described in the\nintroduction to this section, with these exceptions:\n\no The geometries may have a Cartesian or geographic SRS, but only\n LineString values are supported. If the arguments are in the same\n Cartesian or geographic SRS, but either is not a LineString, an\n ER_NOT_IMPLEMENTED_FOR_CARTESIAN_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_cartesian_srs) or\n ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs,\n depending on the SRS type.\n\nST_FrechetDistance() handles its optional unit argument as described in\nthe introduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_GEOHASH]
+declaration=longitude, latitude, max_length
+category=MBR Functions
+description=max_length)\n\nReturns a geohash string in the connection character set and collation.\n\nFor the first syntax, the longitude must be a number in the range\n[−180, 180], and the latitude must be a number in the range [−90,\n90]. For the second syntax, a POINT value is required, where the X and\nY coordinates are in the valid ranges for longitude and latitude,\nrespectively.\n\nThe resulting string is no longer than max_length characters, which has\nan upper limit of 100. The string might be shorter than max_length\ncharacters because the algorithm that creates the geohash value\ncontinues until it has created a string that is either an exact\nrepresentation of the location or max_length characters, whichever\ncomes first.\n\nST_GeoHash() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geohash-functions.html
+[ST_GEOMCOLLFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=ST_GeometryCollectionFromText(wkt [, srid [, options]]),\nST_GeomCollFromTxt(wkt [, srid [, options]])\n\nConstructs a GeometryCollection value using its WKT representation and\nSRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_GEOMCOLLFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=ST_GeometryCollectionFromWKB(wkb [, srid [, options]])\n\nConstructs a GeometryCollection value using its WKB representation and\nSRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_GEOMETRYN]
+declaration=gc, N
+category=GeometryCollection Property Functions
+description=Returns the N-th geometry in the GeometryCollection value gc.\nGeometries are numbered beginning with 1.\n\nST_GeometryN() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-geometrycollection-property-functions.html
+[ST_GEOMETRYTYPE]
+declaration=g
+category=Geometry Property Functions
+description=Returns a binary string indicating the name of the geometry type of\nwhich the geometry instance g is a member. The name corresponds to one\nof the instantiable Geometry subclasses.\n\nST_GeometryType() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_GEOMFROMGEOJSON]
+declaration=str [, options [, srid]]
+category=MBR Functions
+description=Parses a string str representing a GeoJSON object and returns a\ngeometry.\n\nIf any argument is NULL, the return value is NULL. If any non-NULL\nargument is invalid, an error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geojson-functions.html
+[ST_GEOMFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=srid [, options]])\n\nConstructs a geometry value of any type using its WKT representation\nand SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_GEOMFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=srid [, options]])\n\nConstructs a geometry value of any type using its WKB representation\nand SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_HAUSDORFFDISTANCE]
+declaration=g1, g2 [, unit]
+category=Geometry Relation Functions
+description=Returns the discrete Hausdorff distance between two geometries,\nreflecting how similar the geometries are. The result is a\ndouble-precision number measured in the length unit of the spatial\nreference system (SRS) of the geometry arguments, or in the length unit\nof the unit argument if that argument is given.\n\nThis function implements the discrete Hausdorff distance, which means\nit is restricted to distances between the points of the geometries. For\nexample, given two LineString arguments, only the points explicitly\nmentioned in the geometries are considered. Points on the line segments\nbetween these points are not considered.\n\nST_HausdorffDistance() handles its geometry arguments as described in\nthe introduction to this section, with these exceptions:\n\no If the geometry arguments are in the same Cartesian or geographic\n SRS, but are not in a supported combination, an\n ER_NOT_IMPLEMENTED_FOR_CARTESIAN_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_cartesian_srs) or\n ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs,\n depending on the SRS type. These combinations are supported:\n\n o LineString and LineString\n\n o Point and MultiPoint\n\n o LineString and MultiLineString\n\n o MultiPoint and MultiPoint\n\n o MultiLineString and MultiLineString\n\nST_HausdorffDistance() handles its optional unit argument as described\nin the introduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_INTERIORRINGN]
+declaration=poly, N
+category=Polygon Property Functions
+description=Returns the N-th interior ring for the Polygon value poly as a\nLineString. Rings are numbered beginning with 1.\n\nST_InteriorRingN() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-polygon-property-functions.html
+[ST_INTERSECTION]
+declaration=g1, g2
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents the point set intersection of the\ngeometry values g1 and g2. The result is in the same SRS as the\ngeometry arguments.\n\nST_Intersection() permits arguments in either a Cartesian or a\ngeographic SRS, and handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_INTERSECTS]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Returns 1 or 0 to indicate whether g1 spatially intersects g2.\n\nST_Intersects() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_ISCLOSED]
+declaration=ls
+category=LineString Property Functions
+description=For a LineString value ls, ST_IsClosed() returns 1 if ls is closed\n(that is, its ST_StartPoint() and ST_EndPoint() values are the same).\n\nFor a MultiLineString value ls, ST_IsClosed() returns 1 if ls is closed\n(that is, the ST_StartPoint() and ST_EndPoint() values are the same for\neach LineString in ls).\n\nST_IsClosed() returns 0 if ls is not closed, and NULL if ls is NULL.\n\nST_IsClosed() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no If the geometry has an SRID value for a geographic spatial reference\n system (SRS), an ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-linestring-property-functions.html
+[ST_ISEMPTY]
+declaration=g
+category=Geometry Property Functions
+description=This function is a placeholder that returns 1 for an empty geometry\ncollection value or 0 otherwise.\n\nThe only valid empty geometry is represented in the form of an empty\ngeometry collection value. MySQL does not support GIS EMPTY values such\nas POINT EMPTY.\n\nST_IsEmpty() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_ISSIMPLE]
+declaration=g
+category=Geometry Property Functions
+description=Returns 1 if the geometry value g is simple according to the ISO SQL/MM\nPart 3: Spatial standard. ST_IsSimple() returns 0 if the argument is\nnot simple.\n\nThe descriptions of the instantiable geometric classes given under\nhttps://dev.mysql.com/doc/refman/8.3/en/opengis-geometry-model.html\ninclude the specific conditions that cause class instances to be\nclassified as not simple.\n\nST_IsSimple() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no If the geometry has a geographic SRS with a longitude or latitude\n that is out of range, an error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n occurs.\n\n Ranges shown are in degrees. The exact range limits deviate slightly\n due to floating-point arithmetic.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_ISVALID]
+declaration=g
+category=MBR Functions
+description=Returns 1 if the argument is geometrically valid, 0 if the argument is\nnot geometrically valid. Geometry validity is defined by the OGC\nspecification.\n\nThe only valid empty geometry is represented in the form of an empty\ngeometry collection value. ST_IsValid() returns 1 in this case. MySQL\ndoes not support GIS EMPTY values such as POINT EMPTY.\n\nST_IsValid() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no If the geometry has a geographic SRS with a longitude or latitude\n that is out of range, an error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n occurs.\n\n Ranges shown are in degrees. If an SRS uses another unit, the range\n uses the corresponding values in its unit. The exact range limits\n deviate slightly due to floating-point arithmetic.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-convenience-functions.html
+[ST_LATFROMGEOHASH]
+declaration=geohash_str
+category=MBR Functions
+description=Returns the latitude from a geohash string value, as a double-precision\nnumber in the range [−90, 90].\n\nThe ST_LatFromGeoHash() decoding function reads no more than 433\ncharacters from the geohash_str argument. That represents the upper\nlimit on information in the internal representation of coordinate\nvalues. Characters past the 433rd are ignored, even if they are\notherwise illegal and produce an error.\n\nST_LatFromGeoHash() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geohash-functions.html
+[ST_LATITUDE]
+declaration=p [, new_latitude_val]
+category=Point Property Functions
+description=With a single argument representing a valid Point object p that has a\ngeographic spatial reference system (SRS), ST_Latitude() returns the\nlatitude value of p as a double-precision number.\n\nWith the optional second argument representing a valid latitude value,\nST_Latitude() returns a Point object like the first argument with its\nlatitude equal to the second argument.\n\nST_Latitude() handles its arguments as described in the introduction to\nthis section, with the addition that if the Point object is valid but\ndoes not have a geographic SRS, an ER_SRS_NOT_GEOGRAPHIC\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_srs_not_geographic) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-point-property-functions.html
+[ST_LENGTH]
+declaration=ls [, unit]
+category=LineString Property Functions
+description=Returns a double-precision number indicating the length of the\nLineString or MultiLineString value ls in its associated spatial\nreference system. The length of a MultiLineString value is equal to the\nsum of the lengths of its elements.\n\nST_Length() computes a result as follows:\n\no If the geometry is a valid LineString in a Cartesian SRS, the return\n value is the Cartesian length of the geometry.\n\no If the geometry is a valid MultiLineString in a Cartesian SRS, the\n return value is the sum of the Cartesian lengths of its elements.\n\no If the geometry is a valid LineString in a geographic SRS, the return\n value is the geodetic length of the geometry in that SRS, in meters.\n\no If the geometry is a valid MultiLineString in a geographic SRS, the\n return value is the sum of the geodetic lengths of its elements in\n that SRS, in meters.\n\nST_Length() handles its arguments as described in the introduction to\nthis section, with these exceptions:\n\no If the geometry is not a LineString or MultiLineString, the return\n value is NULL.\n\no If the geometry is geometrically invalid, either the result is an\n undefined length (that is, it can be any number), or an error occurs.\n\no If the length computation result is +inf, an ER_DATA_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_data_out_of_range) error occurs.\n\no If the geometry has a geographic SRS with a longitude or latitude\n that is out of range, an error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n occurs.\n\n Ranges shown are in degrees. The exact range limits deviate slightly\n due to floating-point arithmetic.\n ...
+[ST_LINEFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=srid [, options]])\n\nConstructs a LineString value using its WKT representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_LINEFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=srid [, options]])\n\nConstructs a LineString value using its WKB representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_LINEINTERPOLATEPOINT]
+declaration=ls, fractional_distance
+category=GeometryCollection Property Functions
+description=This function takes a LineString geometry and a fractional distance in\nthe range [0.0, 1.0] and returns the Point along the LineString at the\ngiven fraction of the distance from its start point to its endpoint. It\ncan be used to answer questions such as which Point lies halfway along\nthe road described by the geometry argument.\n\nThe function is implemented for LineString geometries in all spatial\nreference systems, both Cartesian and geographic.\n\nIf the fractional_distance argument is 1.0, the result may not be\nexactly the last point of the LineString argument but a point close to\nit due to numerical inaccuracies in approximate-value computations.\n\nA related function, ST_LineInterpolatePoints(), takes similar arguments\nbut returns a MultiPoint consisting of Point values along the\nLineString at each fraction of the distance from its start point to its\nendpoint. For examples of both functions, see the\nST_LineInterpolatePoints() description.\n\nST_LineInterpolatePoint() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no If the geometry argument is not a LineString, an\n ER_UNEXPECTED_GEOMETRY_TYPE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_unexpected_geometry_type) error occurs.\n\no If the fractional distance argument is outside the range [0.0, 1.0],\n an ER_DATA_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_data_out_of_range) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_LINEINTERPOLATEPOINTS]
+declaration=ls, fractional_distance
+category=GeometryCollection Property Functions
+description=This function takes a LineString geometry and a fractional distance in\nthe range (0.0, 1.0] and returns the MultiPoint consisting of the\nLineString start point, plus Point values along the LineString at each\nfraction of the distance from its start point to its endpoint. It can\nbe used to answer questions such as which Point values lie every 10% of\nthe way along the road described by the geometry argument.\n\nThe function is implemented for LineString geometries in all spatial\nreference systems, both Cartesian and geographic.\n\nIf the fractional_distance argument divides 1.0 with zero remainder the\nresult may not contain the last point of the LineString argument but a\npoint close to it due to numerical inaccuracies in approximate-value\ncomputations.\n\nA related function, ST_LineInterpolatePoint(), takes similar arguments\nbut returns the Point along the LineString at the given fraction of the\ndistance from its start point to its endpoint.\n\nST_LineInterpolatePoints() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no If the geometry argument is not a LineString, an\n ER_UNEXPECTED_GEOMETRY_TYPE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_unexpected_geometry_type) error occurs.\n\no If the fractional distance argument is outside the range [0.0, 1.0],\n an ER_DATA_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_data_out_of_range) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_LONGFROMGEOHASH]
+declaration=geohash_str
+category=MBR Functions
+description=Returns the longitude from a geohash string value, as a\ndouble-precision number in the range [−180, 180].\n\nThe remarks in the description of ST_LatFromGeoHash() regarding the\nmaximum number of characters processed from the geohash_str argument\nalso apply to ST_LongFromGeoHash().\n\nST_LongFromGeoHash() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geohash-functions.html
+[ST_LONGITUDE]
+declaration=p [, new_longitude_val]
+category=Point Property Functions
+description=With a single argument representing a valid Point object p that has a\ngeographic spatial reference system (SRS), ST_Longitude() returns the\nlongitude value of p as a double-precision number.\n\nWith the optional second argument representing a valid longitude value,\nST_Longitude() returns a Point object like the first argument with its\nlongitude equal to the second argument.\n\nST_Longitude() handles its arguments as described in the introduction\nto this section, with the addition that if the Point object is valid\nbut does not have a geographic SRS, an ER_SRS_NOT_GEOGRAPHIC\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_srs_not_geographic) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-point-property-functions.html
+[ST_MAKEENVELOPE]
+declaration=pt1, pt2
+category=MBR Functions
+description=Returns the rectangle that forms the envelope around two points, as a\nPoint, LineString, or Polygon.\n\nCalculations are done using the Cartesian coordinate system rather than\non a sphere, spheroid, or on earth.\n\nGiven two points pt1 and pt2, ST_MakeEnvelope() creates the result\ngeometry on an abstract plane like this:\n\no If pt1 and pt2 are equal, the result is the point pt1.\n\no Otherwise, if (pt1, pt2) is a vertical or horizontal line segment,\n the result is the line segment (pt1, pt2).\n\no Otherwise, the result is a polygon using pt1 and pt2 as diagonal\n points.\n\nThe result geometry has an SRID of 0.\n\nST_MakeEnvelope() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no If the arguments are not Point values, an ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs.\n\no An ER_GIS_INVALID_DATA\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_gis_invalid_data) error occurs for the additional\n condition that any coordinate value of the two points is infinite or\n NaN.\n\no If any geometry has an SRID value for a geographic spatial reference\n system (SRS), an ER_NOT_IMPLEMENTED_FOR_GEOGRAPHIC_SRS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_not_implemented_for_geographic_srs) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-convenience-functions.html
+[ST_MLINEFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=ST_MultiLineStringFromText(wkt [, srid [, options]])\n\nConstructs a MultiLineString value using its WKT representation and\nSRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_MLINEFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=ST_MultiLineStringFromWKB(wkb [, srid [, options]])\n\nConstructs a MultiLineString value using its WKB representation and\nSRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_MPOINTFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=[, srid [, options]])\n\nConstructs a MultiPoint value using its WKT representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_MPOINTFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=srid [, options]])\n\nConstructs a MultiPoint value using its WKB representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_MPOLYFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=[, srid [, options]])\n\nConstructs a MultiPolygon value using its WKT representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_MPOLYFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=[, srid [, options]])\n\nConstructs a MultiPolygon value using its WKB representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_NUMGEOMETRIES]
+declaration=gc
+category=GeometryCollection Property Functions
+description=Returns the number of geometries in the GeometryCollection value gc.\n\nST_NumGeometries() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-geometrycollection-property-functions.html
+[ST_NUMINTERIORRINGS]
+declaration=poly
+category=Polygon Property Functions
+description=Returns the number of interior rings in the Polygon value poly.\n\nST_NumInteriorRing() and ST_NuminteriorRings() handle their arguments\nas described in the introduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-polygon-property-functions.html
+[ST_NUMPOINTS]
+declaration=ls
+category=LineString Property Functions
+description=Returns the number of Point objects in the LineString value ls.\n\nST_NumPoints() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-linestring-property-functions.html
+[ST_OVERLAPS]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Two geometries spatially overlap if they intersect and their\nintersection results in a geometry of the same dimension but not equal\nto either of the given geometries.\n\nThis function returns 1 or 0 to indicate whether g1 spatially overlaps\ng2.\n\nST_Overlaps() handles its arguments as described in the introduction to\nthis section except that the return value is NULL for the additional\ncondition that the dimensions of the two geometries are not equal.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_POINTATDISTANCE]
+declaration=ls, distance
+category=GeometryCollection Property Functions
+description=This function takes a LineString geometry and a distance in the range\n[0.0, ST_Length(ls)] measured in the unit of the spatial reference\nsystem (SRS) of the LineString, and returns the Point along the\nLineString at that distance from its start point. It can be used to\nanswer questions such as which Point value is 400 meters from the start\nof the road described by the geometry argument.\n\nThe function is implemented for LineString geometries in all spatial\nreference systems, both Cartesian and geographic.\n\nST_PointAtDistance() handles its arguments as described in the\nintroduction to this section, with these exceptions:\n\no If the geometry argument is not a LineString, an\n ER_UNEXPECTED_GEOMETRY_TYPE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_unexpected_geometry_type) error occurs.\n\no If the fractional distance argument is outside the range [0.0,\n ST_Length(ls)], an ER_DATA_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_data_out_of_range) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_POINTFROMGEOHASH]
+declaration=geohash_str, srid
+category=MBR Functions
+description=Returns a POINT value containing the decoded geohash value, given a\ngeohash string value.\n\nThe X and Y coordinates of the point are the longitude in the range\n[−180, 180] and the latitude in the range [−90, 90], respectively.\n\nThe srid argument is an 32-bit unsigned integer.\n\nThe remarks in the description of ST_LatFromGeoHash() regarding the\nmaximum number of characters processed from the geohash_str argument\nalso apply to ST_PointFromGeoHash().\n\nST_PointFromGeoHash() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-geohash-functions.html
+[ST_POINTFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=Constructs a Point value using its WKT representation and SRID.\n\nST_PointFromText() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_POINTFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=Constructs a Point value using its WKB representation and SRID.\n\nST_PointFromWKB() handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_POINTN]
+declaration=ls, N
+category=LineString Property Functions
+description=Returns the N-th Point in the Linestring value ls. Points are numbered\nbeginning with 1.\n\nST_PointN() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-linestring-property-functions.html
+[ST_POLYFROMTEXT]
+declaration=wkt [, srid [, options]]
+category=WKT Functions
+description=srid [, options]])\n\nConstructs a Polygon value using its WKT representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkt-functions.html
+[ST_POLYFROMWKB]
+declaration=wkb [, srid [, options]]
+category=WKB Functions
+description=[, options]])\n\nConstructs a Polygon value using its WKB representation and SRID.\n\nThese functions handle their arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-wkb-functions.html
+[ST_SIMPLIFY]
+declaration=g, max_distance
+category=MBR Functions
+description=Simplifies a geometry using the Douglas-Peucker algorithm and returns a\nsimplified value of the same type.\n\nThe geometry may be any geometry type, although the Douglas-Peucker\nalgorithm may not actually process every type. A geometry collection is\nprocessed by giving its components one by one to the simplification\nalgorithm, and the returned geometries are put into a geometry\ncollection as result.\n\nThe max_distance argument is the distance (in units of the input\ncoordinates) of a vertex to other segments to be removed. Vertices\nwithin this distance of the simplified linestring are removed.\n\nAccording to Boost.Geometry, geometries might become invalid as a\nresult of the simplification process, and the process might create\nself-intersections. To check the validity of the result, pass it to\nST_IsValid().\n\nST_Simplify() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no If the max_distance argument is not positive, or is NaN, an\n ER_WRONG_ARGUMENTS\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_wrong_arguments) error occurs.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-convenience-functions.html
+[ST_SRID]
+declaration=g [, srid]
+category=Geometry Property Functions
+description=With a single argument representing a valid geometry object g,\nST_SRID() returns an integer indicating the ID of the spatial reference\nsystem (SRS) associated with g.\n\nWith the optional second argument representing a valid SRID value,\nST_SRID() returns an object with the same type as its first argument\nwith an SRID value equal to the second argument. This only sets the\nSRID value of the object; it does not perform any transformation of\ncoordinate values.\n\nST_SRID() handles its arguments as described in the introduction to\nthis section, with this exception:\n\no For the single-argument syntax, ST_SRID() returns the geometry SRID\n even if it refers to an undefined SRS. An ER_SRS_NOT_FOUND\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_srs_not_found) error does not occur.\n\nST_SRID(g, target_srid) and ST_Transform(g, target_srid) differ as\nfollows:\n\no ST_SRID() changes the geometry SRID value without transforming its\n coordinates.\n\no ST_Transform() transforms the geometry coordinates in addition to\n changing its SRID value.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-general-property-functions.html
+[ST_STARTPOINT]
+declaration=ls
+category=LineString Property Functions
+description=Returns the Point that is the start point of the LineString value ls.\n\nST_StartPoint() handles its arguments as described in the introduction\nto this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-linestring-property-functions.html
+[ST_SWAPXY]
+declaration=g
+category=WKB Functions
+description=Accepts an argument in internal geometry format, swaps the X and Y\nvalues of each coordinate pair within the geometry, and returns the\nresult.\n\nST_SwapXY() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-format-conversion-functions.html
+[ST_SYMDIFFERENCE]
+declaration=g1, g2
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents the point set symmetric difference\nof the geometry values g1 and g2, which is defined as:\n\ng1 symdifference g2 := (g1 union g2) difference (g1 intersection g2)\n\nOr, in function call notation:\n\nST_SymDifference(g1, g2) = ST_Difference(ST_Union(g1, g2), ST_Intersection(g1, g2))\n\nThe result is in the same SRS as the geometry arguments.\n\nST_SymDifference() permits arguments in either a Cartesian or a\ngeographic SRS, and handles its arguments as described in the\nintroduction to this section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_TOUCHES]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Two geometries spatially touch if their interiors do not intersect, but\nthe boundary of one of the geometries intersects either the boundary or\nthe interior of the other.\n\nThis function returns 1 or 0 to indicate whether g1 spatially touches\ng2.\n\nST_Touches() handles its arguments as described in the introduction to\nthis section except that the return value is NULL for the additional\ncondition that both geometries are of dimension 0 (Point or\nMultiPoint).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_TRANSFORM]
+declaration=g, target_srid
+category=GeometryCollection Property Functions
+description=Transforms a geometry from one spatial reference system (SRS) to\nanother. The return value is a geometry of the same type as the input\ngeometry with all coordinates transformed to the target SRID,\ntarget_srid. MySQL supports all SRSs defined by EPSG except for those\nlisted here:\n\no EPSG 1042 Krovak Modified\n\no EPSG 1043 Krovak Modified (North Orientated)\n\no EPSG 9816 Tunisia Mining Grid\n\no EPSG 9826 Lambert Conic Conformal (West Orientated)\n\nST_Transform() handles its arguments as described in the introduction\nto this section, with these exceptions:\n\no Geometry arguments that have an SRID value for a geographic SRS do\n not produce an error.\n\no If the geometry or target SRID argument has an SRID value that refers\n to an undefined spatial reference system (SRS), an ER_SRS_NOT_FOUND\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_srs_not_found) error occurs.\n\no If the geometry is in an SRS that ST_Transform() cannot transform\n from, an ER_TRANSFORM_SOURCE_SRS_NOT_SUPPORTED\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_transform_source_srs_not_supported) error occurs.\n\no If the target SRID is in an SRS that ST_Transform() cannot transform\n to, an ER_TRANSFORM_TARGET_SRS_NOT_SUPPORTED\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_transform_target_srs_not_supported) error occurs.\n\no If the geometry is in an SRS that is not WGS 84 and has no TOWGS84\n clause, an ER_TRANSFORM_SOURCE_SRS_MISSING_TOWGS84\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_transform_source_srs_missing_towgs84) error occurs.\n\no If the target SRID is in an SRS that is not WGS 84 and has no TOWGS84\n clause, an ER_TRANSFORM_TARGET_SRS_MISSING_TOWGS84\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference\n .html#error_er_transform_target_srs_missing_towgs84) error occurs.\n\nST_SRID(g, target_srid) and ST_Transform(g, target_srid) differ as\nfollows:\n\no ST_SRID() changes the geometry SRID value without transforming its\n coordinates.\n ...
+[ST_UNION]
+declaration=g1, g2
+category=GeometryCollection Property Functions
+description=Returns a geometry that represents the point set union of the geometry\nvalues g1 and g2. The result is in the same SRS as the geometry\narguments.\n\nST_Union() permits arguments in either a Cartesian or a geographic SRS,\nand handles its arguments as described in the introduction to this\nsection.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-operator-functions.html
+[ST_VALIDATE]
+declaration=g
+category=MBR Functions
+description=Validates a geometry according to the OGC specification. A geometry can\nbe syntactically well-formed (WKB value plus SRID) but geometrically\ninvalid. For example, this polygon is geometrically invalid: POLYGON((0\n0, 0 0, 0 0, 0 0, 0 0))\n\nST_Validate() returns the geometry if it is syntactically well-formed\nand is geometrically valid, NULL if the argument is not syntactically\nwell-formed or is not geometrically valid or is NULL.\n\nST_Validate() can be used to filter out invalid geometry data, although\nat a cost. For applications that require more precise results not\ntainted by invalid data, this penalty may be worthwhile.\n\nIf the geometry argument is valid, it is returned as is, except that if\nan input Polygon or MultiPolygon has clockwise rings, those rings are\nreversed before checking for validity. If the geometry is valid, the\nvalue with the reversed rings is returned.\n\nThe only valid empty geometry is represented in the form of an empty\ngeometry collection value. ST_Validate() returns it directly without\nfurther checks in this case.\n\nST_Validate() handles its arguments as described in the introduction to\nthis section, with the exceptions listed here:\n\no If the geometry has a geographic SRS with a longitude or latitude\n that is out of range, an error occurs:\n\n o If a longitude value is not in the range (−180, 180], an\n ER_GEOMETRY_PARAM_LONGITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_longitude_out_of_range) error\n occurs.\n\n o If a latitude value is not in the range [−90, 90], an\n ER_GEOMETRY_PARAM_LATITUDE_OUT_OF_RANGE\n (https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-referen\n ce.html#error_er_geometry_param_latitude_out_of_range) error\n occurs.\n\n Ranges shown are in degrees. The exact range limits deviate slightly\n due to floating-point arithmetic.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-convenience-functions.html
+[ST_WITHIN]
+declaration=g1, g2
+category=Geometry Relation Functions
+description=Returns 1 or 0 to indicate whether g1 is spatially within g2. This\ntests the opposite relationship as ST_Contains().\n\nST_Within() handles its arguments as described in the introduction to\nthis section.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/spatial-relation-functions-object-shapes.html
+[ST_X]
+declaration=p [, new_x_val]
+category=Point Property Functions
+description=With a single argument representing a valid Point object p, ST_X()\nreturns the X-coordinate value of p as a double-precision number. The X\ncoordinate is considered to refer to the axis that appears first in the\nPoint spatial reference system (SRS) definition.\n\nWith the optional second argument, ST_X() returns a Point object like\nthe first argument with its X coordinate equal to the second argument.\nIf the Point object has a geographic SRS, the second argument must be\nin the proper range for longitude or latitude values.\n\nST_X() handles its arguments as described in the introduction to this\nsection.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-point-property-functions.html
+[ST_Y]
+declaration=p [, new_y_val]
+category=Point Property Functions
+description=With a single argument representing a valid Point object p, ST_Y()\nreturns the Y-coordinate value of p as a double-precision number.The Y\ncoordinate is considered to refer to the axis that appears second in\nthe Point spatial reference system (SRS) definition.\n\nWith the optional second argument, ST_Y() returns a Point object like\nthe first argument with its Y coordinate equal to the second argument.\nIf the Point object has a geographic SRS, the second argument must be\nin the proper range for longitude or latitude values.\n\nST_Y() handles its arguments as described in the introduction to this\nsection.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gis-point-property-functions.html
+[SUBDATE]
+declaration=date,INTERVAL expr unit
+category=Date and Time Functions
+description=When invoked with the INTERVAL form of the second argument, SUBDATE()\nis a synonym for DATE_SUB(). For information on the INTERVAL unit\nargument, see the discussion for DATE_ADD().\n\nmysql> SELECT DATE_SUB('2008-01-02', INTERVAL 31 DAY);\n -> '2007-12-02'\nmysql> SELECT SUBDATE('2008-01-02', INTERVAL 31 DAY);\n -> '2007-12-02'\n\nThe second form enables the use of an integer value for days. In such\ncases, it is interpreted as the number of days to be subtracted from\nthe date or datetime expression expr.\n\nmysql> SELECT SUBDATE('2008-01-02 12:00:00', 31);\n -> '2007-12-02 12:00:00'\n\nThis function returns NULL if any of its arguments are NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[SUBSTR]
+declaration=str,pos
+category=String Functions
+description=FROM pos FOR len)\n\nSUBSTR() is a synonym for SUBSTRING().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SUBSTRING]
+declaration=str,pos
+category=String Functions
+description=SUBSTRING(str FROM pos FOR len)\n\nThe forms without a len argument return a substring from string str\nstarting at position pos. The forms with a len argument return a\nsubstring len characters long from string str, starting at position\npos. The forms that use FROM are standard SQL syntax. It is also\npossible to use a negative value for pos. In this case, the beginning\nof the substring is pos characters from the end of the string, rather\nthan the beginning. A negative value may be used for pos in any of the\nforms of this function. A value of 0 for pos returns an empty string.\n\nFor all forms of SUBSTRING(), the position of the first character in\nthe string from which the substring is to be extracted is reckoned as\n1.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SUBSTRING_INDEX]
+declaration=str,delim,count
+category=String Functions
+description=Returns the substring from string str before count occurrences of the\ndelimiter delim. If count is positive, everything to the left of the\nfinal delimiter (counting from the left) is returned. If count is\nnegative, everything to the right of the final delimiter (counting from\nthe right) is returned. SUBSTRING_INDEX() performs a case-sensitive\nmatch when searching for delim.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[SUBTIME]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=SUBTIME() returns expr1 − expr2 expressed as a value in the same\nformat as expr1. expr1 is a time or datetime expression, and expr2 is a\ntime expression.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[SUM]
+declaration=[DISTINCT] expr
+category=Aggregate Functions and Modifiers
+description=Returns the sum of expr. If the return set has no rows, SUM() returns\nNULL. The DISTINCT keyword can be used to sum only the distinct values\nof expr.\n\nIf there are no matching rows, or if expr is NULL, SUM() returns NULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html; it\ncannot be used with DISTINCT.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[SYSDATE]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current date and time as a value in 'YYYY-MM-DD hh:mm:ss'\nor YYYYMMDDhhmmss format, depending on whether the function is used in\nstring or numeric context.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nSYSDATE() returns the time at which it executes. This differs from the\nbehavior for NOW(), which returns a constant time that indicates the\ntime at which the statement began to execute. (Within a stored function\nor trigger, NOW() returns the time at which the function or triggering\nstatement began to execute.)\n\nmysql> SELECT NOW(), SLEEP(2), NOW();\n+---------------------+----------+---------------------+\n| NOW() | SLEEP(2) | NOW() |\n+---------------------+----------+---------------------+\n| 2006-04-12 13:47:36 | 0 | 2006-04-12 13:47:36 |\n+---------------------+----------+---------------------+\n\nmysql> SELECT SYSDATE(), SLEEP(2), SYSDATE();\n+---------------------+----------+---------------------+\n| SYSDATE() | SLEEP(2) | SYSDATE() |\n+---------------------+----------+---------------------+\n| 2006-04-12 13:47:44 | 0 | 2006-04-12 13:47:46 |\n+---------------------+----------+---------------------+\n\nIn addition, the SET TIMESTAMP statement affects the value returned by\nNOW() but not by SYSDATE(). This means that timestamp settings in the\nbinary log have no effect on invocations of SYSDATE().\n\nBecause SYSDATE() can return different values even within the same\nstatement, and is not affected by SET TIMESTAMP, it is nondeterministic\nand therefore unsafe for replication if statement-based binary logging\nis used. If that is a problem, you can use row-based logging.\n\nAlternatively, you can use the --sysdate-is-now option to cause\nSYSDATE() to be an alias for NOW(). This works if the option is used on\nboth the replication source server and the replica.\n\nThe nondeterministic nature of SYSDATE() also means that indexes cannot\nbe used for evaluating expressions that refer to it.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[SYSTEM_USER]
+declaration=
+category=Information Functions
+description=SYSTEM_USER() is a synonym for USER().\n\n*Note*:\n\nThe SYSTEM_USER() function is distinct from the SYSTEM_USER privilege.\nThe former returns the current MySQL account name. The latter\ndistinguishes the system user and regular user account categories (see\nhttps://dev.mysql.com/doc/refman/8.3/en/account-categories.html).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[TAN]
+declaration=X
+category=Numeric Functions
+description=Returns the tangent of X, where X is given in radians. Returns NULL if\nX is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[TEXT]
+declaration=M
+category=Data Types
+description=A TEXT column with a maximum length of 65,535 (216 − 1) characters.\nThe effective maximum length is less if the value contains multibyte\ncharacters. Each TEXT value is stored using a 2-byte length prefix that\nindicates the number of bytes in the value.\n\nAn optional length M can be given for this type. If this is done, MySQL\ncreates the column as the smallest TEXT type large enough to hold\nvalues M characters long.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[TIME]
+declaration=fsp
+category=Data Types
+description=A time. The range is '-838:59:59.000000' to '838:59:59.000000'. MySQL\ndisplays TIME values in 'hh:mm:ss[.fraction]' format, but permits\nassignment of values to TIME columns using either strings or numbers.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-type-syntax.html
+[TIMEDIFF]
+declaration=expr1,expr2
+category=Date and Time Functions
+description=TIMEDIFF() returns expr1 − expr2 expressed as a time value. expr1 and\nexpr2 are strings which are converted to TIME or DATETIME expressions;\nthese must be of the same type following conversion. Returns NULL if\nexpr1 or expr2 is NULL.\n\nThe result returned by TIMEDIFF() is limited to the range allowed for\nTIME values. Alternatively, you can use either of the functions\nTIMESTAMPDIFF() and UNIX_TIMESTAMP(), both of which return integers.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TIMESTAMP]
+declaration=fsp
+category=Data Types
+description=A timestamp. The range is '1970-01-01 00:00:01.000000' UTC to\n'2038-01-19 03:14:07.499999' UTC. TIMESTAMP values are stored as the\nnumber of seconds since the epoch ('1970-01-01 00:00:00' UTC). A\nTIMESTAMP cannot represent the value '1970-01-01 00:00:00' because that\nis equivalent to 0 seconds from the epoch and the value 0 is reserved\nfor representing '0000-00-00 00:00:00', the "zero" TIMESTAMP value.\n\nAn optional fsp value in the range from 0 to 6 may be given to specify\nfractional seconds precision. A value of 0 signifies that there is no\nfractional part. If omitted, the default precision is 0.\n\nThe way the server handles TIMESTAMP definitions depends on the value\nof the explicit_defaults_for_timestamp system variable (see\nhttps://dev.mysql.com/doc/refman/8.3/en/server-system-variables.html).\n\nIf explicit_defaults_for_timestamp is enabled, there is no automatic\nassignment of the DEFAULT CURRENT_TIMESTAMP or ON UPDATE\nCURRENT_TIMESTAMP attributes to any TIMESTAMP column. They must be\nincluded explicitly in the column definition. Also, any TIMESTAMP not\nexplicitly declared as NOT NULL permits NULL values.\n\nIf explicit_defaults_for_timestamp is disabled, the server handles\nTIMESTAMP as follows:\n\nUnless specified otherwise, the first TIMESTAMP column in a table is\ndefined to be automatically set to the date and time of the most recent\nmodification if not explicitly assigned a value. This makes TIMESTAMP\nuseful for recording the timestamp of an INSERT or UPDATE operation.\nYou can also set any TIMESTAMP column to the current date and time by\nassigning it a NULL value, unless it has been defined with the NULL\nattribute to permit NULL values.\n\nAutomatic initialization and updating to the current date and time can\nbe specified using DEFAULT CURRENT_TIMESTAMP and ON UPDATE\nCURRENT_TIMESTAMP column definition clauses. By default, the first\nTIMESTAMP column has these properties, as previously noted. However,\nany TIMESTAMP column in a table can be defined to have these\nproperties.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-type-syntax.html
+[TIMESTAMPADD]
+declaration=unit,interval,datetime_expr
+category=Date and Time Functions
+description=Adds the integer expression interval to the date or datetime expression\ndatetime_expr. The unit for interval is given by the unit argument,\nwhich should be one of the following values: MICROSECOND\n(microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or\nYEAR.\n\nThe unit value may be specified using one of keywords as shown, or with\na prefix of SQL_TSI_. For example, DAY and SQL_TSI_DAY both are legal.\n\nThis function returns NULL if interval or datetime_expr is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TIMESTAMPDIFF]
+declaration=unit,datetime_expr1,datetime_expr2
+category=Date and Time Functions
+description=Returns datetime_expr2 − datetime_expr1, where datetime_expr1 and\ndatetime_expr2 are date or datetime expressions. One expression may be\na date and the other a datetime; a date value is treated as a datetime\nhaving the time part '00:00:00' where necessary. The unit for the\nresult (an integer) is given by the unit argument. The legal values for\nunit are the same as those listed in the description of the\nTIMESTAMPADD() function.\n\nThis function returns NULL if datetime_expr1 or datetime_expr2 is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TIME_FORMAT]
+declaration=time,format
+category=Date and Time Functions
+description=This is used like the DATE_FORMAT() function, but the format string may\ncontain format specifiers only for hours, minutes, seconds, and\nmicroseconds. Other specifiers produce a NULL or 0. TIME_FORMAT()\nreturns NULL if time or format is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TIME_TO_SEC]
+declaration=time
+category=Date and Time Functions
+description=Returns the time argument, converted to seconds. Returns NULL if time\nis NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TINYINT]
+declaration=M
+category=Data Types
+description=A very small integer. The signed range is -128 to 127. The unsigned\nrange is 0 to 255.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/numeric-type-syntax.html
+[TO_BASE64]
+declaration=str
+category=String Functions
+description=Converts the string argument to base-64 encoded form and returns the\nresult as a character string with the connection character set and\ncollation. If the argument is not a string, it is converted to a string\nbefore conversion takes place. The result is NULL if the argument is\nNULL. Base-64 encoded strings can be decoded using the FROM_BASE64()\nfunction.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[TO_DAYS]
+declaration=date
+category=Date and Time Functions
+description=Given a date date, returns a day number (the number of days since year\n0). Returns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TO_SECONDS]
+declaration=expr
+category=Date and Time Functions
+description=Given a date or datetime expr, returns the number of seconds since the\nyear 0. If expr is not a valid date or datetime value (including NULL),\nit returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[TRIM]
+declaration=[{BOTH | LEADING | TRAILING} [remstr] FROM] str
+category=String Functions
+description=FROM] str)\n\nReturns the string str with all remstr prefixes or suffixes removed. If\nnone of the specifiers BOTH, LEADING, or TRAILING is given, BOTH is\nassumed. remstr is optional and, if not specified, spaces are removed.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[TRUNCATE]
+declaration=X,D
+category=Numeric Functions
+description=Returns the number X, truncated to D decimal places. If D is 0, the\nresult has no decimal point or fractional part. D can be negative to\ncause D digits left of the decimal point of the value X to become zero.\nIf X or D is NULL, the function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/mathematical-functions.html
+[UCASE]
+declaration=str
+category=String Functions
+description=UCASE() is a synonym for UPPER().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[UNCOMPRESS]
+declaration=string_to_uncompress
+category=Encryption Functions
+description=Uncompresses a string compressed by the COMPRESS() function. If the\nargument is not a compressed value, the result is NULL; if\nstring_to_uncompress is NULL, the result is also NULL. This function\nrequires MySQL to have been compiled with a compression library such as\nzlib. Otherwise, the return value is always NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[UNCOMPRESSED_LENGTH]
+declaration=compressed_string
+category=Encryption Functions
+description=Returns the length that the compressed string had before being\ncompressed. Returns NULL if compressed_string is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[UNHEX]
+declaration=str
+category=String Functions
+description=For a string argument str, UNHEX(str) interprets each pair of\ncharacters in the argument as a hexadecimal number and converts it to\nthe byte represented by the number. The return value is a binary\nstring.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[UNIX_TIMESTAMP]
+declaration=[date]
+category=Date and Time Functions
+description=If UNIX_TIMESTAMP() is called with no date argument, it returns a Unix\ntimestamp representing seconds since '1970-01-01 00:00:00' UTC.\n\nIf UNIX_TIMESTAMP() is called with a date argument, it returns the\nvalue of the argument as seconds since '1970-01-01 00:00:00' UTC. The\nserver interprets date as a value in the session time zone and converts\nit to an internal Unix timestamp value in UTC. (Clients can set the\nsession time zone as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/time-zone-support.html.) The\ndate argument may be a DATE, DATETIME, or TIMESTAMP string, or a number\nin YYMMDD, YYMMDDhhmmss, YYYYMMDD, or YYYYMMDDhhmmss format. If the\nargument includes a time part, it may optionally include a fractional\nseconds part.\n\nThe return value is an integer if no argument is given or the argument\ndoes not include a fractional seconds part, or DECIMAL if an argument\nis given that includes a fractional seconds part.\n\nWhen the date argument is a TIMESTAMP column, UNIX_TIMESTAMP() returns\nthe internal timestamp value directly, with no implicit\n"string-to-Unix-timestamp" conversion.\n\nThe valid range of argument values is the same as for the TIMESTAMP\ndata type: '1970-01-01 00:00:01.000000' UTC to '2038-01-19\n03:14:07.999999' UTC for 32-bit platforms; for MySQL running on 64-bit\nplatforms, the valid range of argument values for UNIX_TIMESTAMP() is\n'1970-01-01 00:00:01.000000' UTC to '3001-01-19 03:14:07.999999' UTC\n(corresponding to 32536771199.999999 seconds).\n\nRegardless of MySQL version or platform architecture, if you pass an\nout-of-range date to UNIX_TIMESTAMP(), it returns 0. If date is NULL,\nit returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[UPDATEXML]
+declaration=xml_target, xpath_expr, new_xml
+category=XML
+description=This function replaces a single portion of a given fragment of XML\nmarkup xml_target with a new XML fragment new_xml, and then returns the\nchanged XML. The portion of xml_target that is replaced matches an\nXPath expression xpath_expr supplied by the user.\n\nIf no expression matching xpath_expr is found, or if multiple matches\nare found, the function returns the original xml_target XML fragment.\nAll three arguments should be strings. If any of the arguments to\nUpdateXML() are NULL, the function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/xml-functions.html
+[UPPER]
+declaration=str
+category=String Functions
+description=Returns the string str with all characters changed to uppercase\naccording to the current character set mapping, or NULL if str is NULL.\nThe default character set is utf8mb4.\n\nmysql> SELECT UPPER('Hej');\n -> 'HEJ'\n\nSee the description of LOWER() for information that also applies to\nUPPER(). This included information about how to perform lettercase\nconversion of binary strings (BINARY, VARBINARY, BLOB) for which these\nfunctions are ineffective, and information about case folding for\nUnicode character sets.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-functions.html
+[USER]
+declaration=
+category=Information Functions
+description=Returns the current MySQL user name and host name as a string in the\nutf8mb3 character set.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[UTC_DATE]
+declaration=
+category=Date and Time Functions
+description=Returns the current UTC date as a value in 'YYYY-MM-DD' or YYYYMMDD\nformat, depending on whether the function is used in string or numeric\ncontext.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[UTC_TIME]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current UTC time as a value in 'hh:mm:ss' or hhmmss format,\ndepending on whether the function is used in string or numeric context.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[UTC_TIMESTAMP]
+declaration=[fsp]
+category=Date and Time Functions
+description=Returns the current UTC date and time as a value in 'YYYY-MM-DD\nhh:mm:ss' or YYYYMMDDhhmmss format, depending on whether the function\nis used in string or numeric context.\n\nIf the fsp argument is given to specify a fractional seconds precision\nfrom 0 to 6, the return value includes a fractional seconds part of\nthat many digits.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[UUID]
+declaration=
+category=Miscellaneous Functions
+description=Returns a Universal Unique Identifier (UUID) generated according to RFC\n4122, "A Universally Unique IDentifier (UUID) URN Namespace"\n(http://www.ietf.org/rfc/rfc4122.txt).\n\nA UUID is designed as a number that is globally unique in space and\ntime. Two calls to UUID() are expected to generate two different\nvalues, even if these calls are performed on two separate devices not\nconnected to each other.\n\n*Warning*:\n\nAlthough UUID() values are intended to be unique, they are not\nnecessarily unguessable or unpredictable. If unpredictability is\nrequired, UUID values should be generated some other way.\n\nUUID() returns a value that conforms to UUID version 1 as described in\nRFC 4122. The value is a 128-bit number represented as a utf8mb3 string\nof five hexadecimal numbers in aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\nformat:\n\no The first three numbers are generated from the low, middle, and high\n parts of a timestamp. The high part also includes the UUID version\n number.\n\no The fourth number preserves temporal uniqueness in case the timestamp\n value loses monotonicity (for example, due to daylight saving time).\n\no The fifth number is an IEEE 802 node number that provides spatial\n uniqueness. A random number is substituted if the latter is not\n available (for example, because the host device has no Ethernet card,\n or it is unknown how to find the hardware address of an interface on\n the host operating system). In this case, spatial uniqueness cannot\n be guaranteed. Nevertheless, a collision should have very low\n probability.\n\n The MAC address of an interface is taken into account only on\n FreeBSD, Linux, and Windows. On other operating systems, MySQL uses a\n randomly generated 48-bit number.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[UUID_SHORT]
+declaration=
+category=Miscellaneous Functions
+description=Returns a "short" universal identifier as a 64-bit unsigned integer.\nValues returned by UUID_SHORT() differ from the string-format 128-bit\nidentifiers returned by the UUID() function and have different\nuniqueness properties. The value of UUID_SHORT() is guaranteed to be\nunique if the following conditions hold:\n\no The server_id value of the current server is between 0 and 255 and is\n unique among your set of source and replica servers\n\no You do not set back the system time for your server host between\n mysqld restarts\n\no You invoke UUID_SHORT() on average fewer than 16 million times per\n second between mysqld restarts\n\nThe UUID_SHORT() return value is constructed this way:\n\n (server_id & 255) << 56\n+ (server_startup_time_in_seconds << 24)\n+ incremented_variable++;\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[UUID_TO_BIN]
+declaration=string_uuid
+category=Miscellaneous Functions
+description=Converts a string UUID to a binary UUID and returns the result. (The\nIS_UUID() function description lists the permitted string UUID\nformats.) The return binary UUID is a VARBINARY(16) value. If the UUID\nargument is NULL, the return value is NULL. If any argument is invalid,\nan error occurs.\n\nUUID_TO_BIN() takes one or two arguments:\n\no The one-argument form takes a string UUID value. The binary result is\n in the same order as the string argument.\n\no The two-argument form takes a string UUID value and a flag value:\n\n o If swap_flag is 0, the two-argument form is equivalent to the\n one-argument form. The binary result is in the same order as the\n string argument.\n\n o If swap_flag is 1, the format of the return value differs: The\n time-low and time-high parts (the first and third groups of\n hexadecimal digits, respectively) are swapped. This moves the more\n rapidly varying part to the right and can improve indexing\n efficiency if the result is stored in an indexed column.\n\nTime-part swapping assumes the use of UUID version 1 values, such as\nare generated by the UUID() function. For UUID values produced by other\nmeans that do not follow version 1 format, time-part swapping provides\nno benefit. For details about version 1 format, see the UUID() function\ndescription.\n\nSuppose that you have the following string UUID value:\n\nmysql> SET @uuid = '6ccd780c-baba-1026-9564-5b8c656024db';\n\nTo convert the string UUID to binary with or without time-part\nswapping, use UUID_TO_BIN():\n\nmysql> SELECT HEX(UUID_TO_BIN(@uuid));\n+----------------------------------+\n| HEX(UUID_TO_BIN(@uuid)) |\n+----------------------------------+\n| 6CCD780CBABA102695645B8C656024DB |\n+----------------------------------+\nmysql> SELECT HEX(UUID_TO_BIN(@uuid, 0));\n+----------------------------------+\n| HEX(UUID_TO_BIN(@uuid, 0)) |\n+----------------------------------+\n| 6CCD780CBABA102695645B8C656024DB |\n+----------------------------------+\nmysql> SELECT HEX(UUID_TO_BIN(@uuid, 1));\n+----------------------------------+\n ...
+[VALIDATE_PASSWORD_STRENGTH]
+declaration=str
+category=Encryption Functions
+description=Given an argument representing a plaintext password, this function\nreturns an integer to indicate how strong the password is, or NULL if\nthe argument is NULL. The return value ranges from 0 (weak) to 100\n(strong).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/encryption-functions.html
+[VALUES]
+declaration=col_name
+category=Miscellaneous Functions
+description=In an INSERT ... ON DUPLICATE KEY UPDATE statement, you can use the\nVALUES(col_name) function in the UPDATE clause to refer to column\nvalues from the INSERT portion of the statement. In other words,\nVALUES(col_name) in the UPDATE clause refers to the value of col_name\nthat would be inserted, had no duplicate-key conflict occurred. This\nfunction is especially useful in multiple-row inserts. The VALUES()\nfunction is meaningful only in the ON DUPLICATE KEY UPDATE clause of\nINSERT statements and returns NULL otherwise. See\nhttps://dev.mysql.com/doc/refman/8.3/en/insert-on-duplicate.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/miscellaneous-functions.html
+[VARBINARY]
+declaration=M
+category=Data Types
+description=The VARBINARY type is similar to the VARCHAR type, but stores binary\nbyte strings rather than nonbinary character strings. M represents the\nmaximum column length in bytes.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[VARCHAR]
+declaration=M
+category=Data Types
+description=collation_name]\n\nA variable-length string. M represents the maximum column length in\ncharacters. The range of M is 0 to 65,535. The effective maximum length\nof a VARCHAR is subject to the maximum row size (65,535 bytes, which is\nshared among all columns) and the character set used. For example,\nutf8mb3 characters can require up to three bytes per character, so a\nVARCHAR column that uses the utf8mb3 character set can be declared to\nbe a maximum of 21,844 characters. See\nhttps://dev.mysql.com/doc/refman/8.3/en/column-count-limit.html.\n\nMySQL stores VARCHAR values as a 1-byte or 2-byte length prefix plus\ndata. The length prefix indicates the number of bytes in the value. A\nVARCHAR column uses one length byte if values require no more than 255\nbytes, two length bytes if values may require more than 255 bytes.\n\n*Note*:\n\nMySQL follows the standard SQL specification, and does not remove\ntrailing spaces from VARCHAR values.\n\nVARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the\nstandard SQL way to define that a VARCHAR column should use some\npredefined character set. MySQL uses utf8mb3 as this predefined\ncharacter set.\nhttps://dev.mysql.com/doc/refman/8.3/en/charset-national.html. NVARCHAR\nis shorthand for NATIONAL VARCHAR.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/string-type-syntax.html
+[VARIANCE]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the population standard variance of expr. VARIANCE() is a\nsynonym for the standard SQL function VAR_POP(), provided as a MySQL\nextension.\n\nIf there are no matching rows, or if expr is NULL, VARIANCE() returns\nNULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[VAR_POP]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the population standard variance of expr. It considers rows as\nthe whole population, not as a sample, so it has the number of rows as\nthe denominator. You can also use VARIANCE(), which is equivalent but\nis not standard SQL.\n\nIf there are no matching rows, or if expr is NULL, VAR_POP() returns\nNULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[VAR_SAMP]
+declaration=expr
+category=Aggregate Functions and Modifiers
+description=Returns the sample variance of expr. That is, the denominator is the\nnumber of rows minus one.\n\nIf there are no matching rows, or if expr is NULL, VAR_SAMP() returns\nNULL.\n\nThis function executes as a window function if over_clause is present.\nover_clause is as described in\nhttps://dev.mysql.com/doc/refman/8.3/en/window-functions-usage.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/aggregate-functions.html
+[VERSION]
+declaration=
+category=Information Functions
+description=Returns a string that indicates the MySQL server version. The string\nuses the utf8mb3 character set. The value might have a suffix in\naddition to the version number. See the description of the version\nsystem variable in\nhttps://dev.mysql.com/doc/refman/8.3/en/server-system-variables.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/information-functions.html
+[WAIT_FOR_EXECUTED_GTID_SET]
+declaration=gtid_set[, timeout]
+category=GTID
+description=Wait until the server has applied all of the transactions whose global\ntransaction identifiers are contained in gtid_set; that is, until the\ncondition GTID_SUBSET(gtid_subset, @@GLOBAL.gtid_executed) holds. See\nhttps://dev.mysql.com/doc/refman/8.3/en/replication-gtids-concepts.html\nfor a definition of GTID sets.\n\nIf a timeout is specified, and timeout seconds elapse before all of the\ntransactions in the GTID set have been applied, the function stops\nwaiting. timeout is optional, and the default timeout is 0 seconds, in\nwhich case the function always waits until all of the transactions in\nthe GTID set have been applied. timeout must be greater than or equal\nto 0; when running in strict SQL mode, a negative timeout value is\nimmediately rejected with an error (ER_WRONG_ARGUMENTS\n(https://dev.mysql.com/doc/mysql-errors/8.3/en/server-error-reference.html\n#error_er_wrong_arguments)); otherwise the function returns NULL,\nand raises a warning.\n\nWAIT_FOR_EXECUTED_GTID_SET() monitors all the GTIDs that are applied on\nthe server, including transactions that arrive from all replication\nchannels and user clients. It does not take into account whether\nreplication channels have been started or stopped.\n\nFor more information, see\nhttps://dev.mysql.com/doc/refman/8.3/en/replication-gtids.html.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/gtid-functions.html
+[WEEK]
+declaration=date[,mode]
+category=Date and Time Functions
+description=This function returns the week number for date. The two-argument form\nof WEEK() enables you to specify whether the week starts on Sunday or\nMonday and whether the return value should be in the range from 0 to 53\nor from 1 to 53. If the mode argument is omitted, the value of the\ndefault_week_format system variable is used. See\nhttps://dev.mysql.com/doc/refman/8.3/en/server-system-variables.html.\nFor a NULL date value, the function returns NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[WEEKDAY]
+declaration=date
+category=Date and Time Functions
+description=Returns the weekday index for date (0 = Monday, 1 = Tuesday, ... 6 =\nSunday). Returns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[WEEKOFYEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the calendar week of the date as a number in the range from 1\nto 53. Returns NULL if date is NULL.\n\nWEEKOFYEAR() is a compatibility function that is equivalent to\nWEEK(date,3).\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[WEIGHT_STRING]
+declaration=str [AS {CHAR|BINARY}(N
+category=String Functions
+description=This function returns the weight string for the input string. The\nreturn value is a binary string that represents the comparison and\nsorting value of the string, or NULL if the argument is NULL. It has\nthese properties:\n\no If WEIGHT_STRING(str1) = WEIGHT_STRING(str2), then str1 = str2 (str1\n and str2 are considered equal)\n\no If WEIGHT_STRING(str1) < WEIGHT_STRING(str2), then str1 < str2 (str1\n sorts before str2)\n\nWEIGHT_STRING() is a debugging function intended for internal use. Its\nbehavior can change without notice between MySQL versions. It can be\nused for testing and debugging of collations, especially if you are\nadding a new collation. See\nhttps://dev.mysql.com/doc/refman/8.3/en/adding-collation.html.\n\nThis list briefly summarizes the arguments. More details are given in\nthe discussion following the list.\n\no str: The input string expression.\n\no AS clause: Optional; cast the input string to a given type and\n length.\n\no flags: Optional; unused.\n\nThe input string, str, is a string expression. If the input is a\nnonbinary (character) string such as a CHAR, VARCHAR, or TEXT value,\nthe return value contains the collation weights for the string. If the\ninput is a binary (byte) string such as a BINARY, VARBINARY, or BLOB\nvalue, the return value is the same as the input (the weight for each\nbyte in a binary string is the byte value). If the input is NULL,\nWEIGHT_STRING() returns NULL.\n\nExamples:\n\nmysql> SET @s = _utf8mb4 'AB' COLLATE utf8mb4_0900_ai_ci;\nmysql> SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));\n+------+---------+------------------------+\n| @s | HEX(@s) | HEX(WEIGHT_STRING(@s)) |\n+------+---------+------------------------+\n| AB | 4142 | 1C471C60 |\n+------+---------+------------------------+\n\nmysql> SET @s = _utf8mb4 'ab' COLLATE utf8mb4_0900_ai_ci;\nmysql> SELECT @s, HEX(@s), HEX(WEIGHT_STRING(@s));\n+------+---------+------------------------+\n| @s | HEX(@s) | HEX(WEIGHT_STRING(@s)) |\n+------+---------+------------------------+\n ...
+[YEAR]
+declaration=date
+category=Date and Time Functions
+description=Returns the year for date, in the range 1000 to 9999, or 0 for the\n"zero" date. Returns NULL if date is NULL.\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
+[YEARWEEK]
+declaration=date
+category=Date and Time Functions
+description=Returns year and week for a date. The year in the result may be\ndifferent from the year in the date argument for the first and the last\nweek of the year. Returns NULL if date is NULL.\n\nThe mode argument works exactly like the mode argument to WEEK(). For\nthe single-argument syntax, a mode value of 0 is used. Unlike WEEK(),\nthe value of default_week_format does not influence YEARWEEK().\n\nURL: https://dev.mysql.com/doc/refman/8.3/en/date-and-time-functions.html
\ No newline at end of file
diff --git a/out/functions-postgresql.ini b/out/functions-postgresql.ini
new file mode 100644
index 00000000..bf2a8d0b
--- /dev/null
+++ b/out/functions-postgresql.ini
@@ -0,0 +1,2244 @@
+[ABBREV]
+declaration=inet
+category=Network Address Functions
+description=Creates an abbreviated display format as text. (The result is the same as\nthe inet output function produces; it is "abbreviated" only in comparison\nto the result of an explicit cast to text, which for historical reasons\nwill never suppress the netmask part.)
+[ABS]
+declaration=numeric_type
+category=Numeric/Math Functions
+description=Absolute value
+[ACLDEFAULT]
+declaration=type "char", ownerId oid
+category=Session Information Functions
+description=Constructs an aclitem array holding the default access privileges for an\nobject of type type belonging to the role with OID ownerId. This represents\nthe access privileges that will be assumed when an object's ACL entry is\nnull. (The default access privileges are described in Section 5.8.) The\ntype parameter must be one of 'c' for COLUMN, 'r' for TABLE and table-like\nobjects, 's' for SEQUENCE, 'd' for DATABASE, 'f' for FUNCTION or PROCEDURE,\n'l' for LANGUAGE, 'L' for LARGE OBJECT, 'n' for SCHEMA, 'p' for PARAMETER,\n't' for TABLESPACE, 'F' for FOREIGN DATA WRAPPER, 'S' for FOREIGN SERVER,\nor 'T' for TYPE or DOMAIN.
+[ACLEXPLODE]
+declaration=aclitem[]
+category=Session Information Functions
+description=Returns the aclitem array as a set of rows. If the grantee is the\npseudo-role PUBLIC, it is represented by zero in the grantee column. Each\ngranted privilege is represented as SELECT, INSERT, etc (see Table 5.1 for\na full list). Note that each privilege is broken out as a separate row, so\nonly one keyword appears in the privilege_type column.
+[ACOS]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse cosine, result in radians
+[ACOSD]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse cosine, result in degrees
+[ACOSH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse hyperbolic cosine
+[AGE1]
+name=AGE
+declaration=timestamp, timestamp
+category=Date/Time Functions
+description=Subtract arguments, producing a "symbolic" result that uses years and\nmonths, rather than just days
+[AGE2]
+name=AGE
+declaration=xid
+category=Session Information Functions
+description=Returns the number of transactions between the supplied transaction id and\nthe current transaction counter.
+[ANY_VALUE]
+declaration=anyelement
+category=Aggregate Functions
+description=Returns an arbitrary value from the non-null input values.
+[AREA]
+declaration=geometric_type
+category=Geometric Functions
+description=Computes area. Available for box, path, circle. A path input must be\nclosed, else NULL is returned. Also, if the path is self-intersecting, the\nresult may be meaningless.
+[ARRAY_AGG]
+declaration=anynonarray ORDER BY input_sort_columns
+category=Aggregate Functions
+description=Collects all the input values, including nulls, into an array.
+[ARRAY_APPEND]
+declaration=anycompatiblearray, anycompatible
+category=Array Functions
+description=Appends an element to the end of an array (same as the anycompatiblearray\n|| anycompatible operator).
+[ARRAY_CAT]
+declaration=anycompatiblearray, anycompatiblearray
+category=Array Functions
+description=Concatenates two arrays (same as the anycompatiblearray ||\nanycompatiblearray operator).
+[ARRAY_DIMS]
+declaration=anyarray
+category=Array Functions
+description=Returns a text representation of the array's dimensions.
+[ARRAY_FILL]
+declaration=anyelement, integer[] [, integer[] ]
+category=Array Functions
+description=Returns an array filled with copies of the given value, having dimensions\nof the lengths specified by the second argument. The optional third\nargument supplies lower-bound values for each dimension (which default to\nall 1).
+[ARRAY_LENGTH]
+declaration=anyarray, integer
+category=Array Functions
+description=Returns the length of the requested array dimension. (Produces NULL instead\nof 0 for empty or missing array dimensions.)
+[ARRAY_LOWER]
+declaration=anyarray, integer
+category=Array Functions
+description=Returns the lower bound of the requested array dimension.
+[ARRAY_NDIMS]
+declaration=anyarray
+category=Array Functions
+description=Returns the number of dimensions of the array.
+[ARRAY_POSITION]
+declaration=anycompatiblearray, anycompatible [, integer ]
+category=Array Functions
+description=Returns the subscript of the first occurrence of the second argument in the\narray, or NULL if it's not present. If the third argument is given, the\nsearch begins at that subscript. The array must be one-dimensional.\nComparisons are done using IS NOT DISTINCT FROM semantics, so it is\npossible to search for NULL.
+[ARRAY_POSITIONS]
+declaration=anycompatiblearray, anycompatible
+category=Array Functions
+description=Returns an array of the subscripts of all occurrences of the second\nargument in the array given as first argument. The array must be\none-dimensional. Comparisons are done using IS NOT DISTINCT FROM semantics,\nso it is possible to search for NULL. NULL is returned only if the array is\nNULL; if the value is not found in the array, an empty array is returned.
+[ARRAY_PREPEND]
+declaration=anycompatible, anycompatiblearray
+category=Array Functions
+description=Prepends an element to the beginning of an array (same as the anycompatible\n|| anycompatiblearray operator).
+[ARRAY_REMOVE]
+declaration=anycompatiblearray, anycompatible
+category=Array Functions
+description=Removes all elements equal to the given value from the array. The array\nmust be one-dimensional. Comparisons are done using IS NOT DISTINCT FROM\nsemantics, so it is possible to remove NULLs.
+[ARRAY_REPLACE]
+declaration=anycompatiblearray, anycompatible, anycompatible
+category=Array Functions
+description=Replaces each array element equal to the second argument with the third\nargument.
+[ARRAY_SAMPLE]
+declaration=array anyarray, n integer
+category=Array Functions
+description=Returns an array of n items randomly selected from array. n may not exceed\nthe length of array's first dimension. If array is multi-dimensional, an\n"item" is a slice having a given first subscript.
+[ARRAY_SHUFFLE]
+declaration=anyarray
+category=Array Functions
+description=Randomly shuffles the first dimension of the array.
+[ARRAY_TO_JSON]
+declaration=anyarray [, boolean ]
+category=JSON Functions
+description=Converts an SQL array to a JSON array. The behavior is the same as to_json\nexcept that line feeds will be added between top-level array elements if\nthe optional boolean parameter is true.
+[ARRAY_TO_STRING]
+declaration=array anyarray, delimiter text [, null_string text ]
+category=Array Functions
+description=Converts each array element to its text representation, and concatenates\nthose separated by the delimiter string. If null_string is given and is not\nNULL, then NULL array entries are represented by that string; otherwise,\nthey are omitted. See also string_to_array.
+[ARRAY_TO_TSVECTOR]
+declaration=text[]
+category=Text Search Functions
+description=Converts an array of text strings to a tsvector. The given strings are used\nas lexemes as-is, without further processing. Array elements must not be\nempty strings or NULL.
+[ARRAY_UPPER]
+declaration=anyarray, integer
+category=Array Functions
+description=Returns the upper bound of the requested array dimension.
+[ASCII]
+declaration=text
+category=String Functions
+description=Returns the numeric code of the first character of the argument. In UTF8\nencoding, returns the Unicode code point of the character. In other\nmultibyte encodings, the argument must be an ASCII character.
+[ASIN]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse sine, result in radians
+[ASIND]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse sine, result in degrees
+[ASINH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse hyperbolic sine
+[ATAN]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse tangent, result in radians
+[ATAN2]
+declaration=y double precision, x double precision
+category=Numeric/Math Functions
+description=Inverse tangent of y/x, result in radians
+[ATAN2D]
+declaration=y double precision, x double precision
+category=Numeric/Math Functions
+description=Inverse tangent of y/x, result in degrees
+[ATAND]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse tangent, result in degrees
+[ATANH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Inverse hyperbolic tangent
+[BIT_COUNT]
+declaration=bit
+category=Bit String Functions
+description=Returns the number of bits set in the bit string (also known as\n"popcount").
+[BIT_LENGTH1]
+name=BIT_LENGTH
+declaration=text
+category=String Functions
+description=Returns number of bits in the string (8 times the octet_length).
+[BIT_LENGTH2]
+name=BIT_LENGTH
+declaration=bytea
+category=Binary String Functions
+description=Returns number of bits in the binary string (8 times the octet_length).
+[BIT_LENGTH3]
+name=BIT_LENGTH
+declaration=bit
+category=Bit String Functions
+description=Returns number of bits in the bit string.
+[BOOL_AND]
+declaration=boolean
+category=Aggregate Functions
+description=Returns true if all non-null input values are true, otherwise false.
+[BOOL_OR]
+declaration=boolean
+category=Aggregate Functions
+description=Returns true if any non-null input value is true, otherwise false.
+[BOUND_BOX]
+declaration=box, box
+category=Geometric Functions
+description=Computes bounding box of two boxes.
+[BOX]
+declaration=circle
+category=Geometric Functions
+description=Computes box inscribed within the circle.
+[BRIN_DESUMMARIZE_RANGE]
+declaration=index regclass, blockNumber bigint
+category=System Administration Functions
+description=Removes the BRIN index tuple that summarizes the page range covering the\ngiven table block, if there is one.
+[BRIN_SUMMARIZE_NEW_VALUES]
+declaration=index regclass
+category=System Administration Functions
+description=Scans the specified BRIN index to find page ranges in the base table that\nare not currently summarized by the index; for any such range it creates a\nnew summary index tuple by scanning those table pages. Returns the number\nof new page range summaries that were inserted into the index.
+[BRIN_SUMMARIZE_RANGE]
+declaration=index regclass, blockNumber bigint
+category=System Administration Functions
+description=Summarizes the page range covering the given block, if not already\nsummarized. This is like brin_summarize_new_values except that it only\nprocesses the page range that covers the given table block number.
+[BROADCAST]
+declaration=inet
+category=Network Address Functions
+description=Computes the broadcast address for the address's network.
+[BTRIM1]
+name=BTRIM
+declaration=string text [, characters text ]
+category=String Functions
+description=Removes the longest string containing only characters in characters (a\nspace by default) from the start and end of string.
+[BTRIM2]
+name=BTRIM
+declaration=bytes bytea, bytesremoved bytea
+category=Binary String Functions
+description=Removes the longest string containing only bytes appearing in bytesremoved\nfrom the start and end of bytes.
+[CARDINALITY]
+declaration=anyarray
+category=Array Functions
+description=Returns the total number of elements in the array, or 0 if the array is\nempty.
+[CBRT]
+declaration=double precision
+category=Numeric/Math Functions
+description=Cube root
+[CENTER]
+declaration=geometric_type
+category=Geometric Functions
+description=Computes center point. Available for box, circle.
+[CHARACTER_LENGTH]
+declaration=text
+category=String Functions
+description=Returns number of characters in the string.
+[CHR]
+declaration=integer
+category=String Functions
+description=Returns the character with the given code. In UTF8 encoding the argument is\ntreated as a Unicode code point. In other multibyte encodings the argument\nmust designate an ASCII character. chr(0) is disallowed because text data\ntypes cannot store that character.
+[CIRCLE]
+declaration=box
+category=Geometric Functions
+description=Computes smallest circle enclosing box.
+[CLOCK_TIMESTAMP]
+declaration=
+category=Date/Time Functions
+description=Current date and time (changes during statement execution); see Section\n9.9.5
+[COL_DESCRIPTION]
+declaration=table oid, column integer
+category=Session Information Functions
+description=Returns the comment for a table column, which is specified by the OID of\nits table and its column number. (obj_description cannot be used for table\ncolumns, since columns do not have OIDs of their own.)
+[CONCAT]
+declaration=val1 "any" [, val2 "any" [, ...] ]
+category=String Functions
+description=Concatenates the text representations of all the arguments. NULL arguments\nare ignored.
+[CONCAT_WS]
+declaration=sep text, val1 "any" [, val2 "any" [, ...] ]
+category=String Functions
+description=Concatenates all but the first argument, with separators. The first\nargument is used as the separator string, and should not be NULL. Other\nNULL arguments are ignored.
+[CONVERT]
+declaration=bytes bytea, src_encoding name, dest_encoding name
+category=Binary String Functions
+description=Converts a binary string representing text in encoding src_encoding to a\nbinary string in encoding dest_encoding (see Section 23.3.4 for available\nconversions).
+[CONVERT_FROM]
+declaration=bytes bytea, src_encoding name
+category=Binary String Functions
+description=Converts a binary string representing text in encoding src_encoding to text\nin the database encoding (see Section 23.3.4 for available conversions).
+[CONVERT_TO]
+declaration=string text, dest_encoding name
+category=Binary String Functions
+description=Converts a text string (in the database encoding) to a binary string\nencoded in encoding dest_encoding (see Section 23.3.4 for available\nconversions).
+[COS]
+declaration=double precision
+category=Numeric/Math Functions
+description=Cosine, argument in radians
+[COSD]
+declaration=double precision
+category=Numeric/Math Functions
+description=Cosine, argument in degrees
+[COSH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Hyperbolic cosine
+[COT]
+declaration=double precision
+category=Numeric/Math Functions
+description=Cotangent, argument in radians
+[COTD]
+declaration=double precision
+category=Numeric/Math Functions
+description=Cotangent, argument in degrees
+[COUNT]
+declaration=*
+category=Aggregate Functions
+description=Computes the number of input rows.
+[CUME_DIST1]
+name=CUME_DIST
+declaration=args
+category=Aggregate Functions
+description=Computes the cumulative distribution, that is (number of rows preceding or\npeers with hypothetical row) / (total rows). The value thus ranges from 1/N\nto 1.
+[CUME_DIST2]
+name=CUME_DIST
+declaration=
+category=Window Functions
+description=Returns the cumulative distribution, that is (number of partition rows\npreceding or peers with current row) / (total partition rows). The value\nthus ranges from 1/N to 1.
+[CURRENT_DATABASE]
+declaration=
+category=Session Information Functions
+description=Returns the name of the current database. (Databases are called "catalogs"\nin the SQL standard, so current_catalog is the standard's spelling.)
+[CURRENT_QUERY]
+declaration=
+category=Session Information Functions
+description=Returns the text of the currently executing query, as submitted by the\nclient (which might contain more than one statement).
+[CURRENT_SETTING]
+declaration=setting_name text [, missing_ok boolean ]
+category=System Administration Functions
+description=Returns the current value of the setting setting_name. If there is no such\nsetting, current_setting throws an error unless missing_ok is supplied and\nis true (in which case NULL is returned). This function corresponds to the\nSQL command SHOW.
+[CURRVAL]
+declaration=regclass
+category=Sequence Manipulation Functions
+description=Returns the value most recently obtained by nextval for this sequence in\nthe current session. (An error is reported if nextval has never been called\nfor this sequence in this session.) Because this is returning a\nsession-local value, it gives a predictable answer whether or not other\nsessions have executed nextval since the current session did.
+[DATE_ADD]
+declaration=timestamp with time zone, interval [, text ]
+category=Date/Time Functions
+description=Add an interval to a timestamp with time zone, computing times of day and\ndaylight-savings adjustments according to the time zone named by the third\nargument, or the current TimeZone setting if that is omitted. The form with\ntwo arguments is equivalent to the timestamp with time zone + interval\noperator.
+[DATE_PART]
+declaration=text, timestamp
+category=Date/Time Functions
+description=Get timestamp subfield (equivalent to extract); see Section 9.9.1
+[DATE_SUBTRACT]
+declaration=timestamp with time zone, interval [, text ]
+category=Date/Time Functions
+description=Subtract an interval from a timestamp with time zone, computing times of\nday and daylight-savings adjustments according to the time zone named by\nthe third argument, or the current TimeZone setting if that is omitted. The\nform with two arguments is equivalent to the timestamp with time zone -\ninterval operator.
+[DATE_TRUNC]
+declaration=text, timestamp
+category=Date/Time Functions
+description=Truncate to specified precision; see Section 9.9.2
+[DECODE]
+declaration=string text, format text
+category=Binary String Functions
+description=Decodes binary data from a textual representation; supported format values\nare the same as for encode.
+[DEGREES]
+declaration=double precision
+category=Numeric/Math Functions
+description=Converts radians to degrees
+[DENSE_RANK1]
+name=DENSE_RANK
+declaration=args
+category=Aggregate Functions
+description=Computes the rank of the hypothetical row, without gaps; this function\neffectively counts peer groups.
+[DENSE_RANK2]
+name=DENSE_RANK
+declaration=
+category=Window Functions
+description=Returns the rank of the current row, without gaps; this function\neffectively counts peer groups.
+[DIAGONAL]
+declaration=box
+category=Geometric Functions
+description=Extracts box's diagonal as a line segment (same as lseg(box)).
+[DIAMETER]
+declaration=circle
+category=Geometric Functions
+description=Computes diameter of circle.
+[DIV]
+declaration=y numeric, x numeric
+category=Numeric/Math Functions
+description=Integer quotient of y/x (truncates towards zero)
+[ENCODE]
+declaration=bytes bytea, format text
+category=Binary String Functions
+description=Encodes binary data into a textual representation; supported format values\nare: base64, escape, hex.
+[ENUM_FIRST]
+declaration=anyenum
+category=Enum Support Functions
+description=Returns the first value of the input enum type.
+[ENUM_LAST]
+declaration=anyenum
+category=Enum Support Functions
+description=Returns the last value of the input enum type.
+[ENUM_RANGE]
+declaration=anyenum
+category=Enum Support Functions
+description=Returns all values of the input enum type in an ordered array.
+[ERF]
+declaration=double precision
+category=Numeric/Math Functions
+description=Error function
+[ERFC]
+declaration=double precision
+category=Numeric/Math Functions
+description=Complementary error function (1 - erf(x), without loss of precision for\nlarge inputs)
+[EVERY]
+declaration=boolean
+category=Aggregate Functions
+description=This is the SQL standard's equivalent to bool_and.
+[EXTRACT]
+declaration=field from timestamp
+category=Date/Time Functions
+description=Get timestamp subfield; see Section 9.9.1
+[FACTORIAL]
+declaration=bigint
+category=Numeric/Math Functions
+description=Factorial
+[FAMILY]
+declaration=inet
+category=Network Address Functions
+description=Returns the address's family: 4 for IPv4, 6 for IPv6.
+[FIRST_VALUE]
+declaration=value anyelement
+category=Window Functions
+description=Returns value evaluated at the row that is the first row of the window\nframe.
+[FORMAT]
+declaration=formatstr text [, formatarg "any" [, ...] ]
+category=String Functions
+description=Formats arguments according to a format string; see Section 9.4.1. This\nfunction is similar to the C function sprintf.
+[FORMAT_TYPE]
+declaration=type oid, typemod integer
+category=Session Information Functions
+description=Returns the SQL name for a data type that is identified by its type OID and\npossibly a type modifier. Pass NULL for the type modifier if no specific\nmodifier is known.
+[GCD]
+declaration=numeric_type, numeric_type
+category=Numeric/Math Functions
+description=Greatest common divisor (the largest positive number that divides both\ninputs with no remainder); returns 0 if both inputs are zero; available for\ninteger, bigint, and numeric
+[GET_BIT1]
+name=GET_BIT
+declaration=bytes bytea, n bigint
+category=Binary String Functions
+description=Extracts n'th bit from binary string.
+[GET_BIT2]
+name=GET_BIT
+declaration=bits bit, n integer
+category=Bit String Functions
+description=Extracts n'th bit from bit string; the first (leftmost) bit is bit 0.
+[GET_BYTE]
+declaration=bytes bytea, n integer
+category=Binary String Functions
+description=Extracts n'th byte from binary string.
+[GET_CURRENT_TS_CONFIG]
+declaration=
+category=Text Search Functions
+description=Returns the OID of the current default text search configuration (as set by\ndefault_text_search_config).
+[GIN_CLEAN_PENDING_LIST]
+declaration=index regclass
+category=System Administration Functions
+description=Cleans up the "pending" list of the specified GIN index by moving entries\nin it, in bulk, to the main GIN data structure. Returns the number of pages\nremoved from the pending list. If the argument is a GIN index built with\nthe fastupdate option disabled, no cleanup happens and the result is zero,\nbecause the index doesn't have a pending list. See Section 64.4.4.1 and\nSection 64.4.5 for details about the pending list and fastupdate option.
+[GROUPING]
+declaration=group_by_expression(s
+category=Aggregate Functions
+description=Returns a bit mask indicating which GROUP BY expressions are not included\nin the current grouping set. Bits are assigned with the rightmost argument\ncorresponding to the least-significant bit; each bit is 0 if the\ncorresponding expression is included in the grouping criteria of the\ngrouping set generating the current result row, and 1 if it is not\nincluded.
+[HAS_ANY_COLUMN_PRIVILEGE]
+declaration=[ user name or oid, ] table text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for any column of table? This succeeds either if\nthe privilege is held for the whole table, or if there is a column-level\ngrant of the privilege for at least one column. Allowable privilege types\nare SELECT, INSERT, UPDATE, and REFERENCES.
+[HAS_COLUMN_PRIVILEGE]
+declaration=[ user name or oid, ] table text or oid, column text or smallint, privilege text
+category=Session Information Functions
+description=Does user have privilege for the specified table column? This succeeds\neither if the privilege is held for the whole table, or if there is a\ncolumn-level grant of the privilege for the column. The column can be\nspecified by name or by attribute number (pg_attribute.attnum). Allowable\nprivilege types are SELECT, INSERT, UPDATE, and REFERENCES.
+[HAS_DATABASE_PRIVILEGE]
+declaration=[ user name or oid, ] database text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for database? Allowable privilege types are\nCREATE, CONNECT, TEMPORARY, and TEMP (which is equivalent to TEMPORARY).
+[HAS_FOREIGN_DATA_WRAPPER_PRIVILEGE]
+declaration=[ user name or oid, ] fdw text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for foreign-data wrapper? The only allowable\nprivilege type is USAGE.
+[HAS_FUNCTION_PRIVILEGE]
+declaration=[ user name or oid, ] function text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for function? The only allowable privilege type is\nEXECUTE.
+[HAS_LANGUAGE_PRIVILEGE]
+declaration=[ user name or oid, ] language text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for language? The only allowable privilege type is\nUSAGE.
+[HAS_PARAMETER_PRIVILEGE]
+declaration=[ user name or oid, ] parameter text, privilege text
+category=Session Information Functions
+description=Does user have privilege for configuration parameter? The parameter name is\ncase-insensitive. Allowable privilege types are SET and ALTER SYSTEM.
+[HAS_SCHEMA_PRIVILEGE]
+declaration=[ user name or oid, ] schema text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for schema? Allowable privilege types are CREATE\nand USAGE.
+[HAS_SEQUENCE_PRIVILEGE]
+declaration=[ user name or oid, ] sequence text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for sequence? Allowable privilege types are USAGE,\nSELECT, and UPDATE.
+[HAS_SERVER_PRIVILEGE]
+declaration=[ user name or oid, ] server text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for foreign server? The only allowable privilege\ntype is USAGE.
+[HAS_TABLESPACE_PRIVILEGE]
+declaration=[ user name or oid, ] tablespace text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for tablespace? The only allowable privilege type\nis CREATE.
+[HAS_TABLE_PRIVILEGE]
+declaration=[ user name or oid, ] table text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for table? Allowable privilege types are SELECT,\nINSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, and MAINTAIN.
+[HAS_TYPE_PRIVILEGE]
+declaration=[ user name or oid, ] type text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for data type? The only allowable privilege type\nis USAGE. When specifying a type by name rather than by OID, the allowed\ninput is the same as for the regtype data type (see Section 8.19).
+[HEIGHT]
+declaration=box
+category=Geometric Functions
+description=Computes vertical size of box.
+[HOST]
+declaration=inet
+category=Network Address Functions
+description=Returns the IP address as text, ignoring the netmask.
+[HOSTMASK]
+declaration=inet
+category=Network Address Functions
+description=Computes the host mask for the address's network.
+[ICU_UNICODE_VERSION]
+declaration=
+category=Session Information Functions
+description=Returns a string representing the version of Unicode used by ICU, if the\nserver was built with ICU support; otherwise returns NULL
+[INET_CLIENT_ADDR]
+declaration=
+category=Session Information Functions
+description=Returns the IP address of the current client, or NULL if the current\nconnection is via a Unix-domain socket.
+[INET_CLIENT_PORT]
+declaration=
+category=Session Information Functions
+description=Returns the IP port number of the current client, or NULL if the current\nconnection is via a Unix-domain socket.
+[INET_MERGE]
+declaration=inet, inet
+category=Network Address Functions
+description=Computes the smallest network that includes both of the given networks.
+[INET_SAME_FAMILY]
+declaration=inet, inet
+category=Network Address Functions
+description=Tests whether the addresses belong to the same IP family.
+[INET_SERVER_ADDR]
+declaration=
+category=Session Information Functions
+description=Returns the IP address on which the server accepted the current connection,\nor NULL if the current connection is via a Unix-domain socket.
+[INET_SERVER_PORT]
+declaration=
+category=Session Information Functions
+description=Returns the IP port number on which the server accepted the current\nconnection, or NULL if the current connection is via a Unix-domain socket.
+[INITCAP]
+declaration=text
+category=String Functions
+description=Converts the first letter of each word to upper case and the rest to lower\ncase. Words are sequences of alphanumeric characters separated by\nnon-alphanumeric characters.
+[ISCLOSED]
+declaration=path
+category=Geometric Functions
+description=Is path closed?
+[ISEMPTY1]
+name=ISEMPTY
+declaration=anyrange
+category=Range Functions
+description=Is the range empty?
+[ISEMPTY2]
+name=ISEMPTY
+declaration=anymultirange
+category=Range Functions
+description=Is the multirange empty?
+[ISFINITE]
+declaration=date
+category=Date/Time Functions
+description=Test for finite date (not +/-infinity)
+[ISOPEN]
+declaration=path
+category=Geometric Functions
+description=Is path open?
+[JSON]
+declaration=expression [ FORMAT JSON [ ENCODING UTF8 ]] [ { WITH | WITHOUT } UNIQUE [ KEYS ]]
+category=JSON Functions
+description=Converts a given expression specified as text or bytea string (in UTF8\nencoding) into a JSON value. If expression is NULL, an SQL null value is\nreturned. If WITH UNIQUE is specified, the expression must not contain any\nduplicate object keys.
+[JSONB_AGG]
+declaration=anyelement ORDER BY input_sort_columns
+category=Aggregate Functions
+description=Collects all the input values, including nulls, into a JSON array. Values\nare converted to JSON as per to_json or to_jsonb.
+[JSONB_AGG_STRICT]
+declaration=anyelement
+category=Aggregate Functions
+description=Collects all the input values, skipping nulls, into a JSON array. Values\nare converted to JSON as per to_json or to_jsonb.
+[JSONB_ARRAY_ELEMENTS]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON array into a set of JSON values.
+[JSONB_ARRAY_ELEMENTS_TEXT]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON array into a set of text values.
+[JSONB_ARRAY_LENGTH]
+declaration=jsonb
+category=JSON Functions
+description=Returns the number of elements in the top-level JSON array.
+[JSONB_BUILD_ARRAY]
+declaration=VARIADIC "any"
+category=JSON Functions
+description=Builds a possibly-heterogeneously-typed JSON array out of a variadic\nargument list. Each argument is converted as per to_json or to_jsonb.
+[JSONB_BUILD_OBJECT]
+declaration=VARIADIC "any"
+category=JSON Functions
+description=Builds a JSON object out of a variadic argument list. By convention, the\nargument list consists of alternating keys and values. Key arguments are\ncoerced to text; value arguments are converted as per to_json or to_jsonb.
+[JSONB_EACH]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON object into a set of key/value pairs.
+[JSONB_EACH_TEXT]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON object into a set of key/value pairs. The\nreturned values will be of type text.
+[JSONB_EXTRACT_PATH]
+declaration=from_json jsonb, VARIADIC path_elems text[]
+category=JSON Functions
+description=Extracts JSON sub-object at the specified path. (This is functionally\nequivalent to the #> operator, but writing the path out as a variadic\nlist can be more convenient in some cases.)
+[JSONB_EXTRACT_PATH_TEXT]
+declaration=from_json jsonb, VARIADIC path_elems text[]
+category=JSON Functions
+description=Extracts JSON sub-object at the specified path as text. (This is\nfunctionally equivalent to the #>> operator.)
+[JSONB_INSERT]
+declaration=target jsonb, path text[], new_value jsonb [, insert_after boolean ]
+category=JSON Functions
+description=Returns target with new_value inserted. If the item designated by the path\nis an array element, new_value will be inserted before that item if\ninsert_after is false (which is the default), or after it if insert_after\nis true. If the item designated by the path is an object field, new_value\nwill be inserted only if the object does not already contain that key. All\nearlier steps in the path must exist, or the target is returned unchanged.\nAs with the path oriented operators, negative integers that appear in the\npath count from the end of JSON arrays. If the last path step is an array\nindex that is out of range, the new value is added at the beginning of the\narray if the index is negative, or at the end of the array if it is\npositive.
+[JSONB_OBJECT]
+declaration=text[]
+category=JSON Functions
+description=Builds a JSON object out of a text array. The array must have either\nexactly one dimension with an even number of members, in which case they\nare taken as alternating key/value pairs, or two dimensions such that each\ninner array has exactly two elements, which are taken as a key/value pair.\nAll values are converted to JSON strings.
+[JSONB_OBJECT_AGG]
+declaration=key "any", value "any" ORDER BY input_sort_columns
+category=Aggregate Functions
+description=Collects all the key/value pairs into a JSON object. Key arguments are\ncoerced to text; value arguments are converted as per to_json or to_jsonb.\nValues can be null, but keys cannot.
+[JSONB_OBJECT_AGG_STRICT]
+declaration=key "any", value "any"
+category=Aggregate Functions
+description=Collects all the key/value pairs into a JSON object. Key arguments are\ncoerced to text; value arguments are converted as per to_json or to_jsonb.\nThe key can not be null. If the value is null then the entry is skipped,
+[JSONB_OBJECT_AGG_UNIQUE]
+declaration=key "any", value "any"
+category=Aggregate Functions
+description=Collects all the key/value pairs into a JSON object. Key arguments are\ncoerced to text; value arguments are converted as per to_json or to_jsonb.\nValues can be null, but keys cannot. If there is a duplicate key an error\nis thrown.
+[JSONB_OBJECT_AGG_UNIQUE_STRICT]
+declaration=key "any", value "any"
+category=Aggregate Functions
+description=Collects all the key/value pairs into a JSON object. Key arguments are\ncoerced to text; value arguments are converted as per to_json or to_jsonb.\nThe key can not be null. If the value is null then the entry is skipped. If\nthere is a duplicate key an error is thrown.
+[JSONB_OBJECT_KEYS]
+declaration=jsonb
+category=JSON Functions
+description=Returns the set of keys in the top-level JSON object.
+[JSONB_PATH_EXISTS]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=Checks whether the JSON path returns any item for the specified JSON value.\n(This is useful only with SQL-standard JSON path expressions, not predicate\ncheck expressions, since those always return a value.) If the vars argument\nis specified, it must be a JSON object, and its fields provide named values\nto be substituted into the jsonpath expression. If the silent argument is\nspecified and is true, the function suppresses the same errors as the @?\nand @@ operators do.
+[JSONB_PATH_MATCH]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=Returns the result of a JSON path predicate check for the specified JSON\nvalue. (This is useful only with predicate check expressions, not\nSQL-standard JSON path expressions, since it will either fail or return\nNULL if the path result is not a single boolean value.) The optional vars\nand silent arguments act the same as for jsonb_path_exists.
+[JSONB_PATH_QUERY]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=Returns all JSON items returned by the JSON path for the specified JSON\nvalue. For SQL-standard JSON path expressions it returns the JSON values\nselected from target. For predicate check expressions it returns the result\nof the predicate check: true, false, or null. The optional vars and silent\narguments act the same as for jsonb_path_exists.
+[JSONB_PATH_QUERY_ARRAY]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=Returns all JSON items returned by the JSON path for the specified JSON\nvalue, as a JSON array. The parameters are the same as for\njsonb_path_query.
+[JSONB_PATH_QUERY_FIRST]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=Returns the first JSON item returned by the JSON path for the specified\nJSON value, or NULL if there are no results. The parameters are the same as\nfor jsonb_path_query.
+[JSONB_PATH_QUERY_FIRST_TZ]
+declaration=target jsonb, path jsonpath [, vars jsonb [, silent boolean ]]
+category=JSON Functions
+description=These functions act like their counterparts described above without the _tz\nsuffix, except that these functions support comparisons of date/time values\nthat require timezone-aware conversions. The example below requires\ninterpretation of the date-only value 2015-08-02 as a timestamp with time\nzone, so the result depends on the current TimeZone setting. Due to this\ndependency, these functions are marked as stable, which means these\nfunctions cannot be used in indexes. Their counterparts are immutable, and\nso can be used in indexes; but they will throw errors if asked to make such\ncomparisons.
+[JSONB_POPULATE_RECORD]
+declaration=base anyelement, from_json jsonb
+category=JSON Functions
+description=Expands the top-level JSON object to a row having the composite type of the\nbase argument. The JSON object is scanned for fields whose names match\ncolumn names of the output row type, and their values are inserted into\nthose columns of the output. (Fields that do not correspond to any output\ncolumn name are ignored.) In typical use, the value of base is just NULL,\nwhich means that any output columns that do not match any object field will\nbe filled with nulls. However, if base isn't NULL then the values it\ncontains will be used for unmatched columns.
+[JSONB_POPULATE_RECORDSET]
+declaration=base anyelement, from_json jsonb
+category=JSON Functions
+description=Expands the top-level JSON array of objects to a set of rows having the\ncomposite type of the base argument. Each element of the JSON array is\nprocessed as described above for json[b]_populate_record.
+[JSONB_POPULATE_RECORD_VALID]
+declaration=base anyelement, from_json json
+category=JSON Functions
+description=Function for testing jsonb_populate_record. Returns true if the input\njsonb_populate_record would finish without an error for the given input\nJSON object; that is, it's valid input, false otherwise.
+[JSONB_PRETTY]
+declaration=jsonb
+category=JSON Functions
+description=Converts the given JSON value to pretty-printed, indented text.
+[JSONB_SET]
+declaration=target jsonb, path text[], new_value jsonb [, create_if_missing boolean ]
+category=JSON Functions
+description=Returns target with the item designated by path replaced by new_value, or\nwith new_value added if create_if_missing is true (which is the default)\nand the item designated by path does not exist. All earlier steps in the\npath must exist, or the target is returned unchanged. As with the path\noriented operators, negative integers that appear in the path count from\nthe end of JSON arrays. If the last path step is an array index that is out\nof range, and create_if_missing is true, the new value is added at the\nbeginning of the array if the index is negative, or at the end of the array\nif it is positive.
+[JSONB_SET_LAX]
+declaration=target jsonb, path text[], new_value jsonb [, create_if_missing boolean [, null_value_treatment text ]]
+category=JSON Functions
+description=If new_value is not NULL, behaves identically to jsonb_set. Otherwise\nbehaves according to the value of null_value_treatment which must be one of\n'raise_exception', 'use_json_null', 'delete_key', or 'return_target'. The\ndefault is 'use_json_null'.
+[JSONB_STRIP_NULLS]
+declaration=jsonb
+category=JSON Functions
+description=Deletes all object fields that have null values from the given JSON value,\nrecursively. Null values that are not object fields are untouched.
+[JSONB_TO_RECORD]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON object to a row having the composite type\ndefined by an AS clause. (As with all functions returning record, the\ncalling query must explicitly define the structure of the record with an AS\nclause.) The output record is filled from fields of the JSON object, in the\nsame way as described above for json[b]_populate_record. Since there is no\ninput record value, unmatched columns are always filled with nulls.
+[JSONB_TO_RECORDSET]
+declaration=jsonb
+category=JSON Functions
+description=Expands the top-level JSON array of objects to a set of rows having the\ncomposite type defined by an AS clause. (As with all functions returning\nrecord, the calling query must explicitly define the structure of the\nrecord with an AS clause.) Each element of the JSON array is processed as\ndescribed above for json[b]_populate_record.
+[JSONB_TO_TSVECTOR]
+declaration=[ config regconfig, ] document jsonb, filter jsonb
+category=Text Search Functions
+description=Selects each item in the JSON document that is requested by the filter and\nconverts each one to a tsvector, normalizing words according to the\nspecified or default configuration. The results are then concatenated in\ndocument order to produce the output. Position information is generated as\nthough one stopword exists between each pair of selected items. (Beware\nthat "document order" of the fields of a JSON object is\nimplementation-dependent when the input is jsonb.) The filter must be a\njsonb array containing zero or more of these keywords: "string" (to include\nall string values), "numeric" (to include all numeric values), "boolean"\n(to include all boolean values), "key" (to include all keys), or "all" (to\ninclude all the above). As a special case, the filter can also be a simple\nJSON value that is one of these keywords.
+[JSONB_TYPEOF]
+declaration=jsonb
+category=JSON Functions
+description=Returns the type of the top-level JSON value as a text string. Possible\ntypes are object, array, string, number, boolean, and null. (The null\nresult should not be confused with an SQL NULL; see the examples.)
+[JSON_ARRAYAGG]
+declaration=[ value_expression ] [ ORDER BY sort_expression ] [ { NULL | ABSENT } ON NULL ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ]
+category=Aggregate Functions
+description=Behaves in the same way as json_array but as an aggregate function so it\nonly takes one value_expression parameter. If ABSENT ON NULL is specified,\nany NULL values are omitted. If ORDER BY is specified, the elements will\nappear in the array in that order rather than in the input order.
+[JSON_OBJECT]
+declaration=[ { key_expression { VALUE | ':' } value_expression [ FORMAT JSON [ ENCODING UTF8 ] ] }[, ...] ] [ { NULL | ABSENT } ON NULL ] [ { WITH | WITHOUT } UNIQUE [ KEYS ] ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ]
+category=JSON Functions
+description=Constructs a JSON object of all the key/value pairs given, or an empty\nobject if none are given. key_expression is a scalar expression defining\nthe JSON key, which is converted to the text type. It cannot be NULL nor\ncan it belong to a type that has a cast to the json type. If WITH UNIQUE\nKEYS is specified, there must not be any duplicate key_expression. Any pair\nfor which the value_expression evaluates to NULL is omitted from the output\nif ABSENT ON NULL is specified; if NULL ON NULL is specified or the clause\nomitted, the key is included with value NULL.
+[JSON_OBJECTAGG]
+declaration=[ { key_expression { VALUE | ':' } value_expression } ] [ { NULL | ABSENT } ON NULL ] [ { WITH | WITHOUT } UNIQUE [ KEYS ] ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ]
+category=Aggregate Functions
+description=Behaves like json_object, but as an aggregate function, so it only takes\none key_expression and one value_expression parameter.
+[JSON_SCALAR]
+declaration=expression
+category=JSON Functions
+description=Converts a given SQL scalar value into a JSON scalar value. If the input is\nNULL, an SQL null is returned. If the input is number or a boolean value, a\ncorresponding JSON number or boolean value is returned. For any other\nvalue, a JSON string is returned.
+[JUSTIFY_DAYS]
+declaration=interval
+category=Date/Time Functions
+description=Adjust interval, converting 30-day time periods to months
+[JUSTIFY_HOURS]
+declaration=interval
+category=Date/Time Functions
+description=Adjust interval, converting 24-hour time periods to days
+[JUSTIFY_INTERVAL]
+declaration=interval
+category=Date/Time Functions
+description=Adjust interval using justify_days and justify_hours, with additional sign\nadjustments
+[LAG]
+declaration=value anycompatible [, offset integer [, default anycompatible ]]
+category=Window Functions
+description=Returns value evaluated at the row that is offset rows before the current\nrow within the partition; if there is no such row, instead returns default\n(which must be of a type compatible with value). Both offset and default\nare evaluated with respect to the current row. If omitted, offset defaults\nto 1 and default to NULL.
+[LASTVAL]
+declaration=
+category=Sequence Manipulation Functions
+description=Returns the value most recently returned by nextval in the current session.\nThis function is identical to currval, except that instead of taking the\nsequence name as an argument it refers to whichever sequence nextval was\nmost recently applied to in the current session. It is an error to call\nlastval if nextval has not yet been called in the current session.
+[LAST_VALUE]
+declaration=value anyelement
+category=Window Functions
+description=Returns value evaluated at the row that is the last row of the window\nframe.
+[LCM]
+declaration=numeric_type, numeric_type
+category=Numeric/Math Functions
+description=Least common multiple (the smallest strictly positive number that is an\nintegral multiple of both inputs); returns 0 if either input is zero;\navailable for integer, bigint, and numeric
+[LEAD]
+declaration=value anycompatible [, offset integer [, default anycompatible ]]
+category=Window Functions
+description=Returns value evaluated at the row that is offset rows after the current\nrow within the partition; if there is no such row, instead returns default\n(which must be of a type compatible with value). Both offset and default\nare evaluated with respect to the current row. If omitted, offset defaults\nto 1 and default to NULL.
+[LEFT]
+declaration=string text, n integer
+category=String Functions
+description=Returns first n characters in the string, or when n is negative, returns\nall but last |n| characters.
+[LENGTH1]
+name=LENGTH
+declaration=text
+category=String Functions
+description=Returns the number of characters in the string.
+[LENGTH2]
+name=LENGTH
+declaration=geometric_type
+category=Geometric Functions
+description=Computes the total length. Available for lseg, path.
+[LENGTH3]
+name=LENGTH
+declaration=tsvector
+category=Text Search Functions
+description=Returns the number of lexemes in the tsvector.
+[LINE]
+declaration=point, point
+category=Geometric Functions
+description=Converts two points to the line through them.
+[LOWER1]
+name=LOWER
+declaration=text
+category=String Functions
+description=Converts the string to all lower case, according to the rules of the\ndatabase's locale.
+[LOWER2]
+name=LOWER
+declaration=anyrange
+category=Range Functions
+description=Extracts the lower bound of the range (NULL if the range is empty or has no\nlower bound).
+[LOWER3]
+name=LOWER
+declaration=anymultirange
+category=Range Functions
+description=Extracts the lower bound of the multirange (NULL if the multirange is empty\nhas no lower bound).
+[LOWER_INC1]
+name=LOWER_INC
+declaration=anyrange
+category=Range Functions
+description=Is the range's lower bound inclusive?
+[LOWER_INC2]
+name=LOWER_INC
+declaration=anymultirange
+category=Range Functions
+description=Is the multirange's lower bound inclusive?
+[LOWER_INF1]
+name=LOWER_INF
+declaration=anyrange
+category=Range Functions
+description=Does the range have no lower bound? (A lower bound of -Infinity returns\nfalse.)
+[LOWER_INF2]
+name=LOWER_INF
+declaration=anymultirange
+category=Range Functions
+description=Does the multirange have no lower bound? (A lower bound of -Infinity\nreturns false.)
+[LPAD]
+declaration=string text, length integer [, fill text ]
+category=String Functions
+description=Extends the string to length length by prepending the characters fill (a\nspace by default). If the string is already longer than length then it is\ntruncated (on the right).
+[LSEG]
+declaration=box
+category=Geometric Functions
+description=Extracts box's diagonal as a line segment.
+[LTRIM1]
+name=LTRIM
+declaration=string text [, characters text ]
+category=String Functions
+description=Removes the longest string containing only characters in characters (a\nspace by default) from the start of string.
+[LTRIM2]
+name=LTRIM
+declaration=bytes bytea, bytesremoved bytea
+category=Binary String Functions
+description=Removes the longest string containing only bytes appearing in bytesremoved\nfrom the start of bytes.
+[MACADDR8_SET7BIT]
+declaration=macaddr8
+category=Network Address Functions
+description=Sets the 7th bit of the address to one, creating what is known as modified\nEUI-64, for inclusion in an IPv6 address.
+[MAKEACLITEM]
+declaration=grantee oid, grantor oid, privileges text, is_grantable boolean
+category=Session Information Functions
+description=Constructs an aclitem with the given properties. privileges is a\ncomma-separated list of privilege names such as SELECT, INSERT, etc, all of\nwhich are set in the result. (Case of the privilege string is not\nsignificant, and extra whitespace is allowed between but not within\nprivilege names.)
+[MAKE_DATE]
+declaration=year int, month int, day int
+category=Date/Time Functions
+description=Create date from year, month and day fields (negative years signify BC)
+[MAKE_INTERVAL]
+declaration=[ years int [, months int [, weeks int [, days int [, hours int [, mins int [, secs double precision ]]]]]]]
+category=Date/Time Functions
+description=Create interval from years, months, weeks, days, hours, minutes and seconds\nfields, each of which can default to zero
+[MAKE_TIME]
+declaration=hour int, min int, sec double precision
+category=Date/Time Functions
+description=Create time from hour, minute and seconds fields
+[MAKE_TIMESTAMP]
+declaration=year int, month int, day int, hour int, min int, sec double precision
+category=Date/Time Functions
+description=Create timestamp from year, month, day, hour, minute and seconds fields\n(negative years signify BC)
+[MAKE_TIMESTAMPTZ]
+declaration=year int, month int, day int, hour int, min int, sec double precision [, timezone text ]
+category=Date/Time Functions
+description=Create timestamp with time zone from year, month, day, hour, minute and\nseconds fields (negative years signify BC). If timezone is not specified,\nthe current time zone is used; the examples assume the session time zone is\nEurope/London
+[MASKLEN]
+declaration=inet
+category=Network Address Functions
+description=Returns the netmask length in bits.
+[MAX]
+declaration=see text
+category=Aggregate Functions
+description=Computes the maximum of the non-null input values. Available for any\nnumeric, string, date/time, or enum type, as well as inet, interval, money,\noid, pg_lsn, tid, xid8, and arrays of any of these types.
+[MD51]
+name=MD5
+declaration=text
+category=String Functions
+description=Computes the MD5 hash of the argument, with the result written in\nhexadecimal.
+[MD52]
+name=MD5
+declaration=bytea
+category=Binary String Functions
+description=Computes the MD5 hash of the binary string, with the result written in\nhexadecimal.
+[MERGE_ACTION]
+declaration=
+category=Merge Support Functions
+description=Returns the merge action command executed for the current row. This will be\n'INSERT', 'UPDATE', or 'DELETE'.
+[MIN]
+declaration=see text
+category=Aggregate Functions
+description=Computes the minimum of the non-null input values. Available for any\nnumeric, string, date/time, or enum type, as well as inet, interval, money,\noid, pg_lsn, tid, xid8, and arrays of any of these types.
+[MIN_SCALE]
+declaration=numeric
+category=Numeric/Math Functions
+description=Minimum scale (number of fractional decimal digits) needed to represent the\nsupplied value precisely
+[MOD]
+declaration=y numeric_type, x numeric_type
+category=Numeric/Math Functions
+description=Remainder of y/x; available for smallint, integer, bigint, and numeric
+[MODE]
+declaration=
+category=Aggregate Functions
+description=Computes the mode, the most frequent value of the aggregated argument\n(arbitrarily choosing the first one if there are multiple equally-frequent\nvalues). The aggregated argument must be of a sortable type.
+[MULTIRANGE]
+declaration=anyrange
+category=Range Functions
+description=Returns a multirange containing just the given range.
+[MXID_AGE]
+declaration=xid
+category=Session Information Functions
+description=Returns the number of multixacts IDs between the supplied multixact ID and\nthe current multixacts counter.
+[NETMASK]
+declaration=inet
+category=Network Address Functions
+description=Computes the network mask for the address's network.
+[NETWORK]
+declaration=inet
+category=Network Address Functions
+description=Returns the network part of the address, zeroing out whatever is to the\nright of the netmask. (This is equivalent to casting the value to cidr.)
+[NEXTVAL]
+declaration=regclass
+category=Sequence Manipulation Functions
+description=Advances the sequence object to its next value and returns that value. This\nis done atomically: even if multiple sessions execute nextval concurrently,\neach will safely receive a distinct sequence value. If the sequence object\nhas been created with default parameters, successive nextval calls will\nreturn successive values beginning with 1. Other behaviors can be obtained\nby using appropriate parameters in the CREATE SEQUENCE command.
+[NOW]
+declaration=
+category=Date/Time Functions
+description=Current date and time (start of current transaction); see Section 9.9.5
+[NPOINTS]
+declaration=geometric_type
+category=Geometric Functions
+description=Returns the number of points. Available for path, polygon.
+[NTH_VALUE]
+declaration=value anyelement, n integer
+category=Window Functions
+description=Returns value evaluated at the row that is the n'th row of the window frame\n(counting from 1); returns NULL if there is no such row.
+[NTILE]
+declaration=num_buckets integer
+category=Window Functions
+description=Returns an integer ranging from 1 to the argument value, dividing the\npartition as equally as possible.
+[NUMNODE]
+declaration=tsquery
+category=Text Search Functions
+description=Returns the number of lexemes plus operators in the tsquery.
+[OBJ_DESCRIPTION]
+declaration=object oid, catalog name
+category=Session Information Functions
+description=Returns the comment for a database object specified by its OID and the name\nof the containing system catalog. For example, obj_description(123456,\n'pg_class') would retrieve the comment for the table with OID 123456.
+[OCTET_LENGTH1]
+name=OCTET_LENGTH
+declaration=text
+category=String Functions
+description=Returns number of bytes in the string.
+[OCTET_LENGTH2]
+name=OCTET_LENGTH
+declaration=character
+category=String Functions
+description=Returns number of bytes in the string. Since this version of the function\naccepts type character directly, it will not strip trailing spaces.
+[OCTET_LENGTH3]
+name=OCTET_LENGTH
+declaration=bytea
+category=Binary String Functions
+description=Returns number of bytes in the binary string.
+[OCTET_LENGTH4]
+name=OCTET_LENGTH
+declaration=bit
+category=Bit String Functions
+description=Returns number of bytes in the bit string.
+[OVERLAY1]
+name=OVERLAY
+declaration=string text PLACING newsubstring text FROM start integer [ FOR count integer ]
+category=String Functions
+description=Replaces the substring of string that starts at the start'th character and\nextends for count characters with newsubstring. If count is omitted, it\ndefaults to the length of newsubstring.
+[OVERLAY2]
+name=OVERLAY
+declaration=bytes bytea PLACING newsubstring bytea FROM start integer [ FOR count integer ]
+category=Binary String Functions
+description=Replaces the substring of bytes that starts at the start'th byte and\nextends for count bytes with newsubstring. If count is omitted, it defaults\nto the length of newsubstring.
+[OVERLAY3]
+name=OVERLAY
+declaration=bits bit PLACING newsubstring bit FROM start integer [ FOR count integer ]
+category=Bit String Functions
+description=Replaces the substring of bits that starts at the start'th bit and extends\nfor count bits with newsubstring. If count is omitted, it defaults to the\nlength of newsubstring.
+[PARSE_IDENT]
+declaration=qualified_identifier text [, strict_mode boolean DEFAULT true ]
+category=String Functions
+description=Splits qualified_identifier into an array of identifiers, removing any\nquoting of individual identifiers. By default, extra characters after the\nlast identifier are considered an error; but if the second parameter is\nfalse, then such extra characters are ignored. (This behavior is useful for\nparsing names for objects like functions.) Note that this function does not\ntruncate over-length identifiers. If you want truncation you can cast the\nresult to name[].
+[PATH]
+declaration=polygon
+category=Geometric Functions
+description=Converts polygon to a closed path with the same list of points.
+[PCLOSE]
+declaration=path
+category=Geometric Functions
+description=Converts path to closed form.
+[PERCENTILE_DISC]
+declaration=fraction double precision
+category=Aggregate Functions
+description=Computes the discrete percentile, the first value within the ordered set of\naggregated argument values whose position in the ordering equals or exceeds\nthe specified fraction. The aggregated argument must be of a sortable type.
+[PERCENT_RANK1]
+name=PERCENT_RANK
+declaration=args
+category=Aggregate Functions
+description=Computes the relative rank of the hypothetical row, that is (rank - 1) /\n(total rows - 1). The value thus ranges from 0 to 1 inclusive.
+[PERCENT_RANK2]
+name=PERCENT_RANK
+declaration=
+category=Window Functions
+description=Returns the relative rank of the current row, that is (rank - 1) / (total\npartition rows - 1). The value thus ranges from 0 to 1 inclusive.
+[PG_ADVISORY_UNLOCK_ALL]
+declaration=
+category=System Administration Functions
+description=Releases all session-level advisory locks held by the current session.\n(This function is implicitly invoked at session end, even if the client\ndisconnects ungracefully.)
+[PG_AVAILABLE_WAL_SUMMARIES]
+declaration=
+category=Session Information Functions
+description=Returns information about the WAL summary files present in the data\ndirectory, under pg_wal/summaries. One row will be returned per WAL summary\nfile. Each file summarizes WAL on the indicated TLI within the indicated\nLSN range. This function might be useful to determine whether enough WAL\nsummaries are present on the server to take an incremental backup based on\nsome prior backup whose start LSN is known.
+[PG_BACKEND_PID]
+declaration=
+category=Session Information Functions
+description=Returns the process ID of the server process attached to the current\nsession.
+[PG_BACKUP_START]
+declaration=label text [, fast boolean ]
+category=System Administration Functions
+description=Prepares the server to begin an on-line backup. The only required parameter\nis an arbitrary user-defined label for the backup. (Typically this would be\nthe name under which the backup dump file will be stored.) If the optional\nsecond parameter is given as true, it specifies executing pg_backup_start\nas quickly as possible. This forces an immediate checkpoint which will\ncause a spike in I/O operations, slowing any concurrently executing\nqueries.
+[PG_BACKUP_STOP]
+declaration=[wait_for_archive boolean ]
+category=System Administration Functions
+description=Finishes performing an on-line backup. The desired contents of the backup\nlabel file and the tablespace map file are returned as part of the result\nof the function and must be written to files in the backup area. These\nfiles must not be written to the live data directory (doing so will cause\nPostgreSQL to fail to restart in the event of a crash).
+[PG_BASETYPE]
+declaration=regtype
+category=Session Information Functions
+description=Returns the OID of the base type of a domain identified by its type OID. If\nthe argument is the OID of a non-domain type, returns the argument as-is.\nReturns NULL if the argument is not a valid type OID. If there's a chain of\ndomain dependencies, it will recurse until finding the base type.
+[PG_BLOCKING_PIDS]
+declaration=integer
+category=Session Information Functions
+description=Returns an array of the process ID(s) of the sessions that are blocking the\nserver process with the specified process ID from acquiring a lock, or an\nempty array if there is no such server process or it is not blocked.
+[PG_CANCEL_BACKEND]
+declaration=pid integer
+category=System Administration Functions
+description=Cancels the current query of the session whose backend process has the\nspecified process ID. This is also allowed if the calling role is a member\nof the role whose backend is being canceled or the calling role has\nprivileges of pg_signal_backend, however only superusers can cancel\nsuperuser backends.
+[PG_CHAR_TO_ENCODING]
+declaration=encoding name
+category=Session Information Functions
+description=Converts the supplied encoding name into an integer representing the\ninternal identifier used in some system catalog tables. Returns -1 if an\nunknown encoding name is provided.
+[PG_CLIENT_ENCODING]
+declaration=
+category=String Functions
+description=Returns current client encoding name.
+[PG_COLLATION_ACTUAL_VERSION]
+declaration=oid
+category=System Administration Functions
+description=Returns the actual version of the collation object as it is currently\ninstalled in the operating system. If this is different from the value in\npg_collation.collversion, then objects depending on the collation might\nneed to be rebuilt. See also ALTER COLLATION.
+[PG_COLLATION_IS_VISIBLE]
+declaration=collation oid
+category=Session Information Functions
+description=Is collation visible in search path?
+[PG_COLUMN_COMPRESSION]
+declaration="any"
+category=System Administration Functions
+description=Shows the compression algorithm that was used to compress an individual\nvariable-length value. Returns NULL if the value is not compressed.
+[PG_COLUMN_SIZE]
+declaration="any"
+category=System Administration Functions
+description=Shows the number of bytes used to store any individual data value. If\napplied directly to a table column value, this reflects any compression\nthat was done.
+[PG_COLUMN_TOAST_CHUNK_ID]
+declaration="any"
+category=System Administration Functions
+description=Shows the chunk_id of an on-disk TOASTed value. Returns NULL if the value\nis un-TOASTed or not on-disk. See Section 65.2 for more information about\nTOAST.
+[PG_CONF_LOAD_TIME]
+declaration=
+category=Session Information Functions
+description=Returns the time when the server configuration files were last loaded. If\nthe current session was alive at the time, this will be the time when the\nsession itself re-read the configuration files (so the reading will vary a\nlittle in different sessions). Otherwise it is the time when the postmaster\nprocess re-read the configuration files.
+[PG_CONTROL_CHECKPOINT]
+declaration=
+category=Session Information Functions
+description=Returns information about current checkpoint state, as shown in Table 9.87.
+[PG_CONTROL_INIT]
+declaration=
+category=Session Information Functions
+description=Returns information about cluster initialization state, as shown in Table\n9.89.
+[PG_CONTROL_RECOVERY]
+declaration=
+category=Session Information Functions
+description=Returns information about recovery state, as shown in Table 9.90.
+[PG_CONTROL_SYSTEM]
+declaration=
+category=Session Information Functions
+description=Returns information about current control file state, as shown in Table\n9.88.
+[PG_CONVERSION_IS_VISIBLE]
+declaration=conversion oid
+category=Session Information Functions
+description=Is conversion visible in search path?
+[PG_COPY_LOGICAL_REPLICATION_SLOT]
+declaration=src_slot_name name, dst_slot_name name [, temporary boolean [, plugin name ]]
+category=System Administration Functions
+description=Copies an existing logical replication slot named src_slot_name to a\nlogical replication slot named dst_slot_name, optionally changing the\noutput plugin and persistence. The copied logical slot starts from the same\nLSN as the source logical slot. Both temporary and plugin are optional; if\nthey are omitted, the values of the source slot are used.
+[PG_COPY_PHYSICAL_REPLICATION_SLOT]
+declaration=src_slot_name name, dst_slot_name name [, temporary boolean ]
+category=System Administration Functions
+description=Copies an existing physical replication slot named src_slot_name to a\nphysical replication slot named dst_slot_name. The copied physical slot\nstarts to reserve WAL from the same LSN as the source slot. temporary is\noptional. If temporary is omitted, the same value as the source slot is\nused.
+[PG_CREATE_LOGICAL_REPLICATION_SLOT]
+declaration=slot_name name, plugin name [, temporary boolean, twophase boolean, failover boolean ]
+category=System Administration Functions
+description=Creates a new logical (decoding) replication slot named slot_name using the\noutput plugin plugin. The optional third parameter, temporary, when set to\ntrue, specifies that the slot should not be permanently stored to disk and\nis only meant for use by the current session. Temporary slots are also\nreleased upon any error. The optional fourth parameter, twophase, when set\nto true, specifies that the decoding of prepared transactions is enabled\nfor this slot. The optional fifth parameter, failover, when set to true,\nspecifies that this slot is enabled to be synced to the standbys so that\nlogical replication can be resumed after failover. A call to this function\nhas the same effect as the replication protocol command\nCREATE_REPLICATION_SLOT ... LOGICAL.
+[PG_CREATE_PHYSICAL_REPLICATION_SLOT]
+declaration=slot_name name [, immediately_reserve boolean, temporary boolean ]
+category=System Administration Functions
+description=Creates a new physical replication slot named slot_name. The optional\nsecond parameter, when true, specifies that the LSN for this replication\nslot be reserved immediately; otherwise the LSN is reserved on first\nconnection from a streaming replication client. Streaming changes from a\nphysical slot is only possible with the streaming-replication protocol -\nsee Section 53.4. The optional third parameter, temporary, when set to\ntrue, specifies that the slot should not be permanently stored to disk and\nis only meant for use by the current session. Temporary slots are also\nreleased upon any error. This function corresponds to the replication\nprotocol command CREATE_REPLICATION_SLOT ... PHYSICAL.
+[PG_CREATE_RESTORE_POINT]
+declaration=name text
+category=System Administration Functions
+description=Creates a named marker record in the write-ahead log that can later be used\nas a recovery target, and returns the corresponding write-ahead log\nlocation. The given name can then be used with recovery_target_name to\nspecify the point up to which recovery will proceed. Avoid creating\nmultiple restore points with the same name, since recovery will stop at the\nfirst one whose name matches the recovery target.
+[PG_CURRENT_SNAPSHOT]
+declaration=
+category=Session Information Functions
+description=Returns a current snapshot, a data structure showing which transaction IDs\nare now in-progress. Only top-level transaction IDs are included in the\nsnapshot; subtransaction IDs are not shown; see Section 66.3 for details.
+[PG_CURRENT_WAL_FLUSH_LSN]
+declaration=
+category=System Administration Functions
+description=Returns the current write-ahead log flush location (see notes below).
+[PG_CURRENT_WAL_INSERT_LSN]
+declaration=
+category=System Administration Functions
+description=Returns the current write-ahead log insert location (see notes below).
+[PG_CURRENT_WAL_LSN]
+declaration=
+category=System Administration Functions
+description=Returns the current write-ahead log write location (see notes below).
+[PG_CURRENT_XACT_ID]
+declaration=
+category=Session Information Functions
+description=Returns the current transaction's ID. It will assign a new one if the\ncurrent transaction does not have one already (because it has not performed\nany database updates); see Section 66.1 for details. If executed in a\nsubtransaction, this will return the top-level transaction ID; see Section\n66.3 for details.
+[PG_CURRENT_XACT_ID_IF_ASSIGNED]
+declaration=
+category=Session Information Functions
+description=Returns the current transaction's ID, or NULL if no ID is assigned yet.\n(It's best to use this variant if the transaction might otherwise be\nread-only, to avoid unnecessary consumption of an XID.) If executed in a\nsubtransaction, this will return the top-level transaction ID.
+[PG_DATABASE_COLLATION_ACTUAL_VERSION]
+declaration=oid
+category=System Administration Functions
+description=Returns the actual version of the database's collation as it is currently\ninstalled in the operating system. If this is different from the value in\npg_database.datcollversion, then objects depending on the collation might\nneed to be rebuilt. See also ALTER DATABASE.
+[PG_DESCRIBE_OBJECT]
+declaration=classid oid, objid oid, objsubid integer
+category=Session Information Functions
+description=Returns a textual description of a database object identified by catalog\nOID, object OID, and sub-object ID (such as a column number within a table;\nthe sub-object ID is zero when referring to a whole object). This\ndescription is intended to be human-readable, and might be translated,\ndepending on server configuration. This is especially useful to determine\nthe identity of an object referenced in the pg_depend catalog. This\nfunction returns NULL values for undefined objects.
+[PG_DROP_REPLICATION_SLOT]
+declaration=slot_name name
+category=System Administration Functions
+description=Drops the physical or logical replication slot named slot_name. Same as\nreplication protocol command DROP_REPLICATION_SLOT. For logical slots, this\nmust be called while connected to the same database the slot was created\non.
+[PG_ENCODING_TO_CHAR]
+declaration=encoding integer
+category=Session Information Functions
+description=Converts the integer used as the internal identifier of an encoding in some\nsystem catalog tables into a human-readable string. Returns an empty string\nif an invalid encoding number is provided.
+[PG_EXPORT_SNAPSHOT]
+declaration=
+category=System Administration Functions
+description=Saves the transaction's current snapshot and returns a text string\nidentifying the snapshot. This string must be passed (outside the database)\nto clients that want to import the snapshot. The snapshot is available for\nimport only until the end of the transaction that exported it.
+[PG_FILENODE_RELATION]
+declaration=tablespace oid, filenode oid
+category=System Administration Functions
+description=Returns a relation's OID given the tablespace OID and filenode it is stored\nunder. This is essentially the inverse mapping of pg_relation_filepath. For\na relation in the database's default tablespace, the tablespace can be\nspecified as zero. Returns NULL if no relation in the current database is\nassociated with the given values.
+[PG_FUNCTION_IS_VISIBLE]
+declaration=function oid
+category=Session Information Functions
+description=Is function visible in search path? (This also works for procedures and\naggregates.)
+[PG_GET_CATALOG_FOREIGN_KEYS]
+declaration=
+category=Session Information Functions
+description=Returns a set of records describing the foreign key relationships that\nexist within the PostgreSQL system catalogs. The fktable column contains\nthe name of the referencing catalog, and the fkcols column contains the\nname(s) of the referencing column(s). Similarly, the pktable column\ncontains the name of the referenced catalog, and the pkcols column contains\nthe name(s) of the referenced column(s). If is_array is true, the last\nreferencing column is an array, each of whose elements should match some\nentry in the referenced catalog. If is_opt is true, the referencing\ncolumn(s) are allowed to contain zeroes instead of a valid reference.
+[PG_GET_CONSTRAINTDEF]
+declaration=constraint oid [, pretty boolean ]
+category=Session Information Functions
+description=Reconstructs the creating command for a constraint. (This is a decompiled\nreconstruction, not the original text of the command.)
+[PG_GET_EXPR]
+declaration=expr pg_node_tree, relation oid [, pretty boolean ]
+category=Session Information Functions
+description=Decompiles the internal form of an expression stored in the system\ncatalogs, such as the default value for a column. If the expression might\ncontain Vars, specify the OID of the relation they refer to as the second\nparameter; if no Vars are expected, passing zero is sufficient.
+[PG_GET_FUNCTIONDEF]
+declaration=func oid
+category=Session Information Functions
+description=Reconstructs the creating command for a function or procedure. (This is a\ndecompiled reconstruction, not the original text of the command.) The\nresult is a complete CREATE OR REPLACE FUNCTION or CREATE OR REPLACE\nPROCEDURE statement.
+[PG_GET_FUNCTION_ARGUMENTS]
+declaration=func oid
+category=Session Information Functions
+description=Reconstructs the argument list of a function or procedure, in the form it\nwould need to appear in within CREATE FUNCTION (including default values).
+[PG_GET_FUNCTION_IDENTITY_ARGUMENTS]
+declaration=func oid
+category=Session Information Functions
+description=Reconstructs the argument list necessary to identify a function or\nprocedure, in the form it would need to appear in within commands such as\nALTER FUNCTION. This form omits default values.
+[PG_GET_FUNCTION_RESULT]
+declaration=func oid
+category=Session Information Functions
+description=Reconstructs the RETURNS clause of a function, in the form it would need to\nappear in within CREATE FUNCTION. Returns NULL for a procedure.
+[PG_GET_INDEXDEF]
+declaration=index oid [, column integer, pretty boolean ]
+category=Session Information Functions
+description=Reconstructs the creating command for an index. (This is a decompiled\nreconstruction, not the original text of the command.) If column is\nsupplied and is not zero, only the definition of that column is\nreconstructed.
+[PG_GET_KEYWORDS]
+declaration=
+category=Session Information Functions
+description=Returns a set of records describing the SQL keywords recognized by the\nserver. The word column contains the keyword. The catcode column contains a\ncategory code: U for an unreserved keyword, C for a keyword that can be a\ncolumn name, T for a keyword that can be a type or function name, or R for\na fully reserved keyword. The barelabel column contains true if the keyword\ncan be used as a "bare" column label in SELECT lists, or false if it can\nonly be used after AS. The catdesc column contains a possibly-localized\nstring describing the keyword's category. The baredesc column contains a\npossibly-localized string describing the keyword's column label status.
+[PG_GET_OBJECT_ADDRESS]
+declaration=type text, object_names text[], object_args text[]
+category=Session Information Functions
+description=Returns a row containing enough information to uniquely identify the\ndatabase object specified by a type code and object name and argument\narrays. The returned values are the ones that would be used in system\ncatalogs such as pg_depend; they can be passed to other system functions\nsuch as pg_describe_object or pg_identify_object. classid is the OID of the\nsystem catalog containing the object; objid is the OID of the object\nitself, and objsubid is the sub-object ID, or zero if none. This function\nis the inverse of pg_identify_object_as_address. Undefined objects are\nidentified with NULL values.
+[PG_GET_PARTKEYDEF]
+declaration=table oid
+category=Session Information Functions
+description=Reconstructs the definition of a partitioned table's partition key, in the\nform it would have in the PARTITION BY clause of CREATE TABLE. (This is a\ndecompiled reconstruction, not the original text of the command.)
+[PG_GET_RULEDEF]
+declaration=rule oid [, pretty boolean ]
+category=Session Information Functions
+description=Reconstructs the creating command for a rule. (This is a decompiled\nreconstruction, not the original text of the command.)
+[PG_GET_SERIAL_SEQUENCE]
+declaration=table text, column text
+category=Session Information Functions
+description=Returns the name of the sequence associated with a column, or NULL if no\nsequence is associated with the column. If the column is an identity\ncolumn, the associated sequence is the sequence internally created for that\ncolumn. For columns created using one of the serial types (serial,\nsmallserial, bigserial), it is the sequence created for that serial column\ndefinition. In the latter case, the association can be modified or removed\nwith ALTER SEQUENCE OWNED BY. (This function probably should have been\ncalled pg_get_owned_sequence; its current name reflects the fact that it\nhas historically been used with serial-type columns.) The first parameter\nis a table name with optional schema, and the second parameter is a column\nname. Because the first parameter potentially contains both schema and\ntable names, it is parsed per usual SQL rules, meaning it is lower-cased by\ndefault. The second parameter, being just a column name, is treated\nliterally and so has its case preserved. The result is suitably formatted\nfor passing to the sequence functions (see Section 9.17).
+[PG_GET_STATISTICSOBJDEF]
+declaration=statobj oid
+category=Session Information Functions
+description=Reconstructs the creating command for an extended statistics object. (This\nis a decompiled reconstruction, not the original text of the command.)
+[PG_GET_TRIGGERDEF]
+declaration=trigger oid [, pretty boolean ]
+category=Session Information Functions
+description=Reconstructs the creating command for a trigger. (This is a decompiled\nreconstruction, not the original text of the command.)
+[PG_GET_USERBYID]
+declaration=role oid
+category=Session Information Functions
+description=Returns a role's name given its OID.
+[PG_GET_VIEWDEF]
+declaration=view oid [, pretty boolean ]
+category=Session Information Functions
+description=Reconstructs the underlying SELECT command for a view or materialized view.\n(This is a decompiled reconstruction, not the original text of the\ncommand.)
+[PG_GET_WAL_REPLAY_PAUSE_STATE]
+declaration=
+category=System Administration Functions
+description=Returns recovery pause state. The return values are not paused if pause is\nnot requested, pause requested if pause is requested but recovery is not\nyet paused, and paused if the recovery is actually paused.
+[PG_GET_WAL_RESOURCE_MANAGERS]
+declaration=
+category=System Administration Functions
+description=Returns the currently-loaded WAL resource managers in the system. The\ncolumn rm_builtin indicates whether it's a built-in resource manager, or a\ncustom resource manager loaded by an extension.
+[PG_GET_WAL_SUMMARIZER_STATE]
+declaration=
+category=Session Information Functions
+description=Returns information about the progress of the WAL summarizer. If the WAL\nsummarizer has never run since the instance was started, then\nsummarized_tli and summarized_lsn will be 0 and 0/0 respectively;\notherwise, they will be the TLI and ending LSN of the last WAL summary file\nwritten to disk. If the WAL summarizer is currently running, pending_lsn\nwill be the ending LSN of the last record that it has consumed, which must\nalways be greater than or equal to summarized_lsn; if the WAL summarizer is\nnot running, it will be equal to summarized_lsn. summarizer_pid is the PID\nof the WAL summarizer process, if it is running, and otherwise NULL.
+[PG_HAS_ROLE]
+declaration=[ user name or oid, ] role text or oid, privilege text
+category=Session Information Functions
+description=Does user have privilege for role? Allowable privilege types are MEMBER,\nUSAGE, and SET. MEMBER denotes direct or indirect membership in the role\nwithout regard to what specific privileges may be conferred. USAGE denotes\nwhether the privileges of the role are immediately available without doing\nSET ROLE, while SET denotes whether it is possible to change to the role\nusing the SET ROLE command. WITH ADMIN OPTION or WITH GRANT OPTION can be\nadded to any of these privilege types to test whether the ADMIN privilege\nis held (all six spellings test the same thing). This function does not\nallow the special case of setting user to public, because the PUBLIC\npseudo-role can never be a member of real roles.
+[PG_IDENTIFY_OBJECT]
+declaration=classid oid, objid oid, objsubid integer
+category=Session Information Functions
+description=Returns a row containing enough information to uniquely identify the\ndatabase object specified by catalog OID, object OID and sub-object ID.\nThis information is intended to be machine-readable, and is never\ntranslated. type identifies the type of database object; schema is the\nschema name that the object belongs in, or NULL for object types that do\nnot belong to schemas; name is the name of the object, quoted if necessary,\nif the name (along with schema name, if pertinent) is sufficient to\nuniquely identify the object, otherwise NULL; identity is the complete\nobject identity, with the precise format depending on object type, and each\nname within the format being schema-qualified and quoted as necessary.\nUndefined objects are identified with NULL values.
+[PG_IDENTIFY_OBJECT_AS_ADDRESS]
+declaration=classid oid, objid oid, objsubid integer
+category=Session Information Functions
+description=Returns a row containing enough information to uniquely identify the\ndatabase object specified by catalog OID, object OID and sub-object ID. The\nreturned information is independent of the current server, that is, it\ncould be used to identify an identically named object in another server.\ntype identifies the type of database object; object_names and object_args\nare text arrays that together form a reference to the object. These three\nvalues can be passed to pg_get_object_address to obtain the internal\naddress of the object.
+[PG_IMPORT_SYSTEM_COLLATIONS]
+declaration=schema regnamespace
+category=System Administration Functions
+description=Adds collations to the system catalog pg_collation based on all the locales\nit finds in the operating system. This is what initdb uses; see Section\n23.2.2 for more details. If additional locales are installed into the\noperating system later on, this function can be run again to add collations\nfor the new locales. Locales that match existing entries in pg_collation\nwill be skipped. (But collation objects based on locales that are no longer\npresent in the operating system are not removed by this function.) The\nschema parameter would typically be pg_catalog, but that is not a\nrequirement; the collations could be installed into some other schema as\nwell. The function returns the number of new collation objects it created.\nUse of this function is restricted to superusers.
+[PG_INDEXAM_HAS_PROPERTY]
+declaration=am oid, property text
+category=Session Information Functions
+description=Tests whether an index access method has the named property. Access method\nproperties are listed in Table 9.77. NULL is returned if the property name\nis not known or does not apply to the particular object, or if the OID does\nnot identify a valid object.
+[PG_INDEXES_SIZE]
+declaration=regclass
+category=System Administration Functions
+description=Computes the total disk space used by indexes attached to the specified\ntable.
+[PG_INDEX_COLUMN_HAS_PROPERTY]
+declaration=index regclass, column integer, property text
+category=Session Information Functions
+description=Tests whether an index column has the named property. Common index column\nproperties are listed in Table 9.75. (Note that extension access methods\ncan define additional property names for their indexes.) NULL is returned\nif the property name is not known or does not apply to the particular\nobject, or if the OID or column number does not identify a valid object.
+[PG_INDEX_HAS_PROPERTY]
+declaration=index regclass, property text
+category=Session Information Functions
+description=Tests whether an index has the named property. Common index properties are\nlisted in Table 9.76. (Note that extension access methods can define\nadditional property names for their indexes.) NULL is returned if the\nproperty name is not known or does not apply to the particular object, or\nif the OID does not identify a valid object.
+[PG_INPUT_ERROR_INFO]
+declaration=string text, type text
+category=Session Information Functions
+description=Tests whether the given string is valid input for the specified data type;\nif not, return the details of the error that would have been thrown. If the\ninput is valid, the results are NULL. The inputs are the same as for\npg_input_is_valid.
+[PG_INPUT_IS_VALID]
+declaration=string text, type text
+category=Session Information Functions
+description=Tests whether the given string is valid input for the specified data type,\nreturning true or false.
+[PG_IS_IN_RECOVERY]
+declaration=
+category=System Administration Functions
+description=Returns true if recovery is still in progress.
+[PG_IS_OTHER_TEMP_SCHEMA]
+declaration=oid
+category=Session Information Functions
+description=Returns true if the given OID is the OID of another session's temporary\nschema. (This can be useful, for example, to exclude other sessions'\ntemporary tables from a catalog display.)
+[PG_IS_WAL_REPLAY_PAUSED]
+declaration=
+category=System Administration Functions
+description=Returns true if recovery pause is requested.
+[PG_JIT_AVAILABLE]
+declaration=
+category=Session Information Functions
+description=Returns true if a JIT compiler extension is available (see Chapter 30) and\nthe jit configuration parameter is set to on.
+[PG_LAST_COMMITTED_XACT]
+declaration=
+category=Session Information Functions
+description=Returns the transaction ID, commit timestamp and replication origin of the\nlatest committed transaction.
+[PG_LAST_WAL_RECEIVE_LSN]
+declaration=
+category=System Administration Functions
+description=Returns the last write-ahead log location that has been received and synced\nto disk by streaming replication. While streaming replication is in\nprogress this will increase monotonically. If recovery has completed then\nthis will remain static at the location of the last WAL record received and\nsynced to disk during recovery. If streaming replication is disabled, or if\nit has not yet started, the function returns NULL.
+[PG_LAST_WAL_REPLAY_LSN]
+declaration=
+category=System Administration Functions
+description=Returns the last write-ahead log location that has been replayed during\nrecovery. If recovery is still in progress this will increase\nmonotonically. If recovery has completed then this will remain static at\nthe location of the last WAL record applied during recovery. When the\nserver has been started normally without recovery, the function returns\nNULL.
+[PG_LAST_XACT_REPLAY_TIMESTAMP]
+declaration=
+category=System Administration Functions
+description=Returns the time stamp of the last transaction replayed during recovery.\nThis is the time at which the commit or abort WAL record for that\ntransaction was generated on the primary. If no transactions have been\nreplayed during recovery, the function returns NULL. Otherwise, if recovery\nis still in progress this will increase monotonically. If recovery has\ncompleted then this will remain static at the time of the last transaction\napplied during recovery. When the server has been started normally without\nrecovery, the function returns NULL.
+[PG_LISTENING_CHANNELS]
+declaration=
+category=Session Information Functions
+description=Returns the set of names of asynchronous notification channels that the\ncurrent session is listening to.
+[PG_LOGICAL_SLOT_GET_BINARY_CHANGES]
+declaration=slot_name name, upto_lsn pg_lsn, upto_nchanges integer, VARIADIC options text[]
+category=System Administration Functions
+description=Behaves just like the pg_logical_slot_get_changes() function, except that\nchanges are returned as bytea.
+[PG_LOGICAL_SLOT_GET_CHANGES]
+declaration=slot_name name, upto_lsn pg_lsn, upto_nchanges integer, VARIADIC options text[]
+category=System Administration Functions
+description=Returns changes in the slot slot_name, starting from the point from which\nchanges have been consumed last. If upto_lsn and upto_nchanges are NULL,\nlogical decoding will continue until end of WAL. If upto_lsn is non-NULL,\ndecoding will include only those transactions which commit prior to the\nspecified LSN. If upto_nchanges is non-NULL, decoding will stop when the\nnumber of rows produced by decoding exceeds the specified value. Note,\nhowever, that the actual number of rows returned may be larger, since this\nlimit is only checked after adding the rows produced when decoding each new\ntransaction commit. If the specified slot is a logical failover slot then\nthe function will not return until all physical slots specified in\nsynchronized_standby_slots have confirmed WAL receipt.
+[PG_LOGICAL_SLOT_PEEK_BINARY_CHANGES]
+declaration=slot_name name, upto_lsn pg_lsn, upto_nchanges integer, VARIADIC options text[]
+category=System Administration Functions
+description=Behaves just like the pg_logical_slot_peek_changes() function, except that\nchanges are returned as bytea.
+[PG_LOGICAL_SLOT_PEEK_CHANGES]
+declaration=slot_name name, upto_lsn pg_lsn, upto_nchanges integer, VARIADIC options text[]
+category=System Administration Functions
+description=Behaves just like the pg_logical_slot_get_changes() function, except that\nchanges are not consumed; that is, they will be returned again on future\ncalls.
+[PG_LOG_BACKEND_MEMORY_CONTEXTS]
+declaration=pid integer
+category=System Administration Functions
+description=Requests to log the memory contexts of the backend with the specified\nprocess ID. This function can send the request to backends and auxiliary\nprocesses except logger. These memory contexts will be logged at LOG\nmessage level. They will appear in the server log based on the log\nconfiguration set (see Section 19.8 for more information), but will not be\nsent to the client regardless of client_min_messages.
+[PG_LOG_STANDBY_SNAPSHOT]
+declaration=
+category=System Administration Functions
+description=Take a snapshot of running transactions and write it to WAL, without having\nto wait for bgwriter or checkpointer to log one. This is useful for logical\ndecoding on standby, as logical slot creation has to wait until such a\nrecord is replayed on the standby.
+[PG_LS_ARCHIVE_STATUSDIR]
+declaration=
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's WAL archive status directory (pg_wal/archive_status).\nFilenames beginning with a dot, directories, and other special files are\nexcluded.
+[PG_LS_DIR]
+declaration=dirname text [, missing_ok boolean, include_dot_dirs boolean ]
+category=System Administration Functions
+description=Returns the names of all files (and directories and other special files) in\nthe specified directory. The include_dot_dirs parameter indicates whether\n"." and ".." are to be included in the result set; the default is to\nexclude them. Including them can be useful when missing_ok is true, to\ndistinguish an empty directory from a non-existent directory.
+[PG_LS_LOGDIR]
+declaration=
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's log directory. Filenames beginning with a dot,\ndirectories, and other special files are excluded.
+[PG_LS_LOGICALMAPDIR]
+declaration=
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's pg_logical/mappings directory. Filenames beginning\nwith a dot, directories, and other special files are excluded.
+[PG_LS_LOGICALSNAPDIR]
+declaration=
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's pg_logical/snapshots directory. Filenames beginning\nwith a dot, directories, and other special files are excluded.
+[PG_LS_REPLSLOTDIR]
+declaration=slot_name text
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's pg_replslot/slot_name directory, where slot_name is\nthe name of the replication slot provided as input of the function.\nFilenames beginning with a dot, directories, and other special files are\nexcluded.
+[PG_LS_TMPDIR]
+declaration=[ tablespace oid ]
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the temporary file directory for the specified tablespace. If\ntablespace is not provided, the pg_default tablespace is examined.\nFilenames beginning with a dot, directories, and other special files are\nexcluded.
+[PG_LS_WALDIR]
+declaration=
+category=System Administration Functions
+description=Returns the name, size, and last modification time (mtime) of each ordinary\nfile in the server's write-ahead log (WAL) directory. Filenames beginning\nwith a dot, directories, and other special files are excluded.
+[PG_MY_TEMP_SCHEMA]
+declaration=
+category=Session Information Functions
+description=Returns the OID of the current session's temporary schema, or zero if it\nhas none (because it has not created any temporary tables).
+[PG_NOTIFICATION_QUEUE_USAGE]
+declaration=
+category=Session Information Functions
+description=Returns the fraction (0–1) of the asynchronous notification queue's\nmaximum size that is currently occupied by notifications that are waiting\nto be processed. See LISTEN and NOTIFY for more information.
+[PG_OPCLASS_IS_VISIBLE]
+declaration=opclass oid
+category=Session Information Functions
+description=Is operator class visible in search path?
+[PG_OPERATOR_IS_VISIBLE]
+declaration=operator oid
+category=Session Information Functions
+description=Is operator visible in search path?
+[PG_OPFAMILY_IS_VISIBLE]
+declaration=opclass oid
+category=Session Information Functions
+description=Is operator family visible in search path?
+[PG_OPTIONS_TO_TABLE]
+declaration=options_array text[]
+category=Session Information Functions
+description=Returns the set of storage options represented by a value from\npg_class.reloptions or pg_attribute.attoptions.
+[PG_PARTITION_ANCESTORS]
+declaration=regclass
+category=System Administration Functions
+description=Lists the ancestor relations of the given partition, including the relation\nitself. Returns no rows if the relation does not exist or is not a\npartition or partitioned table.
+[PG_PARTITION_ROOT]
+declaration=regclass
+category=System Administration Functions
+description=Returns the top-most parent of the partition tree to which the given\nrelation belongs. Returns NULL if the relation does not exist or is not a\npartition or partitioned table.
+[PG_PARTITION_TREE]
+declaration=regclass
+category=System Administration Functions
+description=Lists the tables or indexes in the partition tree of the given partitioned\ntable or partitioned index, with one row for each partition. Information\nprovided includes the OID of the partition, the OID of its immediate\nparent, a boolean value telling if the partition is a leaf, and an integer\ntelling its level in the hierarchy. The level value is 0 for the input\ntable or index, 1 for its immediate child partitions, 2 for their\npartitions, and so on. Returns no rows if the relation does not exist or is\nnot a partition or partitioned table.
+[PG_POSTMASTER_START_TIME]
+declaration=
+category=Session Information Functions
+description=Returns the time when the server started.
+[PG_PROMOTE]
+declaration=wait boolean DEFAULT true, wait_seconds integer DEFAULT 60
+category=System Administration Functions
+description=Promotes a standby server to primary status. With wait set to true (the\ndefault), the function waits until promotion is completed or wait_seconds\nseconds have passed, and returns true if promotion is successful and false\notherwise. If wait is set to false, the function returns true immediately\nafter sending a SIGUSR1 signal to the postmaster to trigger promotion.
+[PG_READ_BINARY_FILE]
+declaration=filename text [, offset bigint, length bigint ] [, missing_ok boolean ]
+category=System Administration Functions
+description=Returns all or part of a file. This function is identical to pg_read_file\nexcept that it can read arbitrary binary data, returning the result as\nbytea not text; accordingly, no encoding checks are performed.
+[PG_READ_FILE]
+declaration=filename text [, offset bigint, length bigint ] [, missing_ok boolean ]
+category=System Administration Functions
+description=Returns all or part of a text file, starting at the given byte offset,\nreturning at most length bytes (less if the end of file is reached first).\nIf offset is negative, it is relative to the end of the file. If offset and\nlength are omitted, the entire file is returned. The bytes read from the\nfile are interpreted as a string in the database's encoding; an error is\nthrown if they are not valid in that encoding.
+[PG_RELATION_FILENODE]
+declaration=relation regclass
+category=System Administration Functions
+description=Returns the "filenode" number currently assigned to the specified relation.\nThe filenode is the base component of the file name(s) used for the\nrelation (see Section 65.1 for more information). For most relations the\nresult is the same as pg_class.relfilenode, but for certain system catalogs\nrelfilenode is zero and this function must be used to get the correct\nvalue. The function returns NULL if passed a relation that does not have\nstorage, such as a view.
+[PG_RELATION_FILEPATH]
+declaration=relation regclass
+category=System Administration Functions
+description=Returns the entire file path name (relative to the database cluster's data\ndirectory, PGDATA) of the relation.
+[PG_RELATION_SIZE]
+declaration=relation regclass [, fork text ]
+category=System Administration Functions
+description=Computes the disk space used by one "fork" of the specified relation. (Note\nthat for most purposes it is more convenient to use the higher-level\nfunctions pg_total_relation_size or pg_table_size, which sum the sizes of\nall forks.) With one argument, this returns the size of the main data fork\nof the relation. The second argument can be provided to specify which fork\nto examine:
+[PG_RELOAD_CONF]
+declaration=
+category=System Administration Functions
+description=Causes all processes of the PostgreSQL server to reload their configuration\nfiles. (This is initiated by sending a SIGHUP signal to the postmaster\nprocess, which in turn sends SIGHUP to each of its children.) You can use\nthe pg_file_settings, pg_hba_file_rules and pg_ident_file_mappings views to\ncheck the configuration files for possible errors, before reloading.
+[PG_REPLICATION_ORIGIN_ADVANCE]
+declaration=node_name text, lsn pg_lsn
+category=System Administration Functions
+description=Sets replication progress for the given node to the given location. This is\nprimarily useful for setting up the initial location, or setting a new\nlocation after configuration changes and similar. Be aware that careless\nuse of this function can lead to inconsistently replicated data.
+[PG_REPLICATION_ORIGIN_CREATE]
+declaration=node_name text
+category=System Administration Functions
+description=Creates a replication origin with the given external name, and returns the\ninternal ID assigned to it.
+[PG_REPLICATION_ORIGIN_DROP]
+declaration=node_name text
+category=System Administration Functions
+description=Deletes a previously-created replication origin, including any associated\nreplay progress.
+[PG_REPLICATION_ORIGIN_OID]
+declaration=node_name text
+category=System Administration Functions
+description=Looks up a replication origin by name and returns the internal ID. If no\nsuch replication origin is found, NULL is returned.
+[PG_REPLICATION_ORIGIN_PROGRESS]
+declaration=node_name text, flush boolean
+category=System Administration Functions
+description=Returns the replay location for the given replication origin. The parameter\nflush determines whether the corresponding local transaction will be\nguaranteed to have been flushed to disk or not.
+[PG_REPLICATION_ORIGIN_SESSION_IS_SETUP]
+declaration=
+category=System Administration Functions
+description=Returns true if a replication origin has been selected in the current\nsession.
+[PG_REPLICATION_ORIGIN_SESSION_PROGRESS]
+declaration=flush boolean
+category=System Administration Functions
+description=Returns the replay location for the replication origin selected in the\ncurrent session. The parameter flush determines whether the corresponding\nlocal transaction will be guaranteed to have been flushed to disk or not.
+[PG_REPLICATION_ORIGIN_SESSION_RESET]
+declaration=
+category=System Administration Functions
+description=Cancels the effects of pg_replication_origin_session_setup().
+[PG_REPLICATION_ORIGIN_SESSION_SETUP]
+declaration=node_name text
+category=System Administration Functions
+description=Marks the current session as replaying from the given origin, allowing\nreplay progress to be tracked. Can only be used if no origin is currently\nselected. Use pg_replication_origin_session_reset to undo.
+[PG_REPLICATION_ORIGIN_XACT_RESET]
+declaration=
+category=System Administration Functions
+description=Cancels the effects of pg_replication_origin_xact_setup().
+[PG_REPLICATION_ORIGIN_XACT_SETUP]
+declaration=origin_lsn pg_lsn, origin_timestamp timestamp with time zone
+category=System Administration Functions
+description=Marks the current transaction as replaying a transaction that has committed\nat the given LSN and timestamp. Can only be called when a replication\norigin has been selected using pg_replication_origin_session_setup.
+[PG_REPLICATION_SLOT_ADVANCE]
+declaration=slot_name name, upto_lsn pg_lsn
+category=System Administration Functions
+description=Advances the current confirmed position of a replication slot named\nslot_name. The slot will not be moved backwards, and it will not be moved\nbeyond the current insert location. Returns the name of the slot and the\nactual position that it was advanced to. The updated slot position\ninformation is written out at the next checkpoint if any advancing is done.\nSo in the event of a crash, the slot may return to an earlier position. If\nthe specified slot is a logical failover slot then the function will not\nreturn until all physical slots specified in synchronized_standby_slots\nhave confirmed WAL receipt.
+[PG_ROTATE_LOGFILE]
+declaration=
+category=System Administration Functions
+description=Signals the log-file manager to switch to a new output file immediately.\nThis works only when the built-in log collector is running, since otherwise\nthere is no log-file manager subprocess.
+[PG_SAFE_SNAPSHOT_BLOCKING_PIDS]
+declaration=integer
+category=Session Information Functions
+description=Returns an array of the process ID(s) of the sessions that are blocking the\nserver process with the specified process ID from acquiring a safe\nsnapshot, or an empty array if there is no such server process or it is not\nblocked.
+[PG_SETTINGS_GET_FLAGS]
+declaration=guc text
+category=Session Information Functions
+description=Returns an array of the flags associated with the given GUC, or NULL if it\ndoes not exist. The result is an empty array if the GUC exists but there\nare no flags to show. Only the most useful flags listed in Table 9.78 are\nexposed.
+[PG_SIZE_BYTES]
+declaration=text
+category=System Administration Functions
+description=Converts a size in human-readable format (as returned by pg_size_pretty)\ninto bytes. Valid units are bytes, B, kB, MB, GB, TB, and PB.
+[PG_SNAPSHOT_XIP]
+declaration=pg_snapshot
+category=Session Information Functions
+description=Returns the set of in-progress transaction IDs contained in a snapshot.
+[PG_SNAPSHOT_XMAX]
+declaration=pg_snapshot
+category=Session Information Functions
+description=Returns the xmax of a snapshot.
+[PG_SNAPSHOT_XMIN]
+declaration=pg_snapshot
+category=Session Information Functions
+description=Returns the xmin of a snapshot.
+[PG_SPLIT_WALFILE_NAME]
+declaration=file_name text
+category=System Administration Functions
+description=Extracts the sequence number and timeline ID from a WAL file name.
+[PG_STATISTICS_OBJ_IS_VISIBLE]
+declaration=stat oid
+category=Session Information Functions
+description=Is statistics object visible in search path?
+[PG_STAT_FILE]
+declaration=filename text [, missing_ok boolean ]
+category=System Administration Functions
+description=Returns a record containing the file's size, last access time stamp, last\nmodification time stamp, last file status change time stamp (Unix platforms\nonly), file creation time stamp (Windows only), and a flag indicating if it\nis a directory.
+[PG_SWITCH_WAL]
+declaration=
+category=System Administration Functions
+description=Forces the server to switch to a new write-ahead log file, which allows the\ncurrent file to be archived (assuming you are using continuous archiving).\nThe result is the ending write-ahead log location plus 1 within the\njust-completed write-ahead log file. If there has been no write-ahead log\nactivity since the last write-ahead log switch, pg_switch_wal does nothing\nand returns the start location of the write-ahead log file currently in\nuse.
+[PG_SYNC_REPLICATION_SLOTS]
+declaration=
+category=System Administration Functions
+description=Synchronize the logical failover replication slots from the primary server\nto the standby server. This function can only be executed on the standby\nserver. Temporary synced slots, if any, cannot be used for logical decoding\nand must be dropped after promotion. See Section 47.2.3 for details. Note\nthat this function cannot be executed if sync_replication_slots is enabled\nand the slotsync worker is already running to perform the synchronization\nof slots.
+[PG_TABLESPACE_DATABASES]
+declaration=tablespace oid
+category=Session Information Functions
+description=Returns the set of OIDs of databases that have objects stored in the\nspecified tablespace. If this function returns any rows, the tablespace is\nnot empty and cannot be dropped. To identify the specific objects\npopulating the tablespace, you will need to connect to the database(s)\nidentified by pg_tablespace_databases and query their pg_class catalogs.
+[PG_TABLESPACE_LOCATION]
+declaration=tablespace oid
+category=Session Information Functions
+description=Returns the file system path that this tablespace is located in.
+[PG_TABLE_IS_VISIBLE]
+declaration=table oid
+category=Session Information Functions
+description=Is table visible in search path? (This works for all types of relations,\nincluding views, materialized views, indexes, sequences and foreign\ntables.)
+[PG_TABLE_SIZE]
+declaration=regclass
+category=System Administration Functions
+description=Computes the disk space used by the specified table, excluding indexes (but\nincluding its TOAST table if any, free space map, and visibility map).
+[PG_TERMINATE_BACKEND]
+declaration=pid integer, timeout bigint DEFAULT 0
+category=System Administration Functions
+description=Terminates the session whose backend process has the specified process ID.\nThis is also allowed if the calling role is a member of the role whose\nbackend is being terminated or the calling role has privileges of\npg_signal_backend, however only superusers can terminate superuser\nbackends.
+[PG_TOTAL_RELATION_SIZE]
+declaration=regclass
+category=System Administration Functions
+description=Computes the total disk space used by the specified table, including all\nindexes and TOAST data. The result is equivalent to pg_table_size +\npg_indexes_size.
+[PG_TRIGGER_DEPTH]
+declaration=
+category=Session Information Functions
+description=Returns the current nesting level of PostgreSQL triggers (0 if not called,\ndirectly or indirectly, from inside a trigger).
+[PG_TS_CONFIG_IS_VISIBLE]
+declaration=config oid
+category=Session Information Functions
+description=Is text search configuration visible in search path?
+[PG_TS_DICT_IS_VISIBLE]
+declaration=dict oid
+category=Session Information Functions
+description=Is text search dictionary visible in search path?
+[PG_TS_PARSER_IS_VISIBLE]
+declaration=parser oid
+category=Session Information Functions
+description=Is text search parser visible in search path?
+[PG_TS_TEMPLATE_IS_VISIBLE]
+declaration=template oid
+category=Session Information Functions
+description=Is text search template visible in search path?
+[PG_TYPEOF]
+declaration="any"
+category=Session Information Functions
+description=Returns the OID of the data type of the value that is passed to it. This\ncan be helpful for troubleshooting or dynamically constructing SQL queries.\nThe function is declared as returning regtype, which is an OID alias type\n(see Section 8.19); this means that it is the same as an OID for comparison\npurposes but displays as a type name.
+[PG_TYPE_IS_VISIBLE]
+declaration=type oid
+category=Session Information Functions
+description=Is type (or domain) visible in search path?
+[PG_VISIBLE_IN_SNAPSHOT]
+declaration=xid8, pg_snapshot
+category=Session Information Functions
+description=Is the given transaction ID visible according to this snapshot (that is,\nwas it completed before the snapshot was taken)? Note that this function\nwill not give the correct answer for a subtransaction ID (subxid); see\nSection 66.3 for details.
+[PG_WALFILE_NAME]
+declaration=lsn pg_lsn
+category=System Administration Functions
+description=Converts a write-ahead log location to the name of the WAL file holding\nthat location.
+[PG_WALFILE_NAME_OFFSET]
+declaration=lsn pg_lsn
+category=System Administration Functions
+description=Converts a write-ahead log location to a WAL file name and byte offset\nwithin that file.
+[PG_WAL_LSN_DIFF]
+declaration=lsn1 pg_lsn, lsn2 pg_lsn
+category=System Administration Functions
+description=Calculates the difference in bytes (lsn1 - lsn2) between two write-ahead\nlog locations. This can be used with pg_stat_replication or some of the\nfunctions shown in Table 9.95 to get the replication lag.
+[PG_WAL_REPLAY_PAUSE]
+declaration=
+category=System Administration Functions
+description=Request to pause recovery. A request doesn't mean that recovery stops right\naway. If you want a guarantee that recovery is actually paused, you need to\ncheck for the recovery pause state returned by\npg_get_wal_replay_pause_state(). Note that pg_is_wal_replay_paused()\nreturns whether a request is made. While recovery is paused, no further\ndatabase changes are applied. If hot standby is active, all new queries\nwill see the same consistent snapshot of the database, and no further query\nconflicts will be generated until recovery is resumed.
+[PG_WAL_REPLAY_RESUME]
+declaration=
+category=System Administration Functions
+description=Restarts recovery if it was paused.
+[PG_WAL_SUMMARY_CONTENTS]
+declaration=tli bigint, start_lsn pg_lsn, end_lsn pg_lsn
+category=Session Information Functions
+description=Returns one information about the contents of a single WAL summary file\nidentified by TLI and starting and ending LSNs. Each row with\nis_limit_block false indicates that the block identified by the remaining\noutput columns was modified by at least one WAL record within the range of\nrecords summarized by this file. Each row with is_limit_block true\nindicates either that (a) the relation fork was truncated to the length\ngiven by relblocknumber within the relevant range of WAL records or (b)\nthat the relation fork was created or dropped within the relevant range of\nWAL records; in such cases, relblocknumber will be zero.
+[PG_XACT_COMMIT_TIMESTAMP]
+declaration=xid
+category=Session Information Functions
+description=Returns the commit timestamp of a transaction.
+[PG_XACT_COMMIT_TIMESTAMP_ORIGIN]
+declaration=xid
+category=Session Information Functions
+description=Returns the commit timestamp and replication origin of a transaction.
+[PG_XACT_STATUS]
+declaration=xid8
+category=Session Information Functions
+description=Reports the commit status of a recent transaction. The result is one of in\nprogress, committed, or aborted, provided that the transaction is recent\nenough that the system retains the commit status of that transaction. If it\nis old enough that no references to the transaction survive in the system\nand the commit status information has been discarded, the result is NULL.\nApplications might use this function, for example, to determine whether\ntheir transaction committed or aborted after the application and database\nserver become disconnected while a COMMIT is in progress. Note that\nprepared transactions are reported as in progress; applications must check\npg_prepared_xacts if they need to determine whether a transaction ID\nbelongs to a prepared transaction.
+[PHRASETO_TSQUERY]
+declaration=[ config regconfig, ] query text
+category=Text Search Functions
+description=Converts text to a tsquery, normalizing words according to the specified or\ndefault configuration. Any punctuation in the string is ignored (it does\nnot determine query operators). The resulting query matches phrases\ncontaining all non-stopwords in the text.
+[PI]
+declaration=
+category=Numeric/Math Functions
+description=Approximate value of π
+[PLAINTO_TSQUERY]
+declaration=[ config regconfig, ] query text
+category=Text Search Functions
+description=Converts text to a tsquery, normalizing words according to the specified or\ndefault configuration. Any punctuation in the string is ignored (it does\nnot determine query operators). The resulting query matches documents\ncontaining all non-stopwords in the text.
+[POINT]
+declaration=double precision, double precision
+category=Geometric Functions
+description=Constructs point from its coordinates.
+[POLYGON]
+declaration=box
+category=Geometric Functions
+description=Converts box to a 4-point polygon.
+[POPEN]
+declaration=path
+category=Geometric Functions
+description=Converts path to open form.
+[POSITION1]
+name=POSITION
+declaration=substring text IN string text
+category=String Functions
+description=Returns first starting index of the specified substring within string, or\nzero if it's not present.
+[POSITION2]
+name=POSITION
+declaration=substring bytea IN bytes bytea
+category=Binary String Functions
+description=Returns first starting index of the specified substring within bytes, or\nzero if it's not present.
+[POSITION3]
+name=POSITION
+declaration=substring bit IN bits bit
+category=Bit String Functions
+description=Returns first starting index of the specified substring within bits, or\nzero if it's not present.
+[QUERYTREE]
+declaration=tsquery
+category=Text Search Functions
+description=Produces a representation of the indexable portion of a tsquery. A result\nthat is empty or just T indicates a non-indexable query.
+[QUOTE_IDENT]
+declaration=text
+category=String Functions
+description=Returns the given string suitably quoted to be used as an identifier in an\nSQL statement string. Quotes are added only if necessary (i.e., if the\nstring contains non-identifier characters or would be case-folded).\nEmbedded quotes are properly doubled. See also Example 41.1.
+[QUOTE_LITERAL]
+declaration=text
+category=String Functions
+description=Returns the given string suitably quoted to be used as a string literal in\nan SQL statement string. Embedded single-quotes and backslashes are\nproperly doubled. Note that quote_literal returns null on null input; if\nthe argument might be null, quote_nullable is often more suitable. See also\nExample 41.1.
+[QUOTE_NULLABLE]
+declaration=text
+category=String Functions
+description=Returns the given string suitably quoted to be used as a string literal in\nan SQL statement string; or, if the argument is null, returns NULL.\nEmbedded single-quotes and backslashes are properly doubled. See also\nExample 41.1.
+[RADIANS]
+declaration=double precision
+category=Numeric/Math Functions
+description=Converts degrees to radians
+[RADIUS]
+declaration=circle
+category=Geometric Functions
+description=Computes radius of circle.
+[RANDOM]
+declaration=
+category=Numeric/Math Functions
+description=Returns a random value in the range 0.0 <= x < 1.0
+[RANDOM_NORMAL]
+declaration=[ mean double precision [, stddev double precision ]]
+category=Numeric/Math Functions
+description=Returns a random value from the normal distribution with the given\nparameters; mean defaults to 0.0 and stddev defaults to 1.0
+[RANGE_MERGE1]
+name=RANGE_MERGE
+declaration=anyrange, anyrange
+category=Range Functions
+description=Computes the smallest range that includes both of the given ranges.
+[RANGE_MERGE2]
+name=RANGE_MERGE
+declaration=anymultirange
+category=Range Functions
+description=Computes the smallest range that includes the entire multirange.
+[RANK1]
+name=RANK
+declaration=args
+category=Aggregate Functions
+description=Computes the rank of the hypothetical row, with gaps; that is, the row\nnumber of the first row in its peer group.
+[RANK2]
+name=RANK
+declaration=
+category=Window Functions
+description=Returns the rank of the current row, with gaps; that is, the row_number of\nthe first row in its peer group.
+[REGEXP_COUNT]
+declaration=string text, pattern text [, start integer [, flags text ] ]
+category=String Functions
+description=Returns the number of times the POSIX regular expression pattern matches in\nthe string; see Section 9.7.3.
+[REGEXP_INSTR]
+declaration=string text, pattern text [, start integer [, N integer [, endoption integer [, flags text [, subexpr integer ] ] ] ] ]
+category=String Functions
+description=Returns the position within string where the N'th match of the POSIX\nregular expression pattern occurs, or zero if there is no such match; see\nSection 9.7.3.
+[REGEXP_LIKE]
+declaration=string text, pattern text [, flags text ]
+category=String Functions
+description=Checks whether a match of the POSIX regular expression pattern occurs\nwithin string; see Section 9.7.3.
+[REGEXP_MATCH]
+declaration=string text, pattern text [, flags text ]
+category=String Functions
+description=Returns substrings within the first match of the POSIX regular expression\npattern to the string; see Section 9.7.3.
+[REGEXP_MATCHES]
+declaration=string text, pattern text [, flags text ]
+category=String Functions
+description=Returns substrings within the first match of the POSIX regular expression\npattern to the string, or substrings within all such matches if the g flag\nis used; see Section 9.7.3.
+[REGEXP_REPLACE]
+declaration=string text, pattern text, replacement text [, start integer ] [, flags text ]
+category=String Functions
+description=Replaces the substring that is the first match to the POSIX regular\nexpression pattern, or all such matches if the g flag is used; see Section\n9.7.3.
+[REGEXP_SPLIT_TO_ARRAY]
+declaration=string text, pattern text [, flags text ]
+category=String Functions
+description=Splits string using a POSIX regular expression as the delimiter, producing\nan array of results; see Section 9.7.3.
+[REGEXP_SPLIT_TO_TABLE]
+declaration=string text, pattern text [, flags text ]
+category=String Functions
+description=Splits string using a POSIX regular expression as the delimiter, producing\na set of results; see Section 9.7.3.
+[REGEXP_SUBSTR]
+declaration=string text, pattern text [, start integer [, N integer [, flags text [, subexpr integer ] ] ] ]
+category=String Functions
+description=Returns the substring within string that matches the N'th occurrence of the\nPOSIX regular expression pattern, or NULL if there is no such match; see\nSection 9.7.3.
+[REGR_AVGX]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the average of the independent variable, sum(X)/N.
+[REGR_AVGY]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the average of the dependent variable, sum(Y)/N.
+[REGR_COUNT]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the number of rows in which both inputs are non-null.
+[REGR_R2]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the square of the correlation coefficient.
+[REGR_SXX]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the "sum of squares" of the independent variable, sum(X^2) -\nsum(X)^2/N.
+[REGR_SXY]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the "sum of products" of independent times dependent variables,\nsum(X*Y) - sum(X) * sum(Y)/N.
+[REGR_SYY]
+declaration=Y double precision, X double precision
+category=Aggregate Functions
+description=Computes the "sum of squares" of the dependent variable, sum(Y^2) -\nsum(Y)^2/N.
+[REPEAT]
+declaration=string text, number integer
+category=String Functions
+description=Repeats string the specified number of times.
+[REPLACE]
+declaration=string text, from text, to text
+category=String Functions
+description=Replaces all occurrences in string of substring from with substring to.
+[REVERSE]
+declaration=text
+category=String Functions
+description=Reverses the order of the characters in the string.
+[RIGHT]
+declaration=string text, n integer
+category=String Functions
+description=Returns last n characters in the string, or when n is negative, returns all\nbut first |n| characters.
+[ROW_NUMBER]
+declaration=
+category=Window Functions
+description=Returns the number of the current row within its partition, counting from\n1.
+[ROW_SECURITY_ACTIVE]
+declaration=table text or oid
+category=Session Information Functions
+description=Is row-level security active for the specified table in the context of the\ncurrent user and current environment?
+[ROW_TO_JSON]
+declaration=record [, boolean ]
+category=JSON Functions
+description=Converts an SQL composite value to a JSON object. The behavior is the same\nas to_json except that line feeds will be added between top-level elements\nif the optional boolean parameter is true.
+[RPAD]
+declaration=string text, length integer [, fill text ]
+category=String Functions
+description=Extends the string to length length by appending the characters fill (a\nspace by default). If the string is already longer than length then it is\ntruncated.
+[RTRIM1]
+name=RTRIM
+declaration=string text [, characters text ]
+category=String Functions
+description=Removes the longest string containing only characters in characters (a\nspace by default) from the end of string.
+[RTRIM2]
+name=RTRIM
+declaration=bytes bytea, bytesremoved bytea
+category=Binary String Functions
+description=Removes the longest string containing only bytes appearing in bytesremoved\nfrom the end of bytes.
+[SCALE]
+declaration=numeric
+category=Numeric/Math Functions
+description=Scale of the argument (the number of decimal digits in the fractional part)
+[SETSEED]
+declaration=double precision
+category=Numeric/Math Functions
+description=Sets the seed for subsequent random() and random_normal() calls; argument\nmust be between -1.0 and 1.0, inclusive
+[SETVAL]
+declaration=regclass, bigint [, boolean ]
+category=Sequence Manipulation Functions
+description=Sets the sequence object's current value, and optionally its is_called\nflag. The two-parameter form sets the sequence's last_value field to the\nspecified value and sets its is_called field to true, meaning that the next\nnextval will advance the sequence before returning a value. The value that\nwill be reported by currval is also set to the specified value. In the\nthree-parameter form, is_called can be set to either true or false. true\nhas the same effect as the two-parameter form. If it is set to false, the\nnext nextval will return exactly the specified value, and sequence\nadvancement commences with the following nextval. Furthermore, the value\nreported by currval is not changed in this case. For example,
+[SETWEIGHT1]
+name=SETWEIGHT
+declaration=vector tsvector, weight "char"
+category=Text Search Functions
+description=Assigns the specified weight to each element of the vector.
+[SETWEIGHT2]
+name=SETWEIGHT
+declaration=vector tsvector, weight "char", lexemes text[]
+category=Text Search Functions
+description=Assigns the specified weight to elements of the vector that are listed in\nlexemes. The strings in lexemes are taken as lexemes as-is, without further\nprocessing. Strings that do not match any lexeme in vector are ignored.
+[SET_BIT1]
+name=SET_BIT
+declaration=bytes bytea, n bigint, newvalue integer
+category=Binary String Functions
+description=Sets n'th bit in binary string to newvalue.
+[SET_BIT2]
+name=SET_BIT
+declaration=bits bit, n integer, newvalue integer
+category=Bit String Functions
+description=Sets n'th bit in bit string to newvalue; the first (leftmost) bit is bit 0.
+[SET_BYTE]
+declaration=bytes bytea, n integer, newvalue integer
+category=Binary String Functions
+description=Sets n'th byte in binary string to newvalue.
+[SET_CONFIG]
+declaration=setting_name text, new_value text, is_local boolean
+category=System Administration Functions
+description=Sets the parameter setting_name to new_value, and returns that value. If\nis_local is true, the new value will only apply during the current\ntransaction. If you want the new value to apply for the rest of the current\nsession, use false instead. This function corresponds to the SQL command\nSET.
+[SET_MASKLEN]
+declaration=inet, integer
+category=Network Address Functions
+description=Sets the netmask length for an inet value. The address part does not\nchange.
+[SHA224]
+declaration=bytea
+category=Binary String Functions
+description=Computes the SHA-224 hash of the binary string.
+[SHA256]
+declaration=bytea
+category=Binary String Functions
+description=Computes the SHA-256 hash of the binary string.
+[SHA384]
+declaration=bytea
+category=Binary String Functions
+description=Computes the SHA-384 hash of the binary string.
+[SHA512]
+declaration=bytea
+category=Binary String Functions
+description=Computes the SHA-512 hash of the binary string.
+[SHOBJ_DESCRIPTION]
+declaration=object oid, catalog name
+category=Session Information Functions
+description=Returns the comment for a shared database object specified by its OID and\nthe name of the containing system catalog. This is just like\nobj_description except that it is used for retrieving comments on shared\nobjects (that is, databases, roles, and tablespaces). Some system catalogs\nare global to all databases within each cluster, and the descriptions for\nobjects in them are stored globally as well.
+[SIN]
+declaration=double precision
+category=Numeric/Math Functions
+description=Sine, argument in radians
+[SIND]
+declaration=double precision
+category=Numeric/Math Functions
+description=Sine, argument in degrees
+[SINH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Hyperbolic sine
+[SLOPE]
+declaration=point, point
+category=Geometric Functions
+description=Computes slope of a line drawn through the two points.
+[SPLIT_PART]
+declaration=string text, delimiter text, n integer
+category=String Functions
+description=Splits string at occurrences of delimiter and returns the n'th field\n(counting from one), or when n is negative, returns the |n|'th-from-last\nfield.
+[STARTS_WITH]
+declaration=string text, prefix text
+category=String Functions
+description=Returns true if string starts with prefix.
+[STATEMENT_TIMESTAMP]
+declaration=
+category=Date/Time Functions
+description=Current date and time (start of current statement); see Section 9.9.5
+[STRING_TO_ARRAY]
+declaration=string text, delimiter text [, null_string text ]
+category=String Functions
+description=Splits the string at occurrences of delimiter and forms the resulting\nfields into a text array. If delimiter is NULL, each character in the\nstring will become a separate element in the array. If delimiter is an\nempty string, then the string is treated as a single field. If null_string\nis supplied and is not NULL, fields matching that string are replaced by\nNULL. See also array_to_string.
+[STRING_TO_TABLE]
+declaration=string text, delimiter text [, null_string text ]
+category=String Functions
+description=Splits the string at occurrences of delimiter and returns the resulting\nfields as a set of text rows. If delimiter is NULL, each character in the\nstring will become a separate row of the result. If delimiter is an empty\nstring, then the string is treated as a single field. If null_string is\nsupplied and is not NULL, fields matching that string are replaced by NULL.
+[STRIP]
+declaration=tsvector
+category=Text Search Functions
+description=Removes positions and weights from the tsvector.
+[STRPOS]
+declaration=string text, substring text
+category=String Functions
+description=Returns first starting index of the specified substring within string, or\nzero if it's not present. (Same as position(substring in string), but note\nthe reversed argument order.)
+[SUBSTR1]
+name=SUBSTR
+declaration=string text, start integer [, count integer ]
+category=String Functions
+description=Extracts the substring of string starting at the start'th character, and\nextending for count characters if that is specified. (Same as\nsubstring(string from start for count).)
+[SUBSTR2]
+name=SUBSTR
+declaration=bytes bytea, start integer [, count integer ]
+category=Binary String Functions
+description=Extracts the substring of bytes starting at the start'th byte, and\nextending for count bytes if that is specified. (Same as substring(bytes\nfrom start for count).)
+[SUBSTRING1]
+name=SUBSTRING
+declaration=string text [ FROM start integer ] [ FOR count integer ]
+category=String Functions
+description=Extracts the substring of string starting at the start'th character if that\nis specified, and stopping after count characters if that is specified.\nProvide at least one of start and count.
+[SUBSTRING2]
+name=SUBSTRING
+declaration=bytes bytea [ FROM start integer ] [ FOR count integer ]
+category=Binary String Functions
+description=Extracts the substring of bytes starting at the start'th byte if that is\nspecified, and stopping after count bytes if that is specified. Provide at\nleast one of start and count.
+[SUBSTRING3]
+name=SUBSTRING
+declaration=bits bit [ FROM start integer ] [ FOR count integer ]
+category=Bit String Functions
+description=Extracts the substring of bits starting at the start'th bit if that is\nspecified, and stopping after count bits if that is specified. Provide at\nleast one of start and count.
+[SUPPRESS_REDUNDANT_UPDATES_TRIGGER]
+declaration=
+category=Trigger Functions
+description=Suppresses do-nothing update operations. See below for details.
+[TAN]
+declaration=double precision
+category=Numeric/Math Functions
+description=Tangent, argument in radians
+[TAND]
+declaration=double precision
+category=Numeric/Math Functions
+description=Tangent, argument in degrees
+[TANH]
+declaration=double precision
+category=Numeric/Math Functions
+description=Hyperbolic tangent
+[TEXT]
+declaration=inet
+category=Network Address Functions
+description=Returns the unabbreviated IP address and netmask length as text. (This has\nthe same result as an explicit cast to text.)
+[TIMEOFDAY]
+declaration=
+category=Date/Time Functions
+description=Current date and time (like clock_timestamp, but as a text string); see\nSection 9.9.5
+[TO_JSONB]
+declaration=anyelement
+category=JSON Functions
+description=Converts any SQL value to json or jsonb. Arrays and composites are\nconverted recursively to arrays and objects (multidimensional arrays become\narrays of arrays in JSON). Otherwise, if there is a cast from the SQL data\ntype to json, the cast function will be used to perform the conversion;[a]\notherwise, a scalar JSON value is produced. For any scalar other than a\nnumber, a Boolean, or a null value, the text representation will be used,\nwith escaping as necessary to make it a valid JSON string value.
+[TO_REGCLASS]
+declaration=text
+category=Session Information Functions
+description=Translates a textual relation name to its OID. A similar result is obtained\nby casting the string to type regclass (see Section 8.19); however, this\nfunction will return NULL rather than throwing an error if the name is not\nfound.
+[TO_REGCOLLATION]
+declaration=text
+category=Session Information Functions
+description=Translates a textual collation name to its OID. A similar result is\nobtained by casting the string to type regcollation (see Section 8.19);\nhowever, this function will return NULL rather than throwing an error if\nthe name is not found.
+[TO_REGNAMESPACE]
+declaration=text
+category=Session Information Functions
+description=Translates a textual schema name to its OID. A similar result is obtained\nby casting the string to type regnamespace (see Section 8.19); however,\nthis function will return NULL rather than throwing an error if the name is\nnot found.
+[TO_REGOPER]
+declaration=text
+category=Session Information Functions
+description=Translates a textual operator name to its OID. A similar result is obtained\nby casting the string to type regoper (see Section 8.19); however, this\nfunction will return NULL rather than throwing an error if the name is not\nfound or is ambiguous.
+[TO_REGOPERATOR]
+declaration=text
+category=Session Information Functions
+description=Translates a textual operator name (with parameter types) to its OID. A\nsimilar result is obtained by casting the string to type regoperator (see\nSection 8.19); however, this function will return NULL rather than throwing\nan error if the name is not found.
+[TO_REGPROC]
+declaration=text
+category=Session Information Functions
+description=Translates a textual function or procedure name to its OID. A similar\nresult is obtained by casting the string to type regproc (see Section\n8.19); however, this function will return NULL rather than throwing an\nerror if the name is not found or is ambiguous.
+[TO_REGPROCEDURE]
+declaration=text
+category=Session Information Functions
+description=Translates a textual function or procedure name (with argument types) to\nits OID. A similar result is obtained by casting the string to type\nregprocedure (see Section 8.19); however, this function will return NULL\nrather than throwing an error if the name is not found.
+[TO_REGROLE]
+declaration=text
+category=Session Information Functions
+description=Translates a textual role name to its OID. A similar result is obtained by\ncasting the string to type regrole (see Section 8.19); however, this\nfunction will return NULL rather than throwing an error if the name is not\nfound.
+[TO_REGTYPE]
+declaration=text
+category=Session Information Functions
+description=Parses a string of text, extracts a potential type name from it, and\ntranslates that name into a type OID. A syntax error in the string will\nresult in an error; but if the string is a syntactically valid type name\nthat happens not to be found in the catalogs, the result is NULL. A similar\nresult is obtained by casting the string to type regtype (see Section\n8.19), except that that will throw error for name not found.
+[TO_REGTYPEMOD]
+declaration=text
+category=Session Information Functions
+description=Parses a string of text, extracts a potential type name from it, and\ntranslates its type modifier, if any. A syntax error in the string will\nresult in an error; but if the string is a syntactically valid type name\nthat happens not to be found in the catalogs, the result is NULL. The\nresult is -1 if no type modifier is present.
+[TO_TIMESTAMP]
+declaration=double precision
+category=Date/Time Functions
+description=Convert Unix epoch (seconds since 1970-01-01 00:00:00+00) to timestamp with\ntime zone
+[TO_TSQUERY]
+declaration=[ config regconfig, ] query text
+category=Text Search Functions
+description=Converts text to a tsquery, normalizing words according to the specified or\ndefault configuration. The words must be combined by valid tsquery\noperators.
+[TO_TSVECTOR]
+declaration=[ config regconfig, ] document text
+category=Text Search Functions
+description=Converts text to a tsvector, normalizing words according to the specified\nor default configuration. Position information is included in the result.
+[TRANSACTION_TIMESTAMP]
+declaration=
+category=Date/Time Functions
+description=Current date and time (start of current transaction); see Section 9.9.5
+[TRANSLATE]
+declaration=string text, from text, to text
+category=String Functions
+description=Replaces each character in string that matches a character in the from set\nwith the corresponding character in the to set. If from is longer than to,\noccurrences of the extra characters in from are deleted.
+[TRIM1]
+name=TRIM
+declaration=[ LEADING | TRAILING | BOTH ] [ characters text ] FROM string text
+category=String Functions
+description=Removes the longest string containing only characters in characters (a\nspace by default) from the start, end, or both ends (BOTH is the default)\nof string.
+[TRIM2]
+name=TRIM
+declaration=[ LEADING | TRAILING | BOTH ] bytesremoved bytea FROM bytes bytea
+category=Binary String Functions
+description=Removes the longest string containing only bytes appearing in bytesremoved\nfrom the start, end, or both ends (BOTH is the default) of bytes.
+[TRIM_ARRAY]
+declaration=array anyarray, n integer
+category=Array Functions
+description=Trims an array by removing the last n elements. If the array is\nmultidimensional, only the first dimension is trimmed.
+[TRIM_SCALE]
+declaration=numeric
+category=Numeric/Math Functions
+description=Reduces the value's scale (number of fractional decimal digits) by removing\ntrailing zeroes
+[TRUNC]
+declaration=macaddr
+category=Network Address Functions
+description=Sets the last 3 bytes of the address to zero. The remaining prefix can be\nassociated with a particular manufacturer (using data not included in\nPostgreSQL).
+[TSQUERY_PHRASE]
+declaration=query1 tsquery, query2 tsquery
+category=Text Search Functions
+description=Constructs a phrase query that searches for matches of query1 and query2 at\nsuccessive lexemes (same as <-> operator).
+[TSVECTOR_TO_ARRAY]
+declaration=tsvector
+category=Text Search Functions
+description=Converts a tsvector to an array of lexemes.
+[TSVECTOR_UPDATE_TRIGGER]
+declaration=
+category=Trigger Functions
+description=Automatically updates a tsvector column from associated plain-text document\ncolumn(s). The text search configuration to use is specified by name as a\ntrigger argument. See Section 12.4.3 for details.
+[TSVECTOR_UPDATE_TRIGGER_COLUMN]
+declaration=
+category=Trigger Functions
+description=Automatically updates a tsvector column from associated plain-text document\ncolumn(s). The text search configuration to use is taken from a regconfig\ncolumn of the table. See Section 12.4.3 for details.
+[TS_DEBUG]
+declaration=[ config regconfig, ] document text
+category=Text Search Functions
+description=Extracts and normalizes tokens from the document according to the specified\nor default text search configuration, and returns information about how\neach token was processed. See Section 12.8.1 for details.
+[TS_DELETE]
+declaration=vector tsvector, lexeme text
+category=Text Search Functions
+description=Removes any occurrence of the given lexeme from the vector. The lexeme\nstring is treated as a lexeme as-is, without further processing.
+[TS_FILTER]
+declaration=vector tsvector, weights "char"[]
+category=Text Search Functions
+description=Selects only elements with the given weights from the vector.
+[TS_HEADLINE]
+declaration=[ config regconfig, ] document text, query tsquery [, options text ]
+category=Text Search Functions
+description=Displays, in an abbreviated form, the match(es) for the query in the\ndocument, which must be raw text not a tsvector. Words in the document are\nnormalized according to the specified or default configuration before\nmatching to the query. Use of this function is discussed in Section 12.3.4,\nwhich also describes the available options.
+[TS_LEXIZE]
+declaration=dict regdictionary, token text
+category=Text Search Functions
+description=Returns an array of replacement lexemes if the input token is known to the\ndictionary, or an empty array if the token is known to the dictionary but\nit is a stop word, or NULL if it is not a known word. See Section 12.8.3\nfor details.
+[TS_PARSE]
+declaration=parser_name text, document text
+category=Text Search Functions
+description=Extracts tokens from the document using the named parser. See Section\n12.8.2 for details.
+[TS_RANK]
+declaration=[ weights real[], ] vector tsvector, query tsquery [, normalization integer ]
+category=Text Search Functions
+description=Computes a score showing how well the vector matches the query. See Section\n12.3.3 for details.
+[TS_RANK_CD]
+declaration=[ weights real[], ] vector tsvector, query tsquery [, normalization integer ]
+category=Text Search Functions
+description=Computes a score showing how well the vector matches the query, using a\ncover density algorithm. See Section 12.3.3 for details.
+[TS_REWRITE]
+declaration=query tsquery, target tsquery, substitute tsquery
+category=Text Search Functions
+description=Replaces occurrences of target with substitute within the query. See\nSection 12.4.2.1 for details.
+[TS_STAT]
+declaration=sqlquery text [, weights text ]
+category=Text Search Functions
+description=Executes the sqlquery, which must return a single tsvector column, and\nreturns statistics about each distinct lexeme contained in the data. See\nSection 12.4.4 for details.
+[TS_TOKEN_TYPE]
+declaration=parser_name text
+category=Text Search Functions
+description=Returns a table that describes each type of token the named parser can\nrecognize. See Section 12.8.2 for details.
+[TXID_CURRENT]
+declaration=
+category=Session Information Functions
+description=See pg_current_xact_id().
+[TXID_CURRENT_IF_ASSIGNED]
+declaration=
+category=Session Information Functions
+description=See pg_current_xact_id_if_assigned().
+[TXID_CURRENT_SNAPSHOT]
+declaration=
+category=Session Information Functions
+description=See pg_current_snapshot().
+[TXID_SNAPSHOT_XIP]
+declaration=txid_snapshot
+category=Session Information Functions
+description=See pg_snapshot_xip().
+[TXID_SNAPSHOT_XMAX]
+declaration=txid_snapshot
+category=Session Information Functions
+description=See pg_snapshot_xmax().
+[TXID_SNAPSHOT_XMIN]
+declaration=txid_snapshot
+category=Session Information Functions
+description=See pg_snapshot_xmin().
+[TXID_STATUS]
+declaration=bigint
+category=Session Information Functions
+description=See pg_xact_status().
+[TXID_VISIBLE_IN_SNAPSHOT]
+declaration=bigint, txid_snapshot
+category=Session Information Functions
+description=See pg_visible_in_snapshot().
+[UNICODE_ASSIGNED]
+declaration=text
+category=String Functions
+description=Returns true if all characters in the string are assigned Unicode\ncodepoints; false otherwise. This function can only be used when the server\nencoding is UTF8.
+[UNICODE_VERSION]
+declaration=
+category=Session Information Functions
+description=Returns a string representing the version of Unicode used by PostgreSQL.
+[UNISTR]
+declaration=text
+category=String Functions
+description=Evaluate escaped Unicode characters in the argument. Unicode characters can\nbe specified as \XXXX (4 hexadecimal digits), \+XXXXXX (6 hexadecimal\ndigits), \uXXXX (4 hexadecimal digits), or \UXXXXXXXX (8 hexadecimal\ndigits). To specify a backslash, write two backslashes. All other\ncharacters are taken literally.
+[UNNEST1]
+name=UNNEST
+declaration=tsvector
+category=Text Search Functions
+description=Expands a tsvector into a set of rows, one per lexeme.
+[UNNEST2]
+name=UNNEST
+declaration=anyarray
+category=Array Functions
+description=Expands an array into a set of rows. The array's elements are read out in\nstorage order.
+[UNNEST3]
+name=UNNEST
+declaration=anymultirange
+category=Range Functions
+description=Expands a multirange into a set of ranges in ascending order.
+[UPPER1]
+name=UPPER
+declaration=text
+category=String Functions
+description=Converts the string to all upper case, according to the rules of the\ndatabase's locale.
+[UPPER2]
+name=UPPER
+declaration=anyrange
+category=Range Functions
+description=Extracts the upper bound of the range (NULL if the range is empty or has no\nupper bound).
+[UPPER3]
+name=UPPER
+declaration=anymultirange
+category=Range Functions
+description=Extracts the upper bound of the multirange (NULL if the multirange is empty\nor has no upper bound).
+[UPPER_INC1]
+name=UPPER_INC
+declaration=anyrange
+category=Range Functions
+description=Is the range's upper bound inclusive?
+[UPPER_INC2]
+name=UPPER_INC
+declaration=anymultirange
+category=Range Functions
+description=Is the multirange's upper bound inclusive?
+[UPPER_INF1]
+name=UPPER_INF
+declaration=anyrange
+category=Range Functions
+description=Does the range have no upper bound? (An upper bound of Infinity returns\nfalse.)
+[UPPER_INF2]
+name=UPPER_INF
+declaration=anymultirange
+category=Range Functions
+description=Does the multirange have no upper bound? (An upper bound of Infinity\nreturns false.)
+[VARIANCE]
+declaration=numeric_type
+category=Aggregate Functions
+description=This is a historical alias for var_samp.
+[VERSION]
+declaration=
+category=Session Information Functions
+description=Returns a string describing the PostgreSQL server's version. You can also\nget this information from server_version, or for a machine-readable version\nuse server_version_num. Software developers should use server_version_num\n(available since 8.2) or PQserverVersion instead of parsing the text\nversion.
+[WEBSEARCH_TO_TSQUERY]
+declaration=[ config regconfig, ] query text
+category=Text Search Functions
+description=Converts text to a tsquery, normalizing words according to the specified or\ndefault configuration. Quoted word sequences are converted to phrase tests.\nThe word "or" is understood as producing an OR operator, and a dash\nproduces a NOT operator; other punctuation is ignored. This approximates\nthe behavior of some common web search tools.
+[WIDTH]
+declaration=box
+category=Geometric Functions
+description=Computes horizontal size of box.
+[XMLAGG]
+declaration=xml ORDER BY input_sort_columns
+category=Aggregate Functions
+description=Concatenates the non-null XML input values (see Section 9.15.1.8).
\ No newline at end of file
diff --git a/out/functions-redshift.ini b/out/functions-redshift.ini
new file mode 100644
index 00000000..5e1c1443
--- /dev/null
+++ b/out/functions-redshift.ini
@@ -0,0 +1,684 @@
+[ABS]
+declaration=number
+category=Math Functions
+description=ABS calculates the absolute value of a number, where that number can be a literal or an expression that evaluates to a number.
+[ACOS]
+declaration=number
+category=Math Functions
+description=ACOS is a trigonometric function that returns the arc cosine of a number. The return value is in radians and is between PI/2 and -PI/2.
+[ADD_MONTHS]
+declaration=date,timestamp,integer
+category=Date and Time Functions
+description=ADD_MONTHS adds the specified number of months to a date or time stamp value or expression. The DATEADD function provides similar functionality.
+[APPROXIMATE]
+declaration=percentile,expr
+category=Aggregate Functions
+description=APPROXIMATE PERCENTILE_DISC is an inverse distribution function that assumes a discrete distribution model. It takes a percentile value and a sort specification and returns an element from the given set. Approximation enables the function to execute much faster, with a low relative error of around 0.5 percent.
+[ASIN]
+declaration=number
+category=Math Functions
+description=ASIN is a trigonometric function that returns the arc sine of a number. The return value is in radians and is between PI/2 and -PI/2.
+[ATAN2]
+declaration=number1,number2
+category=Math Functions
+description=ATAN2 is a trigonometric function that returns the arc tangent of a one number divided by another number. The return value is in radians and is between PI/2 and -PI/2.
+[ATAN]
+declaration=number
+category=Math Functions
+description=ATAN is a trigonometric function that returns the arc tangent of a number. The return value is in radians and is between PI/2 and -PI/2.
+[AVG]
+declaration=expression
+category=Aggregate Functions
+description=The AVG function returns the average (arithmetic mean) of the input expression values. The AVG function works with numeric values and ignores NULL values.
+[BIT_AND]
+declaration=expression
+category=Bit-Wise Aggregate Functions
+description=
+[BIT_OR]
+declaration=expression
+category=Bit-Wise Aggregate Functions
+description=
+[BOOL_AND]
+declaration=expression
+category=Bit-Wise Aggregate Functions
+description=
+[BOOL_OR]
+declaration=expression
+category=Bit-Wise Aggregate Functions
+description=
+[BTRIM]
+declaration=string,matching_string
+category=String Functions
+description=The BTRIM function trims a string by removing leading and trailing blanks or by removing characters that match an optional specified string.
+[BTTEXT_PATTERN_CMP]
+declaration=
+category=String Functions
+description=Synonym for the BPCHARCMP function.
+[CASE]
+declaration=expression,value,result,Boolean condition
+category=Conditional Expressions
+description=The CASE expression is a conditional expression, similar to if/then/else statements found in other languages. CASE is used to specify a result when there are multiple conditions.
+[CAST]
+declaration=expression,type
+category=Data Type Formatting Functions
+description=You can do run-time conversions between compatible data types by using the CAST and CONVERT functions.
+[CBRT]
+declaration=
+category=Math Functions
+description=The CBRT function is a mathematical function that calculates the cube root of a number.
+[CEILING]
+declaration=number
+category=Math Functions
+description=The CEILING or CEIL function is used to round a number up to the next whole number. (The FLOOR Function rounds a number down to the next whole number.)
+[CEIL]
+declaration=number
+category=Math Functions
+description=The CEILING or CEIL function is used to round a number up to the next whole number. (The FLOOR Function rounds a number down to the next whole number.)
+[CHARACTER_LENGTH]
+declaration=
+category=String Functions
+description=Synonym of the LEN function.
+[CHARINDEX]
+declaration=substring,string
+category=String Functions
+description=Returns the location of the specified substring within a string. Synonym of the STRPOS function.
+[CHAR_LENGTH]
+declaration=
+category=String Functions
+description=Synonym of the LEN function.
+[CHECKSUM]
+declaration=expression
+category=Math Functions
+description=Computes a checksum value for building a hash index.
+[CHR]
+declaration=number
+category=String Functions
+description=The CHR function returns the character that matches the ASCII code point value specified by of the input parameter.
+[COALESCE]
+declaration=
+category=Conditional Expressions
+description=Synonym of the NVL expression.
+[CONCAT]
+declaration=string1,string2
+category=String Functions
+description=The CONCAT function concatenates two character strings and returns the resulting string. To concatenate more than two strings, use nested CONCAT functions. The concatenation operator (||) between two strings produces the same results as the CONCAT function.
+[CONVERT]
+declaration=expression,type
+category=Data Type Formatting Functions
+description=You can do run-time conversions between compatible data types by using the CAST and CONVERT functions.
+[CONVERT_TIMEZONE]
+declaration=source_timezone,target_timezone,timestamp
+category=Date and Time Functions
+description=CONVERT_TIMEZONE converts a time stamp from one time zone to another.
+[COS]
+declaration=number
+category=Math Functions
+description=COS is a trigonometric function that returns the cosine of a number. The return value is in radians and is between PI/2 and -PI/2.
+[COT]
+declaration=number
+category=Math Functions
+description=COT is a trigonometric function that returns the cotangent of a number. The input parameter must be nonzero.
+[COUNT]
+declaration=expression
+category=Aggregate Functions
+description=The COUNT function counts the rows defined by the expression.
+[CRC32]
+declaration=string
+category=String Functions
+description=CRC32 is an error-detecting function that uses a CRC32 algorithm to detect changes between source and target data. The CRC32 function converts a variable-length string into an 8-character string that is a text representation of the hexadecimal value of a 32 bit-binary sequence.
+[CUME_DIST]
+declaration=partition_expression,order_list
+category=Window Functions
+description=Calculates the cumulative distribution of a value within a window or partition. Assuming ascending ordering, the cumulative distribution is determined using this formula:
+[CURRENT_DATABASE]
+declaration=
+category=System Information Functions
+description=Returns the name of the database where you are currently connected.
+[CURRENT_DATE]
+declaration=
+category=Date and Time Functions
+description=CURRENT_DATE returns a date in the current session time zone (UTC by default) in the default format: YYYY-MM-DD.
+[CURRENT_SCHEMAS]
+declaration=include_implicit
+category=System Information Functions
+description=Returns an array of the names of any schemas in the current search path. The current search path is defined in the search_path parameter.
+[CURRENT_SCHEMA]
+declaration=
+category=System Information Functions
+description=Returns the name of the schema at the front of the search path. This schema will be used for any tables or other named objects that are created without specifying a target schema.
+[CURRENT_SETTING]
+declaration=parameter
+category=System Administration Functions
+description=CURRENT_SETTING returns the current value of the specified configuration parameter.
+[CURRENT_USER]
+declaration=
+category=System Information Functions
+description=Returns the user name of the current "effective" user of the database, as applicable to checking permissions. Usually, this user name will be the same as the session user; however, this can occasionally be changed by superusers.
+[CURRENT_USER_ID]
+declaration=
+category=System Information Functions
+description=Returns the unique identifier for the Amazon Redshift user logged in to the current session.
+[DATEADD]
+declaration=datepart,interval,date,timestamp
+category=Date and Time Functions
+description=Increments a date or time stamp value by a specified interval.
+[DATEDIFF]
+declaration=datepart,datepart boundaries,date,timestamp
+category=Date and Time Functions
+description=DATEDIFF returns the difference between the date parts of two date or time expressions.
+[DATE_CMP]
+declaration=date1,date2
+category=Date and Time Functions
+description=DATE_CMP compares two dates. The function returns 0 if the dates are identical, 1 if date1 is greater, and -1 if date2 is greater.
+[DATE_CMP_TIMESTAMPTZ]
+declaration=date,timestamptz
+category=Date and Time Functions
+description=DATE_CMP_TIMESTAMPTZ compares a date to a time stamp with time zone. If the date and time stamp values are identical, the function returns 0. If the date is greater alphabetically, the function returns 1. If the time stamp is greater, the function returns –1.
+[DATE_CMP_TIMESTAMP]
+declaration=date,timestamp
+category=Date and Time Functions
+description=Compares a date to a time stamp and returns 0 if the values are identical, 1 if date is greater alphabetically and -1 if timestamp is greater.
+[DATE_PART]
+declaration=datepart,date,timestamp
+category=Date and Time Functions
+description=DATE_PART extracts datepart values from an expression. DATE_PART is a synonym of the PGDATE_PART function.
+[DATE_PART_YEAR]
+declaration=date
+category=Date and Time Functions
+description=The DATE_PART_YEAR function extracts the year from a date.
+[DATE_TRUNC]
+declaration=datepart,timestamp
+category=Date and Time Functions
+description=The DATE_TRUNC function truncates a time stamp expression or literal based on the date part that you specify, such as hour, week, or month. DATE_TRUNC returns the first day of the specified year, the first day of the specified month, or the Monday of the specified week.
+[DECODE]
+declaration=expression,search,result,default
+category=Conditional Expressions
+description=A DECODE expression replaces a specific value with either another specific value or a default value, depending on the result of an equality condition. This operation is equivalent to the operation of a simple CASE expression or an IF-THEN-ELSE statement.
+[DEGREES]
+declaration=number
+category=Math Functions
+description=Converts an angle in radians to its equivalent in degrees.
+[DENSE_RANK]
+declaration=expr_list,order_list
+category=Window Functions
+description=The DENSE_RANK window function determines the rank of a value in a group of values, based on the ORDER BY expression in the OVER clause. If the optional PARTITION BY clause is present, the rankings are reset for each group of rows. Rows with equal values for the ranking criteria receive the same rank. The DENSE_RANK function differs from RANK in one respect: If two or more rows tie, there is no gap in the sequence of ranked values. For example, if two rows are ranked 1, the next rank is 2.
+[DEXP]
+declaration=number
+category=Math Functions
+description=The DEXP function returns the exponential value in scientific notation for a double precision number. The only difference between the DEXP and EXP functions is that the parameter for DEXP must be a double precision.
+[DLOG10]
+declaration=number
+category=Math Functions
+description=The DLOG10 returns the base 10 logarithm of the input parameter. Synonym of the LOG function.
+[DLOG1]
+declaration=
+category=Math Functions
+description=The DLOG1 function returns the natural logarithm of the input parameter. Synonym for the LN function.
+[EXP]
+declaration=expression
+category=Math Functions
+description=The EXP function returns the exponential value in scientific notation for a numeric expression.
+[EXTRACT]
+declaration=datepart,literal,timestamp
+category=Date and Time Functions
+description=The EXTRACT function returns a date part, such as a day, month, or year, from a time stamp value or expression.
+[FIRST_VALUE]
+declaration=expression,expr_list,order_list,frame_clause
+category=Window Functions
+description=Given an ordered set of rows, FIRST_VALUE returns the value of the specified expression with respect to the first row in the window frame. The LAST_VALUE function returns the value of the expression with respect to the last row in the frame.
+[FLOOR]
+declaration=number
+category=Math Functions
+description=The FLOOR function rounds a number down to the next whole number.
+[FUNC_SHA1]
+declaration=string
+category=String Functions
+description=The FUNC_SHA1 function uses the SHA1 cryptographic hash function to convert a variable-length string into a 40-character string that is a text representation of the hexadecimal value of a 160-bit checksum.
+[GETDATE]
+declaration=
+category=Date and Time Functions
+description=GETDATE returns the current date and time in the current session time zone (UTC by default).
+[GREATEST]
+declaration=expression_list
+category=Conditional Expressions
+description=Returns the largest value from a list of any number of expressions.
+[HAS_DATABASE_PRIVILEGE]
+declaration=user,database,privilege
+category=System Information Functions
+description=Returns true if the user has the specified privilege for the specified database. For more information about privileges, see GRANT.
+[HAS_SCHEMA_PRIVILEGE]
+declaration=user,schema,privilege
+category=System Information Functions
+description=Returns true if the user has the specified privilege for the specified schema. For more information about privileges, see GRANT.
+[HAS_TABLE_PRIVILEGE]
+declaration=user,table,privilege
+category=System Information Functions
+description=Returns true if the user has the specified privilege for the specified table.
+[INITCAP]
+declaration=string
+category=String Functions
+description=Capitalizes the first letter of each word in a specified string. INITCAP supports UTF-8 multibyte characters, up to a maximum of four bytes per character.
+[INTERVAL_CMP]
+declaration=interval1,interval2
+category=Date and Time Functions
+description=INTERVAL_CMP compares two intervals and returns 1 if the first interval is greater, -1 if the second interval is greater, and 0 if the intervals are equal. For more information, see Interval Literals.
+[IS_VALID_JSON]
+declaration=json_string
+category=JSON Functions
+description=IS_VALID_JSON validates a JSON string. The function returns Boolean true (t) if the string is properly formed JSON or false (f) if the string is malformed. To validate a JSON array, use IS_VALID_JSON_ARRAY Function
+[IS_VALID_JSON_ARRAY]
+declaration=json_array
+category=JSON Functions
+description=IS_VALID_JSON_ARRAY validates a JSON array. The function returns Boolean true (t) if the array is properly formed JSON or false (f) if the array is malformed. To validate a JSON string, use IS_VALID_JSON Function
+[JSON_ARRAY_LENGTH]
+declaration=json_array,null_if_invalid
+category=JSON Functions
+description=JSON_ARRAY_LENGTH returns the number of elements in the outer array of a JSON string. If the null_if_invalid argument is set to true and the JSON string is invalid, the function returns NULL instead of returning an error.
+[JSON_EXTRACT_ARRAY_ELEMENT_TEXT]
+declaration=json_string,pos,null_if_invalid
+category=JSON Functions
+description=JSON_EXTRACT_ARRAY_ELEMENT_TEXT returns a JSON array element in the outermost array of a JSON string, using a zero-based index. The first element in an array is at position 0. If the index is negative or out of bound, JSON_EXTRACT_ARRAY_ELEMENT_TEXT returns empty string. If the null_if_invalid argument is set to true and the JSON string is invalid, the function returns NULL instead of returning an error.
+[JSON_EXTRACT_PATH_TEXT]
+declaration=json_string,path_elem,null_if_invalid
+category=JSON Functions
+description=JSON_EXTRACT_PATH_TEXT returns the value for the key:value pair referenced by a series of path elements in a JSON string. The JSON path can be nested up to five levels deep. Path elements are case-sensitive. If a path element does not exist in the JSON string, JSON_EXTRACT_PATH_TEXT returns an empty string. If the null_if_invalid argument is set to true and the JSON string is invalid, the function returns NULL instead of returning an error.
+[LAG]
+declaration=value_expr,offset,window_partition,window_ordering
+category=Window Functions
+description=The LAG window function returns the values for a row at a given offset above (before) the current row in the partition.
+[LAST_DAY]
+declaration=
+category=Date and Time Functions
+description=LAST_DAY returns the date of the last day of the month that contains date. The return type is always DATE, regardless of the data type of the date argument.
+[LAST_VALUE]
+declaration=expression,expr_list,order_list,frame_clause
+category=Window Functions
+description=Given an ordered set of rows, FIRST_VALUE returns the value of the specified expression with respect to the first row in the window frame. The LAST_VALUE function returns the value of the expression with respect to the last row in the frame.
+[LEAD]
+declaration=value_expr,offset,window_partition,window_ordering
+category=Window Functions
+description=The LEAD window function returns the values for a row at a given offset below (after) the current row in the partition.
+[LEAST]
+declaration=expression_list
+category=Conditional Expressions
+description=Returns the smallest value from a list of any number of expressions.
+[LEFT]
+declaration=string,integer
+category=String Functions
+description=These functions return the specified number of leftmost or rightmost characters from a character string.
+[LENGTH]
+declaration=
+category=String Functions
+description=Synonym of the LEN function.
+[LEN]
+declaration=expression
+category=String Functions
+description=Returns the length of the specified string as the number of characters.
+[LISTAGG]
+declaration=aggregate_expression,delimiter,WITHIN GROUP (ORDER BY order_list)
+category=Aggregate Functions
+description=For each group in a query, the LISTAGG aggregate function orders the rows for that group according to the ORDER BY expression, then concatenates the values into a single string.
+[LN]
+declaration=expression
+category=Math Functions
+description=Returns the natural logarithm of the input parameter. Synonym of the DLOG1 function.
+[LOG]
+declaration=number
+category=Math Functions
+description=Returns the base 10 logarithm of a number.
+[LOWER]
+declaration=string
+category=String Functions
+description=Converts a string to lowercase. LOWER supports UTF-8 multibyte characters, up to a maximum of four bytes per character.
+[LPAD]
+declaration=string1,length,string2
+category=String Functions
+description=These functions prepend or append characters to a string, based on a specified length.
+[LTRIM]
+declaration=string,trim_chars
+category=String Functions
+description=The LTRIM function trims a specified set of characters from the beginning of a string.
+[MAX]
+declaration=expression
+category=Aggregate Functions
+description=The MAX function returns the maximum value in a set of rows. DISTINCT or ALL may be used but do not affect the result.
+[MD5]
+declaration=string
+category=String Functions
+description=Uses the MD5 cryptographic hash function to convert a variable-length string into a 32-character string that is a text representation of the hexadecimal value of a 128-bit checksum.
+[MEDIAN]
+declaration=median_expression
+category=Aggregate Functions
+description=Calculates the median value for the range of values. NULL values in the range are ignored.
+[MIN]
+declaration=expression
+category=Aggregate Functions
+description=The MIN function returns the minimum value in a set of rows. DISTINCT or ALL may be used but do not affect the result.
+[MOD]
+declaration=number1,number2
+category=Math Functions
+description=The MOD function returns a numeric result that is the remainder of two numeric parameters. The first parameter is divided by the second parameter.
+[MONTHS_BETWEEN]
+declaration=date1,date2
+category=Date and Time Functions
+description=MONTHS_BETWEEN determines the number of months between two dates.
+[NEXT_DAY]
+declaration=date,timestamp,day
+category=Date and Time Functions
+description=NEXT_DAY returns the date of the first instance of the specified day that is later than the given date.
+[NTH_VALUE]
+declaration=expr,offset,window_partition,window_ordering,frame_clause
+category=Window Functions
+description=The NTH_VALUE window function returns the expression value of the specified row of the window frame relative to the first row of the window.
+[NTILE]
+declaration=expr,window_partition,window_ordering
+category=Window Functions
+description=The NTILE window function divides ordered rows in the partition into the specified number of ranked groups of as equal size as possible and returns the group that a given row falls into.
+[NULLIF]
+declaration=expression1, expression2
+category=Conditional Expressions
+description=The NULLIF expression compares two arguments and returns null if the arguments are equal.
+[NVL2]
+declaration=expression,not_null_return_value,null_return_value
+category=Conditional Expressions
+description=Returns one of two values based on whether a specified expression evaluates to NULL or NOT NULL.
+[NVL]
+declaration=
+category=Conditional Expressions
+description=An NVL expression is identical to a COALESCE expression. NVL and COALESCE are synonyms.
+[OCTET_LENGTH]
+declaration=expression
+category=String Functions
+description=Returns the length of the specified string as the number of bytes.
+[PERCENTILE_CONT]
+declaration=percentile,expr
+category=Aggregate Functions
+description=PERCENTILE_CONT is an inverse distribution function that assumes a continuous distribution model. It takes a percentile value and a sort specification, and returns an interpolated value that would fall into the given percentile value with respect to the sort specification.
+[PERCENTILE_DISC]
+declaration=percentile,expr
+category=Aggregate Functions
+description=APPROXIMATE PERCENTILE_DISC is an inverse distribution function that assumes a discrete distribution model. It takes a percentile value and a sort specification and returns an element from the given set. Approximation enables the function to execute much faster, with a low relative error of around 0.5 percent.
+[PERCENT_RANK]
+declaration=partition_expression,order_list
+category=Window Functions
+description=Calculates the percent rank of a given row. The percent rank is determined using this formula:
+[PG_BACKEND_PID]
+declaration=
+category=System Information Functions
+description=Returns the process ID (PID) of the server process handling the current session.
+[PG_CANCEL_BACKEND]
+declaration=pid
+category=System Administration Functions
+description=Cancels a query. PG_CANCEL_BACKEND is functionally equivalent to the CANCEL command. You can cancel queries currently being run by your user. Superusers can cancel any query.
+[PG_GET_COLS]
+declaration=name
+category=System Information Functions
+description=Returns the column metadata for a table or view definition.
+[PG_GET_LATE_BINDING_VIEW_COLS]
+declaration=
+category=System Information Functions
+description=Returns the column metadata for all late-binding views in the database. For more information, see Late-Binding Views
+[PG_LAST_COPY_COUNT]
+declaration=
+category=System Information Functions
+description=Returns the number of rows that were loaded by the last COPY command executed in the current session. PG_LAST_COPY_COUNT is updated with the last COPY ID, which is the query ID of the last COPY that began the load process, even if the load failed. The query ID and COPY ID are updated when the COPY command begins the load process.
+[PG_LAST_COPY_ID]
+declaration=
+category=System Information Functions
+description=Returns the query ID of the most recently executed COPY command in the current session. If no COPY commands have been executed in the current session, PG_LAST_COPY_ID returns -1.
+[PG_LAST_QUERY_ID]
+declaration=
+category=System Information Functions
+description=Returns the query ID of the most recently executed query in the current session. If no queries have been executed in the current session, PG_LAST_QUERY_ID returns -1. PG_LAST_QUERY_ID does not return the query ID for queries that execute exclusively on the leader node. For more information, see Leader Node–Only Functions.
+[PG_LAST_UNLOAD_COUNT]
+declaration=
+category=System Information Functions
+description=Returns the number of rows that were unloaded by the last UNLOAD command executed in the current session. PG_LAST_UNLOAD_COUNT is updated with the query ID of the last UNLOAD, even if the operation failed. The query ID is updated when the UNLOAD is executed. If the UNLOAD fails because of a syntax error or because of insufficient privileges, PG_LAST_UNLOAD_COUNT returns the count for the previous UNLOAD. If no UNLOAD commands were executed in the current session, or if the last UNLOAD failed during the unload operation, PG_LAST_UNLOAD_COUNT returns 0.
+[PG_LAST_UNLOAD_ID]
+declaration=
+category=System Information Functions
+description=Returns the query ID of the most recently executed UNLOAD command in the current session. If no UNLOAD commands have been executed in the current session, PG_LAST_UNLOAD_ID returns -1.
+[PG_TERMINATE_BACKEND]
+declaration=pid
+category=System Administration Functions
+description=Terminates a session. You can terminate a session owned by your user. A superuser can terminate any session.
+[PI]
+declaration=
+category=Math Functions
+description=The PI function returns the value of PI to 14 decimal places.
+[POSITION]
+declaration=substring,string
+category=String Functions
+description=Returns the location of the specified substring within a string.
+[POWER]
+declaration=expression1,expression2
+category=Math Functions
+description=The POWER function is an exponential function that raises a numeric expression to the power of a second numeric expression.
+[QUOTE_IDENT]
+declaration=string
+category=String Functions
+description=The QUOTE_IDENT function returns the specified string as a double quoted string so that it can be used as an identifier in a SQL statement. Appropriately doubles any embedded double quotes.
+[QUOTE_LITERAL]
+declaration=string
+category=String Functions
+description=The QUOTE_LITERAL function returns the specified string as a quoted string so that it can be used as a string literal in a SQL statement. If the input parameter is a number, QUOTE_LITERAL treats it as a string. Appropriately doubles any embedded single quotes and backslashes.
+[RADIANS]
+declaration=string
+category=Math Functions
+description=Converts an angle in degrees to its equivalent in radians.
+[RANDOM]
+declaration=
+category=Math Functions
+description=The RANDOM function generates a random value between 0.0 and 1.0.
+[RANK]
+declaration=expr_list,order_list
+category=Window Functions
+description=The RANK window function determines the rank of a value in a group of values, based on the ORDER BY expression in the OVER clause. If the optional PARTITION BY clause is present, the rankings are reset for each group of rows. Rows with equal values for the ranking criteria receive the same rank. Amazon Redshift adds the number of tied rows to the tied rank to calculate the next rank and thus the ranks might not be consecutive numbers. For example, if two rows are ranked 1, the next rank is 3.
+[RATIO_TO_REPORT]
+declaration=ratio_expression,partition_expression
+category=Window Functions
+description=Calculates the ratio of a value to the sum of the values in a window or partition. The ratio to report value is determined using the formula:
+[REGEXP_COUNT]
+declaration=source_string,pattern,position
+category=String Functions
+description=Searches a string for a regular expression pattern and returns an integer that indicates the number of times the pattern occurs in the string. If no match is found, then the function returns 0. For more information about regular expressions, see POSIX Operators.
+[REGEXP_INSTR]
+declaration=source_string,pattern,position,occurrence,option,parameters
+category=String Functions
+description=Searches a string for a regular expression pattern and returns an integer that indicates the beginning position or ending position of the matched substring. If no match is found, then the function returns 0. REGEXP_INSTR is similar to the POSITION function, but lets you search a string for a regular expression pattern. For more information about regular expressions, see POSIX Operators.
+[REGEXP_REPLACE]
+declaration=source_string,pattern,replace_string,position
+category=String Functions
+description=Searches a string for a regular expression pattern and replaces every occurrence of the pattern with the specified string. REGEXP_REPLACE is similar to the REPLACE Function, but lets you search a string for a regular expression pattern. For more information about regular expressions, see POSIX Operators.
+[REGEXP_SUBSTR]
+declaration=source_string,pattern,position,occurrence,parameters
+category=String Functions
+description=Returns the characters extracted from a string by searching for a regular expression pattern. REGEXP_SUBSTR is similar to the SUBSTRING Function function, but lets you search a string for a regular expression pattern. For more information about regular expressions, see POSIX Operators.
+[REPEAT]
+declaration=string,integer
+category=String Functions
+description=Repeats a string the specified number of times. If the input parameter is numeric, REPEAT treats it as a string.
+[REPLACE]
+declaration=string,old_chars,new_chars,old_string
+category=String Functions
+description=Replaces all occurrences of a set of characters within an existing string with other specified characters.
+[REPLICATE]
+declaration=
+category=String Functions
+description=Synonym for the REPEAT function.
+[REVERSE]
+declaration=expression
+category=String Functions
+description=The REVERSE function operates on a string and returns the characters in reverse order. For example, reverse('abcde') returns edcba. This function works on numeric and date data types as well as character data types; however, in most cases it has practical value for character strings.
+[RIGHT]
+declaration=string,integer
+category=String Functions
+description=These functions return the specified number of leftmost or rightmost characters from a character string.
+[ROUND]
+declaration=number
+category=Math Functions
+description=The ROUND function rounds numbers to the nearest integer or decimal.
+[ROW_NUMBER]
+declaration=expr_list,order_list
+category=Window Functions
+description=Determines the ordinal number of the current row within a group of rows, counting from 1, based on the ORDER BY expression in the OVER clause. If the optional PARTITION BY clause is present, the ordinal numbers are reset for each group of rows. Rows with equal values for the ORDER BY expressions receive the different row numbers nondeterministically.
+[RPAD]
+declaration=string1,length,string2
+category=String Functions
+description=These functions prepend or append characters to a string, based on a specified length.
+[RTRIM]
+declaration=string,trim_chars
+category=String Functions
+description=The RTRIM function trims a specified set of characters from the end of a string.
+[SESSION_USER]
+declaration=
+category=System Information Functions
+description=Returns the name of the user associated with the current session. This is the user who initiated the current database connection.
+[SET_CONFIG]
+declaration=parameter,new_value,is_local
+category=System Administration Functions
+description=Sets a configuration parameter to a new setting.
+[SIGN]
+declaration=numeric
+category=Math Functions
+description=The SIGN function returns the sign (positive or negative) of a numeric value. The result of the SIGN function will be a 1, -1, or 0 indicating the sign of the argument.
+[SIN]
+declaration=number
+category=Math Functions
+description=SIN is a trigonometric function that returns the sine of a number. The return value is in radians and is between PI/2 and -PI/2.
+[SLICE_NUM]
+declaration=
+category=System Information Functions
+description=Returns an integer corresponding to the slice number in the cluster where the data for a row is located. SLICE_NUM takes no parameters.
+[SPLIT_PART]
+declaration=string,delimiter,part
+category=String Functions
+description=Splits a string on the specified delimiter and returns the part at the specified position.
+[SQRT]
+declaration=expression
+category=Math Functions
+description=The SQRT function returns the square root of a numeric value.
+[STDDEV_POP]
+declaration=
+category=Aggregate Functions
+description=The STDDEV_SAMP and STDDEV_POP functions return the sample and population standard deviation of a set of numeric values (integer, decimal, or floating-point). The result of the STDDEV_SAMP function is equivalent to the square root of the sample variance of the same set of values.
+[STDDEV_SAMP]
+declaration=
+category=Aggregate Functions
+description=The STDDEV_SAMP and STDDEV_POP functions return the sample and population standard deviation of a set of numeric values (integer, decimal, or floating-point). The result of the STDDEV_SAMP function is equivalent to the square root of the sample variance of the same set of values.
+[STRPOS]
+declaration=string,substring
+category=String Functions
+description=Returns the position of a substring within a specified string.
+[STRTOL]
+declaration=num_string,base
+category=String Functions
+description=Converts a string expression of a number of the specified base to the equivalent integer value. The converted value must be within the signed 64-bit range.
+[SUBSTRING]
+declaration=string,start_position,number_characters
+category=String Functions
+description=Returns the characters extracted from a string based on the specified character position for a specified number of characters.
+[SUM]
+declaration=expression
+category=Aggregate Functions
+description=The SUM function returns the sum of the input column or expression values. The SUM function works with numeric values and ignores NULL values.
+[SYSDATE]
+declaration=
+category=Date and Time Functions
+description=SYSDATE returns the current date and time in the current session time zone (UTC by default).
+[TAN]
+declaration=number
+category=Math Functions
+description=TAN is a trigonometric function that returns the tangent of a number. The input parameter must be a non-zero number (in radians).
+[TEXTLEN]
+declaration=
+category=String Functions
+description=Synonym of LEN function.
+[TIMEOFDAY]
+declaration=
+category=Date and Time Functions
+description=TIMEOFDAY is a special alias used to return the weekday, date, and time as a string value.
+[TIMESTAMPTZ_CMP]
+declaration=timestamptz1,timestamptz2
+category=Date and Time Functions
+description=TIMESTAMPTZ_CMP compares the value of two time stamp with time zone values and returns an integer. If the time stamps are identical, the function returns 0. If the first time stamp is greater alphabetically, the function returns 1. If the second time stamp is greater, the function returns –1.
+[TIMESTAMPTZ_CMP_DATE]
+declaration=timestamptz,date
+category=Date and Time Functions
+description=TIMESTAMPTZ_CMP_DATE compares the value of a time stamp and a date. If the time stamp and date values are identical, the function returns 0. If the time stamp is greater alphabetically, the function returns 1. If the date is greater, the function returns –1.
+[TIMESTAMPTZ_CMP_TIMESTAMP]
+declaration=timestamptz,timestamp
+category=Date and Time Functions
+description=TIMESTAMPTZ_CMP_TIMESTAMP compares the value of a time stamp with time zone expression with a time stamp expression. If the time stamp with time zone and time stamp values are identical, the function returns 0. If the time stamp with time zone is greater alphabetically, the function returns 1. If the time stamp is greater, the function returns –1.
+[TIMESTAMP_CMP]
+declaration=timestamp1,timestamp2
+category=Date and Time Functions
+description=Compares the value of two time stamps and returns an integer. If the time stamps are identical, the function returns 0. If the first time stamp is greater alphabetically, the function returns 1. If the second time stamp is greater, the function returns –1.
+[TIMESTAMP_CMP_DATE]
+declaration=timestamp,date
+category=Date and Time Functions
+description=TIMESTAMP_CMP_DATE compares the value of a time stamp and a date. If the time stamp and date values are identical, the function returns 0. If the time stamp is greater alphabetically, the function returns 1. If the date is greater, the function returns –1.
+[TIMESTAMP_CMP_TIMESTAMPTZ]
+declaration=timestamp,timestamptz
+category=Date and Time Functions
+description=TIMESTAMP_CMP_TIMESTAMPTZ compares the value of a time stamp expression with a time stamp with time zone expression. If the time stamp and time stamp with time zone values are identical, the function returns 0. If the time stamp is greater alphabetically, the function returns 1. If the time stamp with time zone is greater, the function returns –1.
+[TIMEZONE]
+declaration=timezone,timestamp,timestamptz
+category=Date and Time Functions
+description=TIMEZONE returns a time stamp for the specified time zone and time stamp value.
+[TO_CHAR]
+declaration=timestamp_expression,numeric_expression,format
+category=Data Type Formatting Functions
+description=TO_CHAR converts a time stamp or numeric expression to a character-string data format.
+[TO_DATE]
+declaration=string,format
+category=Data Type Formatting Functions
+description=TO_DATE converts a date represented in a character string to a DATE data type.
+[TO_HEX]
+declaration=number
+category=Math Functions
+description=The TO_HEX function converts a number to its equivalent hexadecimal value.
+[TO_NUMBER]
+declaration=string,format
+category=Data Type Formatting Functions
+description=TO_NUMBER converts a string to a numeric (decimal) value.
+[TO_TIMESTAMP]
+declaration=timestamp,format
+category=Date and Time Functions
+description=TO_TIMESTAMP converts a TIMESTAMP string to TIMESTAMPTZ.
+[TRANSLATE]
+declaration=expression,characters_to_replace,characters_to_substitute
+category=String Functions
+description=For a given expression, replaces all occurrences of specified characters with specified substitutes. Existing characters are mapped to replacement characters by their positions in the characters_to_replace and characters_to_substitute arguments. If more characters are specified in the characters_to_replace argument than in the characters_to_substitute argument, the extra characters from the characters_to_replace argument are omitted in the return value.
+[TRIM]
+declaration=characters,string
+category=String Functions
+description=The TRIM function trims a string by removing leading and trailing blanks or by removing characters that match an optional specified string.
+[TRUNC]
+declaration=number,integer,timestamp
+category=Math Functions
+description=The TRUNC function truncates a number and right-fills it with zeros from the position specified. This function also truncates a time stamp and returns a date.
+[TRUNC]
+declaration=timestamp
+category=Date and Time Functions
+description=Truncates a time stamp and returns a date.
+[UPPER]
+declaration=string
+category=String Functions
+description=Converts a string to uppercase. UPPER supports UTF-8 multibyte characters, up to a maximum of four bytes per character.
+[USER]
+declaration=
+category=System Information Functions
+description=Synonym for CURRENT_USER. See CURRENT_USER.
+[VAR_POP]
+declaration=
+category=Aggregate Functions
+description=The VAR_SAMP and VAR_POP functions return the sample and population variance of a set of numeric values (integer, decimal, or floating-point). The result of the VAR_SAMP function is equivalent to the squared sample standard deviation of the same set of values.
+[VAR_SAMP]
+declaration=
+category=Aggregate Functions
+description=The VAR_SAMP and VAR_POP functions return the sample and population variance of a set of numeric values (integer, decimal, or floating-point). The result of the VAR_SAMP function is equivalent to the squared sample standard deviation of the same set of values.
+[VERSION]
+declaration=
+category=System Information Functions
+description=The VERSION() function returns details about the currently installed release, with specific Amazon Redshift version information at the end.
\ No newline at end of file
diff --git a/out/functions-sqlite.ini b/out/functions-sqlite.ini
new file mode 100644
index 00000000..d827e453
Binary files /dev/null and b/out/functions-sqlite.ini differ
diff --git a/out/libcrypto-1_1-x64.dll b/out/libcrypto-1_1-x64.dll
new file mode 100644
index 00000000..bdd30f72
Binary files /dev/null and b/out/libcrypto-1_1-x64.dll differ
diff --git a/out/libcrypto-3-x64.dll b/out/libcrypto-3-x64.dll
new file mode 100644
index 00000000..26e1997c
Binary files /dev/null and b/out/libcrypto-3-x64.dll differ
diff --git a/out/libiconv-2.dll b/out/libiconv-2.dll
new file mode 100644
index 00000000..aa33b642
Binary files /dev/null and b/out/libiconv-2.dll differ
diff --git a/out/libintl-9.dll b/out/libintl-9.dll
new file mode 100644
index 00000000..5582953e
Binary files /dev/null and b/out/libintl-9.dll differ
diff --git a/out/libmysql-6.1.dll b/out/libmysql-6.1.dll
new file mode 100644
index 00000000..1f9c9dab
Binary files /dev/null and b/out/libmysql-6.1.dll differ
diff --git a/out/libmysql-8.4.0.dll b/out/libmysql-8.4.0.dll
new file mode 100644
index 00000000..d9a2c297
Binary files /dev/null and b/out/libmysql-8.4.0.dll differ
diff --git a/out/libmysql.dll b/out/libmysql.dll
new file mode 100644
index 00000000..f22dd273
Binary files /dev/null and b/out/libmysql.dll differ
diff --git a/out/libpq-10.dll b/out/libpq-10.dll
new file mode 100644
index 00000000..cc0bed2b
Binary files /dev/null and b/out/libpq-10.dll differ
diff --git a/out/libpq-12.dll b/out/libpq-12.dll
new file mode 100644
index 00000000..14703e5f
Binary files /dev/null and b/out/libpq-12.dll differ
diff --git a/out/libpq-15.dll b/out/libpq-15.dll
new file mode 100644
index 00000000..c9dd40c7
Binary files /dev/null and b/out/libpq-15.dll differ
diff --git a/out/libpq-17.dll b/out/libpq-17.dll
new file mode 100644
index 00000000..ecde37d0
Binary files /dev/null and b/out/libpq-17.dll differ
diff --git a/out/libssl-1_1-x64.dll b/out/libssl-1_1-x64.dll
new file mode 100644
index 00000000..5c086777
Binary files /dev/null and b/out/libssl-1_1-x64.dll differ
diff --git a/out/libssl-3-x64.dll b/out/libssl-3-x64.dll
new file mode 100644
index 00000000..b20119ed
Binary files /dev/null and b/out/libssl-3-x64.dll differ
diff --git a/out/libwinpthread-1.dll b/out/libwinpthread-1.dll
new file mode 100644
index 00000000..2d5ecc2e
Binary files /dev/null and b/out/libwinpthread-1.dll differ
diff --git a/out/sqlite3.dll b/out/sqlite3.dll
new file mode 100644
index 00000000..f9212a6e
Binary files /dev/null and b/out/sqlite3.dll differ
diff --git a/out/sqlite3mc.dll b/out/sqlite3mc.dll
new file mode 100644
index 00000000..a2007faf
Binary files /dev/null and b/out/sqlite3mc.dll differ
diff --git a/source/about.pas b/source/about.pas
index 00aaebd9..373db5ed 100644
--- a/source/about.pas
+++ b/source/about.pas
@@ -132,22 +132,11 @@ begin
lnklblCredits.Font.Style := lnklblCredits.Font.Style + [fsUnderline];
ImageHeidisql.Hint := APPDOMAIN+'?place='+EncodeURLParam(ImageHeidisql.Name);
- lblEnvironment.Caption := _('Environment:');
- if IsWine then begin
- lblEnvironment.Caption := lblEnvironment.Caption +
- ' Linux/Wine';
- end else begin
- OsMajor := Win32MajorVersion;
- OsMinor := Win32MinorVersion;
- OsBuild := Win32BuildNumber;
- if (OsMajor = 10) and (OsBuild >= 22000) then
- OsMajor := 11;
- lblEnvironment.Caption := lblEnvironment.Caption +
- ' Windows ' +
- IntToStr(OsMajor) +
- IfThen(OsMinor > 0, '.'+IntToStr(OsMinor), '') +
- ' Build '+IntToStr(OsBuild);
- end;
+ lblEnvironment.Caption := _('Environment:') +
+ {$IFDEF WINDOWS}'Windows'{$EndIf}
+ {$IFDEF LINUX}'Linux'{$EndIf}
+ {$IFDEF MACOS}'MacOS'{$EndIf}
+ ;
Screen.Cursor := crDefault;
btnClose.TrySetFocus;
diff --git a/source/apphelpers.pas b/source/apphelpers.pas
index ecd90860..c49d3124 100644
--- a/source/apphelpers.pas
+++ b/source/apphelpers.pas
@@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, Generics.Collections, Generics.Defaults, Controls, RegExpr, Math, FileUtil,
StrUtils, Graphics, GraphUtil, LCLIntf, Forms, Clipbrd, Process, ActnList, Menus, Dialogs,
- Character,
+ Character, DateUtils,
dbconnection, dbstructures;
type
@@ -245,13 +245,13 @@ type
DefaultString, CurrentString: String;
Synced: Boolean;
end;
- {TAppSettings = class(TObject)
+ TAppSettings = class(TObject)
private
FReads, FWrites: Integer;
FBasePath: String;
FSessionPath: String;
FStoredPath: String;
- FRegistry: TRegistry;
+ //FRegistry: TRegistry;
FPortableMode: Boolean;
FPortableModeReadOnly: Boolean;
FRestoreTabsInitValue: Boolean;
@@ -314,7 +314,7 @@ type
function DirnameHighlighters: String;
// "Static" options, initialized in OnCreate only. For settings which need a restart to take effect.
property RestoreTabsInitValue: Boolean read FRestoreTabsInitValue;
- end;}
+ end;
{$I const.inc}
@@ -355,11 +355,11 @@ type
function FormatByteNumber( Bytes: String; Decimals: Byte = 1 ): String; Overload;
function FormatTimeNumber(Seconds: Double; DisplaySeconds: Boolean; MilliSecondsPrecision: Integer=1): String;
//function GetTempDir: String;
- //procedure SaveUnicodeFile(Filename: String; Text: String; Encoding: TEncoding);
- //procedure OpenTextFile(const Filename: String; out Stream: TFileStream; var Encoding: TEncoding);
+ procedure SaveUnicodeFile(Filename: String; Text: String; Encoding: TEncoding);
+ procedure OpenTextFile(const Filename: String; out Stream: TFileStream; var Encoding: TEncoding);
//function DetectEncoding(Stream: TStream): TEncoding;
- //function ReadTextfileChunk(Stream: TFileStream; Encoding: TEncoding; ChunkSize: Int64 = 0): String;
- //function ReadTextfile(Filename: String; Encoding: TEncoding): String;
+ function ReadTextfileChunk(Stream: TFileStream; Encoding: TEncoding; ChunkSize: Int64 = 0): String;
+ function ReadTextfile(Filename: String; Encoding: TEncoding): String;
//function ReadBinaryFile(Filename: String; MaxBytes: Int64): AnsiString;
//procedure StreamToClipboard(Text, HTML: TStream);
function WideHexToBin(text: String): AnsiString;
@@ -384,14 +384,14 @@ type
//function CheckForSecondInstance: Boolean;
function GetParentFormOrFrame(Comp: TWinControl): TWinControl;
//function KeyPressed(Code: Integer): Boolean;
- //function GeneratePassword(Len: Integer): String;
+ function GeneratePassword(Len: Integer): String;
//procedure InvalidateVT(VT: TVirtualStringTree; RefreshTag: Integer; ImmediateRepaint: Boolean);
- //function CharAtPos(Str: String; Pos: Integer): Char;
- //function CompareAnyNode(Text1, Text2: String): Integer;
+ function CharAtPos(Str: String; Pos: Integer): Char;
+ function CompareAnyNode(Text1, Text2: String): Integer;
//function StringListCompareAnythingAsc(List: TStringList; Index1, Index2: Integer): Integer;
//function StringListCompareAnythingDesc(List: TStringList; Index1, Index2: Integer): Integer;
//function StringListCompareByValue(List: TStringList; Index1, Index2: Integer): Integer;
- //function StringListCompareByLength(List: TStringList; Index1, Index2: Integer): Integer;
+ function StringListCompareByLength(List: TStringList; Index1, Index2: Integer): Integer;
//function GetImageLinkTimeStamp(const FileName: string): TDateTime;
function IsEmpty(Str: String): Boolean;
function IsNotEmpty(Str: String): Boolean;
@@ -401,21 +401,21 @@ type
function ErrorDialog(const Title, Msg: string): Integer; overload;
//function GetLocaleString(const ResourceId: Integer): WideString;
//function GetHTMLCharsetByEncoding(Encoding: TEncoding): String;
- //procedure ParseCommandLine(CommandLine: String; var ConnectionParams: TConnectionParameters; var FileNames: TStringList; var RunFrom: String);
+ procedure ParseCommandLine(CommandLine: String; var ConnectionParams: TConnectionParameters; var FileNames: TStringList; var RunFrom: String);
function _(const Pattern: string): string;
function f_(const Pattern: string; const Args: array of const): string;
- //function GetOutputFilename(FilenameWithPlaceholders: String; DBObj: TDBObject): String;
- //function GetOutputFilenamePlaceholders: TStringList;
+ function GetOutputFilename(FilenameWithPlaceholders: String; DBObj: TDBObject): String;
+ function GetOutputFilenamePlaceholders: TStringList;
//function GetSystemImageList: TImageList;
//function GetSystemImageIndex(Filename: String): Integer;
- //function GetExecutableBits: Byte;
+ function GetExecutableBits: Byte;
procedure Help(Sender: TObject; Anchor: String);
//function PortOpen(Port: Word): Boolean;
//function IsValidFilePath(FilePath: String): Boolean;
//function FileIsWritable(FilePath: String): Boolean;
//function GetProductInfo(dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion, dwSpMinorVersion: DWORD; out pdwReturnedProductType: DWORD): BOOL stdcall; external kernel32 delayed;
//function GetCurrentPackageFullName(out Len: Cardinal; Name: PWideChar): Integer; stdcall; external kernel32 delayed;
- //function GetThemeColor(Color: TColor): TColor;
+ function GetThemeColor(Color: TColor): TColor;
//function ThemeIsDark(ThemeName: String=''): Boolean;
//function ProcessExists(pid: Cardinal; ExeNamePattern: String): Boolean;
//procedure ToggleCheckBoxWithoutClick(chk: TCheckBox; State: Boolean);
@@ -426,11 +426,14 @@ type
//procedure FindComponentInstances(BaseForm: TComponent; ClassType: TClass; var List: TObjectList);
//function WebColorStrToColorDef(WebColor: string; Default: TColor): TColor;
function UserAgent(OwnerComponent: TComponent): String;
- //function CodeIndent(Steps: Integer=1): String;
+ function CodeIndent(Steps: Integer=1): String;
//function EscapeHotkeyPrefix(Text: String): String;
+ function GetFileNameWithoutExtension(Filename: String): String;
+ function GetCommandLine: String;
+ function GetDynLibExtension: String;
var
- //AppSettings: TAppSettings;
+ AppSettings: TAppSettings;
MutexHandle: THandle = 0;
SystemImageList: TImageList = nil;
mtCriticalConfirmation: TMsgDlgType = mtCustom;
@@ -1013,7 +1016,6 @@ begin
end;
-
{***
Returns first word of a given text
@param string Given text
@@ -1203,25 +1205,25 @@ end;}
{**
Save a textfile with unicode
}
-{procedure SaveUnicodeFile(Filename: String; Text: String; Encoding: TEncoding);
+procedure SaveUnicodeFile(Filename: String; Text: String; Encoding: TEncoding);
var
- Writer: TStreamWriter;
+ Writer: TStringList;
begin
- Writer := TStreamWriter.Create(Filename, False, Encoding);
- Writer.Write(Text);
- Writer.Free;
-end;}
+ Writer := TStringList.Create;
+ Writer.Text := Text;
+ Writer.SaveToFile(Filename, Encoding);
+end;
-{procedure OpenTextFile(const Filename: String; out Stream: TFileStream; var Encoding: TEncoding);
+procedure OpenTextFile(const Filename: String; out Stream: TFileStream; var Encoding: TEncoding);
var
Header: TBytes;
BomLen: Integer;
begin
// Open a textfile and return a stream. Detect its encoding if not passed by the caller
Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyNone);
- if Encoding = nil then
- Encoding := DetectEncoding(Stream);
+ //if Encoding = nil then
+ // Encoding := DetectEncoding(Stream);
// If the file contains a BOM, advance the stream's position
BomLen := 0;
if Length(Encoding.GetPreamble) > 0 then begin
@@ -1231,7 +1233,7 @@ begin
BomLen := Length(Encoding.GetPreamble);
end;
Stream.Position := BomLen;
-end;}
+end;
{**
@@ -1265,7 +1267,7 @@ begin
end;}
-{function ReadTextfileChunk(Stream: TFileStream; Encoding: TEncoding; ChunkSize: Int64 = 0): String;
+function ReadTextfileChunk(Stream: TFileStream; Encoding: TEncoding; ChunkSize: Int64 = 0): String;
const
BufferPadding = 1;
var
@@ -1304,10 +1306,10 @@ begin
end;
Result := TEncoding.Unicode.GetString(LBuffer);
-end;}
+end;
-{function ReadTextfile(Filename: String; Encoding: TEncoding): String;
+function ReadTextfile(Filename: String; Encoding: TEncoding): String;
var
Stream: TFileStream;
begin
@@ -1315,7 +1317,7 @@ begin
OpenTextfile(Filename, Stream, Encoding);
Result := ReadTextfileChunk(Stream, Encoding);
Stream.Free;
-end;}
+end;
{function ReadBinaryFile(Filename: String; MaxBytes: Int64): AnsiString;
@@ -2085,7 +2087,7 @@ begin
end;}
-{function GeneratePassword(Len: Integer): String;
+function GeneratePassword(Len: Integer): String;
var
i: Integer;
CharTable: String;
@@ -2105,7 +2107,7 @@ begin
CharTable := Consos;
Result[i] := CharTable[Random(Length(CharTable)-1)+1];
end;
-end;}
+end;
{procedure InvalidateVT(VT: TVirtualStringTree; RefreshTag: Integer; ImmediateRepaint: Boolean);
@@ -2121,17 +2123,17 @@ begin
end;}
-{function CharAtPos(Str: String; Pos: Integer): Char;
+function CharAtPos(Str: String; Pos: Integer): Char;
begin
// Access char in string without causing access violation
if Length(Str) < Pos then
Result := #0
else
Result := Str[Pos];
-end;}
+end;
-{function CompareAnyNode(Text1, Text2: String): Integer;
+function CompareAnyNode(Text1, Text2: String): Integer;
var
Number1, Number2 : Extended;
a1, a2, b1, b2: Char;
@@ -2162,7 +2164,7 @@ begin
// Compare Strings
Result := CompareText(Text1, Text2);
end;
-end;}
+end;
{function StringListCompareAnythingAsc(List: TStringList; Index1, Index2: Integer): Integer;
@@ -2186,11 +2188,11 @@ begin
end;}
-{function StringListCompareByLength(List: TStringList; Index1, Index2: Integer): Integer;
+function StringListCompareByLength(List: TStringList; Index1, Index2: Integer): Integer;
begin
// Sort TStringList items by their length
Result := CompareValue(List[Index2].Length, List[Index1].Length);
-end;}
+end;
{**
@@ -2493,7 +2495,7 @@ begin
end;}
-{procedure ParseCommandLine(CommandLine: String; var ConnectionParams: TConnectionParameters; var FileNames: TStringList; var RunFrom: String);
+procedure ParseCommandLine(CommandLine: String; var ConnectionParams: TConnectionParameters; var FileNames: TStringList; var RunFrom: String);
var
rx: TRegExpr;
ExeName, SessName, Host, Lib, Port, User, Pass, Socket, AllDatabases,
@@ -2505,11 +2507,11 @@ var
begin
// Return one command line switch. Doublequotes are not mandatory.
Result := '';
- rx.Expression := '\s(\-'+ShortName+'|\-\-'+LongName+')\s*\=?\s*\"([^\-][^\"]*)\"';
+ rx.Expression := '\s(\-'+ShortName+'|\-\-'+LongName+')[\s\=]\"([^\"]+)\"';
if rx.Exec(CommandLine) then
Result := rx.Match[2]
else begin
- rx.Expression := '\s(\-'+ShortName+'|\-\-'+LongName+')\s*\=?\s*([^\-]\S*)';
+ rx.Expression := '\s(\-'+ShortName+'|\-\-'+LongName+')[\s\=](\S+)';
if rx.Exec(CommandLine) then
Result := rx.Match[2];
end;
@@ -2634,7 +2636,7 @@ begin
AbsentFiles.Free;
rx.Free;
-end;}
+end;
function _(const Pattern: string): string;
@@ -2661,7 +2663,7 @@ begin
end;
-{function GetOutputFilename(FilenameWithPlaceholders: String; DBObj: TDBObject): String;
+function GetOutputFilename(FilenameWithPlaceholders: String; DBObj: TDBObject): String;
var
Arguments: TExtStringList;
Year, Month, Day, Hour, Min, Sec, MSec: Word;
@@ -2690,10 +2692,10 @@ begin
Result := StringReplace(Result, '%'+Arguments.Names[i], Arguments.ValueFromIndex[i], [rfReplaceAll]);
end;
Arguments.Free;
-end;}
+end;
-{function GetOutputFilenamePlaceholders: TStringList;
+function GetOutputFilenamePlaceholders: TStringList;
begin
// Return a list with valid placeholder=>description pairs
Result := TStringList.Create;
@@ -2708,7 +2710,7 @@ begin
Result.Values['h'] := _('Hour');
Result.Values['i'] := _('Minute');
Result.Values['s'] := _('Second');
-end;}
+end;
{function GetSystemImageList: TImageList;
@@ -2740,14 +2742,10 @@ begin
end;}
-{function GetExecutableBits: Byte;
-begin}
- //{$IFDEF WIN64}
- //Result := 64;
- //{$ELSE}
- //Result := 32;
- //{$ENDIF}
-//end;
+function GetExecutableBits: Byte;
+begin
+ Result := 64;
+end;
procedure Help(Sender: TObject; Anchor: String);
@@ -2822,12 +2820,12 @@ begin
end;}
-{function GetThemeColor(Color: TColor): TColor;
+function GetThemeColor(Color: TColor): TColor;
begin
// Not required with vcl-style-utils:
// Result := TStyleManager.ActiveStyle.GetSystemColor(Color);
Result := Color;
-end;}
+end;
{function ThemeIsDark(ThemeName: String=''): Boolean;
@@ -2981,14 +2979,14 @@ begin
end;
-{function CodeIndent(Steps: Integer=1): String;
+function CodeIndent(Steps: Integer=1): String;
begin
// Provide tab or spaces for indentation, uniquely used for all SQL statements
if AppSettings.ReadBool(asTabsToSpaces) then
Result := StringOfChar(' ', AppSettings.ReadInt(asTabWidth) * Steps)
else
Result := StringOfChar(#9, Steps);
-end;}
+end;
{function EscapeHotkeyPrefix(Text: String): String;
@@ -2997,6 +2995,35 @@ begin
Result := StringReplace(Text, Vcl.Menus.cHotkeyPrefix, Vcl.Menus.cHotkeyPrefix + Vcl.Menus.cHotkeyPrefix, [rfReplaceAll]);
end;}
+function GetFileNameWithoutExtension(Filename: String): String;
+var
+ LastDotPos: Integer;
+begin
+ Result := ExtractFileName(Filename);
+ LastDotPos := Result.LastIndexOf('.');
+ if LastDotPos > -1 then
+ Result := Result.Substring(0, LastDotPos+1);
+end;
+
+function GetCommandLine: String;
+var
+ i: Integer;
+begin
+ Result := '';
+ for i:=0 to ParamCount do begin
+ Result := Result + ParamStr(i) + ' ';
+ end;
+ Result := Trim(Result);
+end;
+
+function GetDynLibExtension: String;
+begin
+ Result :=
+ {$IFDEF WINDOWS}'dll'{$EndIf}
+ {$IFDEF LINUX}'so'{$EndIf}
+ {$IFDEF MACOS}'dylib'{$EndIf}
+ ;
+end;
{ Get SID of current Windows user, probably useful in the future
{function GetCurrentUserSID: string;
@@ -3580,7 +3607,7 @@ end;
{ TAppSettings }
-{constructor TAppSettings.Create;
+constructor TAppSettings.Create;
var
rx: TRegExpr;
i: Integer;
@@ -3589,7 +3616,7 @@ var
NewFileHandle: THandle;
begin
inherited;
- FRegistry := TRegistry.Create;
+ //FRegistry := TRegistry.Create;
FReads := 0;
FWrites := 0;
@@ -3623,7 +3650,7 @@ begin
NewFileHandle := FileCreate(FSettingsFile);
FileClose(NewFileHandle);
end;
- FBasePath := '\Software\' + APPNAME + ' Portable '+IntToStr(GetCurrentProcessId)+'\';
+ FBasePath := '\Software\' + APPNAME + ' Portable '+IntToStr(123 {GetCurrentProcessId})+'\';
try
ImportSettings(FSettingsFile);
except
@@ -3958,14 +3985,14 @@ begin
// Initialization values
FRestoreTabsInitValue := ReadBool(asRestoreTabs);
-end;}
+end;
-{destructor TAppSettings.Destroy;
+destructor TAppSettings.Destroy;
var
AllKeys: TStringList;
i: Integer;
- Proc: TProcessEntry32;
+ //Proc: TProcessEntry32;
ProcRuns: Boolean;
SnapShot: THandle;
rx: TRegExpr;
@@ -3977,11 +4004,11 @@ begin
except
// do nothing, even ShowMessage or ErrorDialog would trigger timer events followed by crashes;
end;
- FRegistry.CloseKey;
- FRegistry.DeleteKey(FBasePath);
+ //FRegistry.CloseKey;
+ //FRegistry.DeleteKey(FBasePath);
// Remove dead keys from instances which didn't close clean, e.g. because of an AV
- SnapShot := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0);
+ {SnapShot := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0);
Proc.dwSize := Sizeof(Proc);
FRegistry.OpenKeyReadOnly('\Software\');
AllKeys := TStringList.Create;
@@ -4003,17 +4030,17 @@ begin
FRegistry.CloseKey;
CloseHandle(SnapShot);
AllKeys.Free;
- rx.Free;
+ rx.Free; }
except
on E:Exception do // Prefer ShowMessage, see http://www.heidisql.com/forum.php?t=14001
ShowMessage('Error: '+E.Message);
end;
- FRegistry.Free;
+ //FRegistry.Free;
inherited;
-end;}
+end;
-{procedure TAppSettings.InitSetting(Index: TAppSettingIndex; Name: String;
+procedure TAppSettings.InitSetting(Index: TAppSettingIndex; Name: String;
DefaultInt: Integer=0; DefaultBool: Boolean=False; DefaultString: String='';
Session: Boolean=False);
begin
@@ -4023,37 +4050,37 @@ begin
FSettings[Index].DefaultBool := DefaultBool;
FSettings[Index].DefaultString := DefaultString;
FSettings[Index].Synced := False;
-end;}
+end;
-{procedure TAppSettings.SetSessionPath(Value: String);
+procedure TAppSettings.SetSessionPath(Value: String);
begin
// Following calls may want to read or write some session specific setting
if Value <> FSessionPath then begin
FSessionPath := Value;
PrepareRegistry;
end;
-end;}
+end;
-{procedure TAppSettings.ResetPath;
+procedure TAppSettings.ResetPath;
begin
SessionPath := '';
-end;}
+end;
-{procedure TAppSettings.StorePath;
+procedure TAppSettings.StorePath;
begin
FStoredPath := SessionPath;
-end;}
+end;
-{procedure TAppSettings.RestorePath;
+procedure TAppSettings.RestorePath;
begin
SessionPath := FStoredPath;
-end;}
+end;
-{procedure TAppSettings.PrepareRegistry;
+procedure TAppSettings.PrepareRegistry;
var
Folder: String;
begin
@@ -4061,7 +4088,7 @@ begin
Folder := FBasePath;
if FSessionPath <> '' then
Folder := Folder + REGKEY_SESSIONS + '\' + FSessionPath;
- if '\'+FRegistry.CurrentPath <> Folder then try
+ {if '\'+FRegistry.CurrentPath <> Folder then try
FRegistry.OpenKey(Folder, True);
except
on E:Exception do begin
@@ -4069,33 +4096,33 @@ begin
E.Message := E.Message + CRLF + CRLF + 'While trying to open registry key "'+Folder+'"';
raise;
end;
- end;
-end;}
+ end;}
+end;
-{function TAppSettings.GetValueNames: TStringList;
+function TAppSettings.GetValueNames: TStringList;
begin
PrepareRegistry;
Result := TStringList.Create;
- FRegistry.GetValueNames(Result);
-end;}
+ //FRegistry.GetValueNames(Result);
+end;
-{function TAppSettings.GetValueName(Index: TAppSettingIndex): String;
+function TAppSettings.GetValueName(Index: TAppSettingIndex): String;
begin
Result := FSettings[Index].Name;
-end;}
+end;
-{function TAppSettings.GetKeyNames: TStringList;
+function TAppSettings.GetKeyNames: TStringList;
begin
PrepareRegistry;
Result := TStringList.Create;
- FRegistry.GetKeyNames(Result);
-end;}
+ //FRegistry.GetKeyNames(Result);
+end;
-{function TAppSettings.DeleteValue(Index: TAppSettingIndex; FormatName: String=''): Boolean;
+function TAppSettings.DeleteValue(Index: TAppSettingIndex; FormatName: String=''): Boolean;
var
ValueName: String;
begin
@@ -4103,18 +4130,18 @@ begin
ValueName := GetValueName(Index);
if FormatName <> '' then
ValueName := Format(ValueName, [FormatName]);
- Result := FRegistry.DeleteValue(ValueName);
+ Result := True; //FRegistry.DeleteValue(ValueName);
FSettings[Index].Synced := False;
-end;}
+end;
-{function TAppSettings.DeleteValue(ValueName: String): Boolean;
+function TAppSettings.DeleteValue(ValueName: String): Boolean;
begin
- Result := FRegistry.DeleteValue(ValueName);
-end;}
+ //Result := FRegistry.DeleteValue(ValueName);
+end;
-{procedure TAppSettings.DeleteCurrentKey;
+procedure TAppSettings.DeleteCurrentKey;
var
KeyPath: String;
begin
@@ -4122,78 +4149,78 @@ begin
// Note that, contrary to the documentation, .DeleteKey is done even when this key has subkeys
PrepareRegistry;
if FSessionPath.IsEmpty then
- raise Exception.CreateFmt(_('No path set, won''t delete root key %s'), [FRegistry.CurrentPath])
+ //raise Exception.CreateFmt(_('No path set, won''t delete root key %s'), [FRegistry.CurrentPath])
else begin
KeyPath := REGKEY_SESSIONS + '\' + FSessionPath;
ResetPath;
- FRegistry.DeleteKey(KeyPath);
+ //FRegistry.DeleteKey(KeyPath);
end;
-end;}
+end;
-{procedure TAppSettings.MoveCurrentKey(TargetPath: String);
+procedure TAppSettings.MoveCurrentKey(TargetPath: String);
var
KeyPath: String;
begin
PrepareRegistry;
if FSessionPath.IsEmpty then
- raise Exception.CreateFmt(_('No path set, won''t move root key %s'), [FRegistry.CurrentPath])
+ //raise Exception.CreateFmt(_('No path set, won''t move root key %s'), [FRegistry.CurrentPath])
else begin
KeyPath := REGKEY_SESSIONS + '\' + FSessionPath;
ResetPath;
- FRegistry.MoveKey(KeyPath, TargetPath, True);
+ //FRegistry.MoveKey(KeyPath, TargetPath, True);
end;
-end;}
+end;
-{function TAppSettings.ValueExists(Index: TAppSettingIndex): Boolean;
+function TAppSettings.ValueExists(Index: TAppSettingIndex): Boolean;
var
ValueName: String;
begin
PrepareRegistry;
ValueName := GetValueName(Index);
- Result := FRegistry.ValueExists(ValueName);
-end;}
+ Result := True; //FRegistry.ValueExists(ValueName);
+end;
-{function TAppSettings.SessionPathExists(SessionPath: String): Boolean;
+function TAppSettings.SessionPathExists(SessionPath: String): Boolean;
begin
- Result := FRegistry.KeyExists(FBasePath + REGKEY_SESSIONS + '\' + SessionPath);
-end;}
+ Result := True; //FRegistry.KeyExists(FBasePath + REGKEY_SESSIONS + '\' + SessionPath);
+end;
-{function TAppSettings.IsEmptyKey: Boolean;
+function TAppSettings.IsEmptyKey: Boolean;
var
TestList: TStringList;
begin
TestList := GetValueNames;
- Result := (not FRegistry.HasSubKeys) and (TestList.Count = 0);
+ Result := {(not FRegistry.HasSubKeys) and} (TestList.Count = 0);
TestList.Free;
-end;}
+end;
-{function TAppSettings.GetDefaultInt(Index: TAppSettingIndex): Integer;
+function TAppSettings.GetDefaultInt(Index: TAppSettingIndex): Integer;
begin
// Return default integer value
Result := FSettings[Index].DefaultInt;
-end;}
+end;
-{function TAppSettings.GetDefaultBool(Index: TAppSettingIndex): Boolean;
+function TAppSettings.GetDefaultBool(Index: TAppSettingIndex): Boolean;
begin
// Return default boolean value
Result := FSettings[Index].DefaultBool;
-end;}
+end;
-{function TAppSettings.GetDefaultString(Index: TAppSettingIndex): String;
+function TAppSettings.GetDefaultString(Index: TAppSettingIndex): String;
begin
// Return default string value
Result := FSettings[Index].DefaultString;
-end;}
+end;
-{procedure TAppSettings.Read(Index: TAppSettingIndex; FormatName: String;
+procedure TAppSettings.Read(Index: TAppSettingIndex; FormatName: String;
DataType: TAppSettingDataType; var I: Integer; var B: Boolean; var S: String;
DI: Integer; DB: Boolean; DS: String);
var
@@ -4222,14 +4249,14 @@ begin
adString: S := FSettings[Index].CurrentString;
else raise Exception.CreateFmt(_(SUnsupportedSettingsDatatype), [FSettings[Index].Name]);
end;
- end else if FRegistry.ValueExists(ValueName) then begin
+ end else if true {FRegistry.ValueExists(ValueName)} then begin
Inc(FReads);
- case DataType of
+ {case DataType of
adInt: I := FRegistry.ReadInteger(ValueName);
adBool: B := FRegistry.ReadBool(ValueName);
adString: S := FRegistry.ReadString(ValueName);
else raise Exception.CreateFmt(_(SUnsupportedSettingsDatatype), [FSettings[Index].Name]);
- end;
+ end;}
end;
if (FormatName = '') and (FSessionPath = '') then begin
FSettings[Index].Synced := True;
@@ -4237,51 +4264,51 @@ begin
FSettings[Index].CurrentBool := B;
FSettings[Index].CurrentString := S;
end;
-end;}
+end;
-{function TAppSettings.ReadInt(Index: TAppSettingIndex; FormatName: String=''; Default: Integer=0): Integer;
+function TAppSettings.ReadInt(Index: TAppSettingIndex; FormatName: String=''; Default: Integer=0): Integer;
var
S: String;
B: Boolean;
begin
Read(Index, FormatName, adInt, Result, B, S, Default, False, '');
-end;}
+end;
-{function TAppSettings.ReadIntDpiAware(Index: TAppSettingIndex; AControl: TControl; FormatName: String=''; Default: Integer=0): Integer;
+function TAppSettings.ReadIntDpiAware(Index: TAppSettingIndex; AControl: TControl; FormatName: String=''; Default: Integer=0): Integer;
begin
Result := ReadInt(Index, FormatName, Default);
- Result := Round(Result * AControl.ScaleFactor);
-end;}
+ //Result := Round(Result * AControl.ScaleFactor);
+end;
-{function TAppSettings.ReadBool(Index: TAppSettingIndex; FormatName: String=''; Default: Boolean=False): Boolean;
+function TAppSettings.ReadBool(Index: TAppSettingIndex; FormatName: String=''; Default: Boolean=False): Boolean;
var
I: Integer;
S: String;
begin
Read(Index, FormatName, adBool, I, Result, S, 0, Default, '');
-end;}
+end;
-{function TAppSettings.ReadString(Index: TAppSettingIndex; FormatName: String=''; Default: String=''): String;
+function TAppSettings.ReadString(Index: TAppSettingIndex; FormatName: String=''; Default: String=''): String;
var
I: Integer;
B: Boolean;
begin
Read(Index, FormatName, adString, I, B, Result, 0, False, Default);
-end;}
+end;
-{function TAppSettings.ReadString(ValueName: String): String;
+function TAppSettings.ReadString(ValueName: String): String;
begin
PrepareRegistry;
- Result := FRegistry.ReadString(ValueName);
-end;}
+ Result := ''; //FRegistry.ReadString(ValueName);
+end;
-{procedure TAppSettings.Write(Index: TAppSettingIndex; FormatName: String;
+procedure TAppSettings.Write(Index: TAppSettingIndex; FormatName: String;
DataType: TAppSettingDataType; I: Integer; B: Boolean; S: String);
var
ValueName: String;
@@ -4301,7 +4328,7 @@ begin
adInt: begin
SameAsCurrent := FSettings[Index].Synced and (I = FSettings[Index].CurrentInt);
if not SameAsCurrent then begin
- FRegistry.WriteInteger(ValueName, I);
+ //FRegistry.WriteInteger(ValueName, I);
Inc(FWrites);
end;
FSettings[Index].CurrentInt := I;
@@ -4309,7 +4336,7 @@ begin
adBool: begin
SameAsCurrent := FSettings[Index].Synced and (B = FSettings[Index].CurrentBool);
if not SameAsCurrent then begin
- FRegistry.WriteBool(ValueName, B);
+ //FRegistry.WriteBool(ValueName, B);
Inc(FWrites);
end;
FSettings[Index].CurrentBool := B;
@@ -4317,7 +4344,7 @@ begin
adString: begin
SameAsCurrent := FSettings[Index].Synced and (S = FSettings[Index].CurrentString);
if not SameAsCurrent then begin
- FRegistry.WriteString(ValueName, S);
+ //FRegistry.WriteString(ValueName, S);
Inc(FWrites);
end;
FSettings[Index].CurrentString := S;
@@ -4327,51 +4354,51 @@ begin
end;
if (FormatName = '') and (FSessionPath = '') then
FSettings[Index].Synced := True;
-end;}
+end;
-{procedure TAppSettings.WriteInt(Index: TAppSettingIndex; Value: Integer; FormatName: String='');
+procedure TAppSettings.WriteInt(Index: TAppSettingIndex; Value: Integer; FormatName: String='');
begin
Write(Index, FormatName, adInt, Value, False, '');
-end;}
+end;
-{procedure TAppSettings.WriteIntDpiAware(Index: TAppSettingIndex; AControl: TControl; Value: Integer; FormatName: String='');
+procedure TAppSettings.WriteIntDpiAware(Index: TAppSettingIndex; AControl: TControl; Value: Integer; FormatName: String='');
begin
- Value := Round(Value / AControl.ScaleFactor);
+ Value := Round(Value {/ AControl.ScaleFactor});
WriteInt(Index, Value, FormatName);
-end;}
+end;
-{procedure TAppSettings.WriteBool(Index: TAppSettingIndex; Value: Boolean; FormatName: String='');
+procedure TAppSettings.WriteBool(Index: TAppSettingIndex; Value: Boolean; FormatName: String='');
begin
Write(Index, FormatName, adBool, 0, Value, '');
-end;}
+end;
-{procedure TAppSettings.WriteString(Index: TAppSettingIndex; Value: String; FormatName: String='');
+procedure TAppSettings.WriteString(Index: TAppSettingIndex; Value: String; FormatName: String='');
begin
Write(Index, FormatName, adString, 0, False, Value);
-end;}
+end;
-{procedure TAppSettings.WriteString(ValueName, Value: String);
+procedure TAppSettings.WriteString(ValueName, Value: String);
begin
PrepareRegistry;
- FRegistry.WriteString(ValueName, Value);
-end;}
+ //FRegistry.WriteString(ValueName, Value);
+end;
-{function TAppSettings.GetSessionNames(ParentPath: String; var Folders: TStringList): TStringList;
+function TAppSettings.GetSessionNames(ParentPath: String; var Folders: TStringList): TStringList;
var
i: Integer;
CurPath: String;
begin
ResetPath;
CurPath := FBasePath + REGKEY_SESSIONS + '\' + ParentPath;
- FRegistry.OpenKey(CurPath, False);
+ //FRegistry.OpenKey(CurPath, False);
Result := TStringList.Create;
- FRegistry.GetKeyNames(Result);
+ {FRegistry.GetKeyNames(Result);
for i:=Result.Count-1 downto 0 do begin
// Issue #1111 describes a recursive endless loop, which may be caused by an empty key name here?
if Result[i].IsEmpty then
@@ -4383,11 +4410,11 @@ begin
Result.Delete(i);
end;
end;
- end;
-end;}
+ end;}
+end;
-{procedure TAppSettings.GetSessionPaths(ParentPath: String; var Sessions: TStringList);
+procedure TAppSettings.GetSessionPaths(ParentPath: String; var Sessions: TStringList);
var
Folders, Names: TStringList;
i: Integer;
@@ -4401,15 +4428,15 @@ begin
Sessions.Sort;
Names.Free;
Folders.Free;
-end;}
+end;
-{procedure TAppSettings.ImportSettings(Filename: String);
+procedure TAppSettings.ImportSettings(Filename: String);
var
Content, Name, Value, KeyPath: String;
Lines, Segments: TStringList;
i: Integer;
- DataType: TRegDataType;
+ //DataType: TRegDataType;
begin
// Load registry settings from file
@@ -4419,7 +4446,7 @@ begin
Content := ReadTextfile(FileName, UTF8NoBOMEncoding);
Lines := Explode(CRLF, Content);
- for i:=0 to Lines.Count-1 do begin
+ {for i:=0 to Lines.Count-1 do begin
// Each line has 3 segments: reg path | data type | value. Continue if explode finds less or more than 3.
Segments := Explode(DELIMITER, Lines[i]);
if Segments.Count <> 3 then
@@ -4445,15 +4472,15 @@ begin
ErrorDialog(Name+' has an unsupported data type.');
end;
Segments.Free;
- end;
+ end;}
Lines.Free;
-end;}
+end;
-{function TAppSettings.ExportSettings(Filename: String): Boolean;
+function TAppSettings.ExportSettings(Filename: String): Boolean;
var
Content, Value: String;
- DataType: TRegDataType;
+ //DataType: TRegDataType;
procedure ReadKeyToContent(Path: String);
var
@@ -4462,7 +4489,7 @@ var
SubPath: String;
begin
// Recursively read values in keys and their subkeys into "content" variable
- FRegistry.OpenKey(Path, True);
+ {FRegistry.OpenKey(Path, True);
SubPath := Copy(Path, Length(FBasePath)+1, MaxInt);
Names := TStringList.Create;
FRegistry.GetValueNames(Names);
@@ -4488,7 +4515,7 @@ var
FRegistry.GetKeyNames(Names);
for i:=0 to Names.Count-1 do
ReadKeyToContent(Path + Names[i] + '\');
- Names.Free;
+ Names.Free;}
end;
begin
@@ -4497,10 +4524,10 @@ begin
ReadKeyToContent(FBasePath);
SaveUnicodeFile(FileName, Content, UTF8NoBOMEncoding);
Result := True;
-end;}
+end;
-{function TAppSettings.ExportSettings: Boolean;
+function TAppSettings.ExportSettings: Boolean;
begin
Result := False;
if not FPortableModeReadOnly then begin
@@ -4516,28 +4543,28 @@ begin
end;
end;
end;
-end;}
+end;
-{function TAppSettings.DirnameUserAppData: String;
+function TAppSettings.DirnameUserAppData: String;
begin
// User folder for HeidiSQL's data (\Application Data)
- Result := GetShellFolder(FOLDERID_RoamingAppData) + '\' + APPNAME + '\';
+ Result := GetAppConfigDir(False);
if not DirectoryExists(Result) then begin
ForceDirectories(Result);
end;
-end;}
+end;
-{function TAppSettings.DirnameUserDocuments: String;
+function TAppSettings.DirnameUserDocuments: String;
begin
// "HeidiSQL" folder under user's documents folder, e.g. c:\Users\Mike\Documents\HeidiSQL\
- Result := GetShellFolder(FOLDERID_Documents) + '\' + APPNAME + '\';
+ Result := DirnameUserAppData;
// Do not auto-create it, as we only use it for snippets which can also have a custom path.
-end;}
+end;
-{function TAppSettings.DirnameSnippets: String;
+function TAppSettings.DirnameSnippets: String;
begin
// Folder for snippets
Result := ReadString(asCustomSnippetsDirectory);
@@ -4547,34 +4574,34 @@ begin
if not DirectoryExists(Result) then begin
ForceDirectories(Result);
end;
-end;}
+end;
-{function TAppSettings.DirnameBackups: String;
+function TAppSettings.DirnameBackups: String;
begin
// Create backup folder if it does not exist and return it
if PortableMode then begin
- Result := ExtractFilePath(Application.ExeName) + 'Backups\'
+ Result := ExtractFilePath(Application.ExeName) + 'Backups' + DirectorySeparator
end else begin
- Result := DirnameUserAppData + 'Backups\';
+ Result := DirnameUserAppData + 'Backups' + DirectorySeparator;
end;
if not DirectoryExists(Result) then begin
ForceDirectories(Result);
end;
-end;}
+end;
-{function TAppSettings.DirnameHighlighters: string;
+function TAppSettings.DirnameHighlighters: string;
begin
if PortableMode then begin
- Result := ExtractFilePath(Application.ExeName) + 'Highlighters\'
+ Result := ExtractFilePath(Application.ExeName) + 'Highlighters' + DirectorySeparator
end else begin
- Result := DirnameUserAppData + 'Highlighters\';
+ Result := DirnameUserAppData + 'Highlighters' + DirectorySeparator;
end;
if not DirectoryExists(Result) then begin
ForceDirectories(Result);
end;
-end;}
+end;
diff --git a/source/change_password.lfm b/source/change_password.lfm
new file mode 100644
index 00000000..c23253d6
--- /dev/null
+++ b/source/change_password.lfm
@@ -0,0 +1,159 @@
+object frmPasswordChange: TfrmPasswordChange
+ Left = 0
+ Top = 0
+ BorderStyle = bsDialog
+ Caption = 'Change expired password'
+ ClientHeight = 187
+ ClientWidth = 456
+ Color = clBtnFace
+ Constraints.MaxHeight = 300
+ Constraints.MaxWidth = 600
+ Constraints.MinHeight = 185
+ Constraints.MinWidth = 400
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ Position = poMainFormCenter
+ OnCreate = FormCreate
+ OnShow = FormShow
+ object lblHeading: TLabel
+ Left = 8
+ Top = 16
+ Width = 440
+ Height = 51
+ Anchors = [akLeft, akTop, akRight, akBottom]
+ AutoSize = False
+ Caption = 'lblHeading'
+ WordWrap = True
+ end
+ object lblPassword: TLabel
+ Left = 8
+ Top = 76
+ Width = 74
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'New password:'
+ end
+ object lblRepeatPassword: TLabel
+ Left = 8
+ Top = 103
+ Width = 111
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'Repeat new password:'
+ end
+ object lblStatus: TLabel
+ Left = 8
+ Top = 132
+ Width = 41
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = 'lblStatus'
+ end
+ object editPassword: TEdit
+ Left = 146
+ Top = 73
+ Width = 302
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ TabOrder = 0
+ TextHint = 'Type new password or select a suggestion'
+ OnChange = editPasswordChange
+ OnClick = editPasswordChange
+ OnKeyDown = editPasswordKeyDown
+ end
+ object editRepeatPassword: TEdit
+ Left = 146
+ Top = 100
+ Width = 302
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ PasswordChar = '*'
+ TabOrder = 1
+ TextHint = 'Retype password'
+ OnChange = editPasswordChange
+ OnClick = editPasswordChange
+ OnKeyDown = editPasswordKeyDown
+ end
+ object btnCancel: TButton
+ Left = 292
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Cancel = True
+ Caption = 'Cancel'
+ ModalResult = 2
+ TabOrder = 2
+ end
+ object btnOK: TButton
+ Left = 213
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'OK'
+ Default = True
+ ModalResult = 1
+ TabOrder = 3
+ end
+ object btnCopyToClipboard: TButton
+ Left = 373
+ Top = 154
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'Copy'
+ TabOrder = 4
+ OnClick = btnCopyToClipboardClick
+ end
+ object progressbarPasswordStrength: TProgressBar
+ Left = 146
+ Top = 131
+ Width = 302
+ Height = 17
+ TabOrder = 5
+ end
+ object popupPassword: TPopupMenu
+ Left = 344
+ Top = 8
+ object N6characters1: TMenuItem
+ Caption = '6 characters'
+ OnClick = menuPasswordClick
+ object menuDummy1: TMenuItem
+ Caption = 'dummy'
+ OnClick = menuPasswordInsert
+ end
+ end
+ object N8characters1: TMenuItem
+ Caption = '8 characters'
+ OnClick = menuPasswordClick
+ object menuDummy2: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N10characters1: TMenuItem
+ Caption = '10 characters'
+ OnClick = menuPasswordClick
+ object menuDummy3: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N12characters1: TMenuItem
+ Caption = '12 characters'
+ OnClick = menuPasswordClick
+ object menuDummy4: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ object N30characters1: TMenuItem
+ Caption = '30 characters'
+ OnClick = menuPasswordClick
+ object menuDummy5: TMenuItem
+ Caption = 'dummy'
+ end
+ end
+ end
+end
diff --git a/source/change_password.pas b/source/change_password.pas
new file mode 100644
index 00000000..cc165ebb
--- /dev/null
+++ b/source/change_password.pas
@@ -0,0 +1,182 @@
+unit change_password;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ SysUtils, Variants, Classes, Graphics,
+ Controls, Forms, Dialogs, StdCtrls, ExtCtrls,
+ Menus, Clipbrd, ComCtrls, Math;
+
+type
+ TfrmPasswordChange = class(TForm)
+ lblHeading: TLabel;
+ lblPassword: TLabel;
+ lblRepeatPassword: TLabel;
+ editPassword: TEdit;
+ editRepeatPassword: TEdit;
+ btnCancel: TButton;
+ btnOK: TButton;
+ lblStatus: TLabel;
+ popupPassword: TPopupMenu;
+ N6characters1: TMenuItem;
+ N8characters1: TMenuItem;
+ N10characters1: TMenuItem;
+ N12characters1: TMenuItem;
+ N30characters1: TMenuItem;
+ menuDummy1: TMenuItem;
+ menuDummy2: TMenuItem;
+ menuDummy3: TMenuItem;
+ menuDummy4: TMenuItem;
+ menuDummy5: TMenuItem;
+ btnCopyToClipboard: TButton;
+ progressbarPasswordStrength: TProgressBar;
+ procedure editPasswordChange(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure editPasswordKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+ procedure menuPasswordClick(Sender: TObject);
+ procedure menuPasswordInsert(Sender: TObject);
+ procedure btnCopyToClipboardClick(Sender: TObject);
+ procedure FormCreate(Sender: TObject);
+ private
+ { Private-Deklarationen }
+ procedure CheckPasswordStrength;
+ public
+ { Public-Deklarationen }
+ end;
+
+var
+ frmPasswordChange: TfrmPasswordChange;
+
+implementation
+
+uses main, apphelpers;
+
+{$R *.lfm}
+
+
+procedure TfrmPasswordChange.FormCreate(Sender: TObject);
+begin
+ //HasSizeGrip := True;
+end;
+
+
+procedure TfrmPasswordChange.FormShow(Sender: TObject);
+begin
+ // Manually trigger change event on password box
+ editPassword.OnChange(Sender);
+end;
+
+
+procedure TfrmPasswordChange.menuPasswordClick(Sender: TObject);
+var
+ Parent, Item: TMenuItem;
+ PasswordLen, i: Integer;
+begin
+ // Create menu items with random passwords
+ Parent := Sender as TMenuItem;
+ PasswordLen := MakeInt(Parent.Caption);
+ for i:=0 to 19 do begin
+ if Parent.Count > i then
+ Item := Parent[i]
+ else begin
+ Item := TMenuItem.Create(Parent);
+ Parent.Add(Item);
+ end;
+ Item.OnClick := menuPasswordInsert;
+ Item.Caption := GeneratePassword(PasswordLen);
+ end;
+end;
+
+
+procedure TfrmPasswordChange.menuPasswordInsert(Sender: TObject);
+var
+ Item: TMenuItem;
+begin
+ // Insert password from menu item
+ Item := Sender as TMenuItem;
+ editPassword.Text := Item.Caption;
+ editRepeatPassword.Text := editPassword.Text;
+end;
+
+
+procedure TfrmPasswordChange.btnCopyToClipboardClick(Sender: TObject);
+var
+ OldImageIndex: Integer;
+begin
+ // Copy new password to clipboard
+ Clipboard.TryAsText := editPassword.Text;
+ //OldImageIndex := btnCopyToClipboard.ImageIndex;
+ //btnCopyToClipboard.ImageIndex := 55;
+ //btnCopyToClipboard.Repaint;
+ //Sleep(500);
+ //btnCopyToClipboard.ImageIndex := OldImageIndex;
+ Beep;
+end;
+
+
+procedure TfrmPasswordChange.editPasswordChange(Sender: TObject);
+var
+ PasswordsMatch: Boolean;
+begin
+ // User has entered something on one or both password fields
+ btnOK.Enabled := False;
+ btnCopyToClipboard.Enabled := False;
+ progressbarPasswordStrength.Visible := False;
+ if Sender = editPassword then
+ editRepeatPassword.Modified := False;
+
+ if editPassword.Text = '' then begin
+ editPassword.PasswordChar := #0;
+ lblStatus.Caption := _('Please change your password')
+ end else begin
+ editPassword.PasswordChar := '*';
+ PasswordsMatch := editPassword.Text = editRepeatPassword.Text;
+ if editRepeatPassword.Modified and (not PasswordsMatch) then
+ lblStatus.Caption := _('Error: Passwords do not match!')
+ else begin
+ lblStatus.Caption := _('Password strength:');
+ btnOK.Enabled := PasswordsMatch;
+ btnCopyToClipboard.Enabled := True;
+ progressbarPasswordStrength.Visible := True;
+ CheckPasswordStrength;
+ end;
+ end;
+end;
+
+
+procedure TfrmPasswordChange.editPasswordKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+begin
+ editPassword.OnChange(Sender);
+end;
+
+
+procedure TfrmPasswordChange.CheckPasswordStrength;
+var
+ i, Distance, LastOrd: Integer;
+ p: String;
+begin
+ // Simple password-strength checker
+ // Calculates the distance between all character codes, and give it a bonus for longer passwords
+ p := editPassword.Text;
+ Distance := 0;
+ LastOrd := -1;
+ for i:=1 to Length(p) do begin
+ if LastOrd > -1 then
+ Inc(Distance, Abs(LastOrd - Ord(p[i])));
+ LastOrd := Ord(p[i]);
+ end;
+ Inc(Distance, Length(p)*10);
+ progressbarPasswordStrength.Position := Round(progressbarPasswordStrength.Max / 500 * Distance);
+ {case progressbarPasswordStrength.Position of
+ 0..20: progressbarPasswordStrength.State := pbsError;
+ 21..50: progressbarPasswordStrength.State := pbsPaused;
+ 51..100: progressbarPasswordStrength.State := pbsNormal;
+ end;}
+end;
+
+
+end.
diff --git a/source/dbconnection.pas b/source/dbconnection.pas
index a603f20e..dc55255b 100644
--- a/source/dbconnection.pas
+++ b/source/dbconnection.pas
@@ -5,10 +5,420 @@ unit dbconnection;
interface
uses
- Classes, SysUtils, Generics.Collections, Generics.Defaults;
+ Classes, SysUtils, Generics.Collections, Generics.Defaults,
+ DateUtils, Types, Math, Dialogs, DB, Graphics, ExtCtrls, StrUtils,
+ Controls, Forms, IniFiles, Variants, Rtti, FileUtil,
+ RegExpr, generic_types,
+ {$IFDEF WINDOWS}Windows,
+ {$ELSE}Unix, {$IFDEF LCLCarbon}MacOSAll, {$ENDIF}
+ {$ENDIF}
+ dbstructures, dbstructures.mysql, dbstructures.mssql, dbstructures.postgresql, dbstructures.sqlite, dbstructures.interbase;
+
type
+ {$M+} // Needed to add published properties
+
+ { TDBObjectList and friends }
+
+ TListNodeType = (lntNone, lntDb, lntGroup, lntTable, lntView, lntFunction, lntProcedure, lntTrigger, lntEvent, lntColumn);
+ TListNodeTypes = Set of TListNodeType;
TDBConnection = class;
+ TConnectionParameters = class;
+ TDBQuery = class;
+ TDBQueryList = TObjectList;
+ TDBObject = class;
+
+ TColumnPart = (cpAll, cpName, cpType, cpAllowNull, cpSRID, cpDefault, cpVirtuality, cpComment, cpCollation, cpInvisible);
+ TColumnParts = Set of TColumnPart;
+ TColumnDefaultType = (cdtNothing, cdtText, cdtNull, cdtAutoInc, cdtExpression);
+ // General purpose editing status flag
+ TEditingStatus = (esUntouched, esModified, esDeleted, esAddedUntouched, esAddedModified, esAddedDeleted);
+
+ // Column object, many of them in a TObjectList
+ TTableColumn = class(TPersistent)
+ private
+ FConnection: TDBConnection;
+ FStatus: TEditingStatus;
+ procedure SetStatus(Value: TEditingStatus);
+ public
+ Name, OldName: String;
+ DataType, OldDataType: TDBDatatype;
+ LengthSet: String;
+ Unsigned, AllowNull, ZeroFill, LengthCustomized, Invisible: Boolean;
+ DefaultType: TColumnDefaultType;
+ DefaultText: String;
+ OnUpdateType: TColumnDefaultType;
+ OnUpdateText: String;
+ Comment, Charset, Collation, GenerationExpression, Virtuality: String;
+ SRID: Cardinal;
+ constructor Create(AOwner: TDBConnection; Serialized: String='');
+ destructor Destroy; override;
+ procedure Assign(Source: TPersistent); override;
+ function Serialize: String;
+ function SQLCode(OverrideCollation: String=''; Parts: TColumnParts=[cpAll]): String;
+ function ValueList: TStringList;
+ procedure ParseDatatype(Source: String);
+ function CastAsText: String;
+ property Status: TEditingStatus read FStatus write SetStatus;
+ property Connection: TDBConnection read FConnection;
+ function AutoIncName: String;
+ function FullDataType: String;
+ end;
+ PTableColumn = ^TTableColumn;
+ TTableColumnList = class(TObjectList)
+ public
+ procedure Assign(Source: TTableColumnList);
+ function FindByName(const Value: String): TTableColumn;
+ end;
+ TColumnCache = TDictionary;
+
+ TTableKey = class(TPersistent)
+ const
+ PRIMARY = 'PRIMARY';
+ KEY = 'KEY';
+ UNIQUE = 'UNIQUE';
+ FULLTEXT = 'FULLTEXT';
+ SPATIAL = 'SPATIAL';
+ private
+ FConnection: TDBConnection;
+ function GetInsideCreateCode: Boolean;
+ function GetImageIndex: Integer;
+ public
+ Name, OldName: String;
+ IndexType, OldIndexType, Algorithm, Comment: String;
+ Columns, SubParts, Collations: TStringList;
+ Modified, Added: Boolean;
+ constructor Create(AOwner: TDBConnection);
+ destructor Destroy; override;
+ procedure Assign(Source: TPersistent); override;
+ function IsPrimary: Boolean;
+ function IsIndex: Boolean;
+ function IsUnique: Boolean;
+ function IsFulltext: Boolean;
+ function IsSpatial: Boolean;
+ function IsExpression(KeyPart: Integer): Boolean;
+ procedure Modification(Sender: TObject);
+ function SQLCode(TableName: String=''): String;
+ property InsideCreateCode: Boolean read GetInsideCreateCode;
+ property ImageIndex: Integer read GetImageIndex;
+ property Connection: TDBConnection read FConnection;
+ end;
+ TTableKeyList = class(TObjectList)
+ public
+ procedure Assign(Source: TTableKeyList);
+ end;
+ TKeyCache = TDictionary;
+
+ // Helper object to manage foreign keys in a TObjectList
+ TForeignKey = class(TPersistent)
+ private
+ FConnection: TDBConnection;
+ public
+ KeyName, OldKeyName, Db, ReferenceDb, ReferenceTable, OnUpdate, OnDelete: String;
+ Columns, ForeignColumns: TStringList;
+ Modified, Added, KeyNameWasCustomized: Boolean;
+ constructor Create(AOwner: TDBConnection);
+ destructor Destroy; override;
+ procedure Assign(Source: TPersistent); override;
+ function SQLCode(IncludeSymbolName: Boolean): String;
+ function ReferenceTableObj: TDBObject;
+ property Connection: TDBConnection read FConnection;
+ end;
+ TForeignKeyList = class(TObjectList)
+ public
+ procedure Assign(Source: TForeignKeyList);
+ end;
+ TForeignKeyCache = TDictionary;
+
+ TCheckConstraint = class(TPersistent)
+ private
+ FConnection: TDBConnection;
+ FName, FCheckClause: String;
+ FModified, FAdded: Boolean;
+ public
+ constructor Create(AOwner: TDBConnection);
+ procedure Assign(Source: TPersistent); override;
+ function SQLCode: String;
+ property Connection: TDBConnection read FConnection;
+ property Name: String read FName write FName;
+ property CheckClause: String read FCheckClause write FCheckClause;
+ property Modified: Boolean read FModified write FModified;
+ property Added: Boolean read FAdded write FAdded;
+ end;
+ TCheckConstraintList = class(TObjectList)
+ public
+ procedure Assign(Source: TCheckConstraintList);
+ end;
+ TCheckConstraintCache = TDictionary;
+
+ TRoutineParam = class(TObject)
+ public
+ Name, Context, Datatype: String;
+ end;
+ TRoutineParamList = TObjectList;
+
+ TDBObject = class(TPersistent)
+ private
+ FCreateCode: String;
+ FCreateCodeLoaded: Boolean;
+ FWasSelected: Boolean;
+ FConnection: TDBConnection;
+ function GetObjType: String;
+ function GetImageIndex: Integer;
+ function GetOverlayImageIndex: Integer;
+ function GetPath: String;
+ function GetTableColumns: TTableColumnList;
+ function GetTableKeys: TTableKeyList;
+ function GetTableForeignKeys: TForeignKeyList;
+ function GetTableCheckConstraints: TCheckConstraintList;
+ public
+ // Table options:
+ Name, Schema, Database, Column, Engine, Comment, RowFormat, CreateOptions, Collation: String;
+ Created, Updated, LastChecked: TDateTime;
+ Rows, Size, Version, AvgRowLen, MaxDataLen, IndexLen, DataLen, DataFree, AutoInc, CheckSum: Int64;
+ // Routine options:
+ Body, Definer, Returns, DataAccess, Security, ArgTypes: String;
+ Deterministic, RowsAreExact: Boolean;
+
+ NodeType, GroupType: TListNodeType;
+ constructor Create(OwnerConnection: TDBConnection);
+ procedure Assign(Source: TPersistent); override;
+ procedure UnloadDetails;
+ procedure Drop;
+ function IsSameAs(CompareTo: TDBObject): Boolean;
+ function QuotedDatabase(AlwaysQuote: Boolean=True): String;
+ function QuotedName(AlwaysQuote: Boolean=True; SeparateSegments: Boolean=True): String;
+ function QuotedDbAndTableName(AlwaysQuote: Boolean=True): String;
+ function QuotedColumn(AlwaysQuote: Boolean=True): String;
+ function SchemaClauseIS(Prefix: String): String;
+ function RowCount(Reload: Boolean; ForceExact: Boolean=False): Int64;
+ function GetCreateCode: String; overload;
+ function GetCreateCode(RemoveAutoInc, RemoveDefiner: Boolean): String; overload;
+ property ObjType: String read GetObjType;
+ property ImageIndex: Integer read GetImageIndex;
+ property OverlayImageIndex: Integer read GetOverlayImageIndex;
+ property Path: String read GetPath;
+ property CreateCode: String read GetCreateCode;
+ property WasSelected: Boolean read FWasSelected write FWasSelected;
+ property Connection: TDBConnection read FConnection;
+ property TableColumns: TTableColumnList read GetTableColumns;
+ property TableKeys: TTableKeyList read GetTableKeys;
+ property TableForeignKeys: TForeignKeyList read GetTableForeignKeys;
+ property TableCheckConstraints: TCheckConstraintList read GetTableCheckConstraints;
+ end;
+ PDBObject = ^TDBObject;
+ TDBObjectList = class(TObjectList)
+ private
+ FDatabase: String;
+ FDataSize: Int64;
+ FLargestObjectSize: Int64;
+ FLastUpdate: TDateTime;
+ FCollation: String;
+ FOnlyNodeType: TListNodeType;
+ FObjectsLoaded: Boolean;
+ public
+ property Database: String read FDatabase;
+ property DataSize: Int64 read FDataSize;
+ property LargestObjectSize: Int64 read FLargestObjectSize;
+ property LastUpdate: TDateTime read FLastUpdate;
+ property Collation: String read FCollation;
+ property OnlyNodeType: TListNodeType read FOnlyNodeType;
+ end;
+ TDatabaseCache = class(TObjectList); // A list of db object lists, used for caching
+ TDBObjectComparer = class(TComparer)
+ function Compare(constref Left, Right: TDBObject): Integer; override;
+ end;
+ TDBObjectDropComparer = class(TComparer)
+ function Compare(constref Left, Right: TDBObject): Integer; override;
+ end;
+
+ TOidStringPairs = TDictionary;
+
+ // Structures for in-memory changes of a TDBQuery
+ TGridValue = class(TObject)
+ public
+ NewText, OldText: String;
+ NewIsNull, OldIsNull: Boolean;
+ NewIsFunction, OldIsFunction: Boolean;
+ Modified: Boolean;
+ destructor Destroy; override;
+ end;
+ TGridRow = class(TObjectList)
+ public
+ RecNo: Int64;
+ Inserted: Boolean;
+ end;
+ TGridRows = class(TObjectList);
+
+ // SSH related
+ TProcessPipe = class(TObject)
+ public
+ ReadHandle: THandle;
+ WriteHandle: THandle;
+ constructor Create;
+ destructor Destroy; override;
+ end;
+ TSecureShellCmd = class(TObject)
+ private
+ //FProcessInfo: TProcessInformation;
+ FInPipe: TProcessPipe;
+ FOutPipe: TProcessPipe;
+ FErrorPipe: TProcessPipe;
+ FConnection: TDBConnection;
+ function ReadPipe(const Pipe: TProcessPipe): String;
+ function AsciiToAnsi(Text: AnsiString): AnsiString;
+ function CleanEscSeq(const Buffer: String): String;
+ procedure SendText(Text: String);
+ public
+ procedure Connect;
+ constructor Create(Connection: TDBConnection);
+ destructor Destroy; override;
+ end;
+
+ TSQLFunction = class(TPersistent)
+ public
+ Name, Declaration, Category, Description: String;
+ end;
+ TSQLFunctionList = class(TObjectList)
+ private
+ FOwner: TDBConnection;
+ FCategories: TStringList;
+ FNames: TStringList;
+ public
+ constructor Create(AOwner: TDBConnection; SQLFunctionsFileOrder: String);
+ property Categories: TStringList read FCategories;
+ property Names: TStringList read FNames;
+ end;
+
+ { TConnectionParameters and friends }
+
+ TNetType = (
+ ntMySQL_TCPIP,
+ ntMySQL_NamedPipe,
+ ntMySQL_SSHtunnel,
+ ntMSSQL_NamedPipe,
+ ntMSSQL_TCPIP,
+ ntMSSQL_SPX,
+ ntMSSQL_VINES,
+ ntMSSQL_RPC,
+ ntPgSQL_TCPIP,
+ ntPgSQL_SSHtunnel,
+ ntSQLite,
+ ntMySQL_ProxySQLAdmin,
+ ntInterbase_TCPIP,
+ ntInterbase_Local,
+ ntFirebird_TCPIP,
+ ntFirebird_Local,
+ ntMySQL_RDS,
+ ntSQLiteEncrypted
+ );
+ TNetTypeGroup = (ngMySQL, ngMSSQL, ngPgSQL, ngSQLite, ngInterbase);
+ TNetTypeLibs = TDictionary;
+
+ TConnectionParameters = class(TObject)
+ strict private
+ FNetType: TNetType;
+ FHostname, FUsername, FPassword, FAllDatabases, FLibraryOrProvider, FComment, FStartupScriptFilename,
+ FSessionPath, FSSLPrivateKey, FSSLCertificate, FSSLCACertificate, FSSLCipher, FServerVersion,
+ FSSHHost, FSSHUser, FSSHPassword, FSSHExe, FSSHPrivateKey,
+ FIgnoreDatabasePattern: String;
+ FPort, FSSHPort, FSSHLocalPort, FSSHTimeout, FCounter, FQueryTimeout, FKeepAlive, FSSLVerification: Integer;
+ FSSHActive, FLoginPrompt, FCompressed, FLocalTimeZone, FFullTableStatus,
+ FWindowsAuth, FWantSSL, FIsFolder, FCleartextPluginEnabled: Boolean;
+ FSessionColor: TColor;
+ FLastConnect: TDateTime;
+ FLogFileDdl: Boolean;
+ FLogFileDml: Boolean;
+ FLogFilePath: String;
+ class var FLibraries: TNetTypeLibs;
+ function GetImageIndex: Integer;
+ function GetSessionName: String;
+ function GetAllDatabasesList: TStringList;
+ public
+ constructor Create; overload;
+ constructor Create(SessionRegPath: String); overload;
+ procedure SaveToRegistry;
+ function CreateConnection(AOwner: TComponent): TDBConnection;
+ function CreateQuery(Connection: TDbConnection): TDBQuery;
+ function NetTypeName(LongFormat: Boolean): String;
+ function GetNetTypeGroup: TNetTypeGroup;
+ function SshSupport: Boolean;
+ function IsAnyMySQL: Boolean;
+ function IsAnyMSSQL: Boolean;
+ function IsAnyPostgreSQL: Boolean;
+ function IsAnySQLite: Boolean;
+ function IsAnyInterbase: Boolean;
+ function IsMariaDB: Boolean;
+ function IsMySQL(StrictDetect: Boolean): Boolean;
+ function IsPercona: Boolean;
+ function IsTokudb: Boolean;
+ function IsInfiniDB: Boolean;
+ function IsInfobright: Boolean;
+ function IsProxySQLAdmin: Boolean;
+ function IsMySQLonRDS: Boolean;
+ function IsAzure: Boolean;
+ function IsMemSQL: Boolean;
+ function IsRedshift: Boolean;
+ function IsInterbase: Boolean;
+ function IsFirebird: Boolean;
+ property ImageIndex: Integer read GetImageIndex;
+ function GetLibraries: TStringList;
+ function DefaultLibrary: String;
+ function DefaultHost: String;
+ function DefaultPort: Integer;
+ function DefaultUsername: String;
+ function DefaultIgnoreDatabasePattern: String;
+ function DefaultSshActive: Boolean;
+ function GetExternalCliArguments(Connection: TDBConnection; ReplacePassword: TThreeStateBoolean): String;
+ published
+ property IsFolder: Boolean read FIsFolder write FIsFolder;
+ property NetType: TNetType read FNetType write FNetType;
+ property NetTypeGroup: TNetTypeGroup read GetNetTypeGroup;
+ property ServerVersion: String read FServerVersion write FServerVersion;
+ property Counter: Integer read FCounter write FCounter;
+ property LastConnect: TDateTime read FLastConnect;
+ property SessionPath: String read FSessionPath write FSessionPath;
+ property SessionName: String read GetSessionName;
+ property SessionColor: TColor read FSessionColor write FSessionColor;
+ property Hostname: String read FHostname write FHostname;
+ property Port: Integer read FPort write FPort;
+ property Username: String read FUsername write FUsername;
+ property Password: String read FPassword write FPassword;
+ property LoginPrompt: Boolean read FLoginPrompt write FLoginPrompt;
+ property WindowsAuth: Boolean read FWindowsAuth write FWindowsAuth;
+ property CleartextPluginEnabled: Boolean read FCleartextPluginEnabled write FCleartextPluginEnabled;
+ property AllDatabasesStr: String read FAllDatabases write FAllDatabases;
+ property AllDatabasesList: TStringList read GetAllDatabasesList;
+ property LibraryOrProvider: String read FLibraryOrProvider write FLibraryOrProvider;
+ property Comment: String read FComment write FComment;
+ property StartupScriptFilename: String read FStartupScriptFilename write FStartupScriptFilename;
+ property QueryTimeout: Integer read FQueryTimeout write FQueryTimeout;
+ property KeepAlive: Integer read FKeepAlive write FKeepAlive;
+ property Compressed: Boolean read FCompressed write FCompressed;
+ property LocalTimeZone: Boolean read FLocalTimeZone write FLocalTimeZone;
+ property FullTableStatus: Boolean read FFullTableStatus write FFullTableStatus;
+ property SSHActive: Boolean read FSSHActive write FSSHActive;
+ property SSHHost: String read FSSHHost write FSSHHost;
+ property SSHPort: Integer read FSSHPort write FSSHPort;
+ property SSHUser: String read FSSHUser write FSSHUser;
+ property SSHPassword: String read FSSHPassword write FSSHPassword;
+ property SSHTimeout: Integer read FSSHTimeout write FSSHTimeout;
+ property SSHPrivateKey: String read FSSHPrivateKey write FSSHPrivateKey;
+ property SSHLocalPort: Integer read FSSHLocalPort write FSSHLocalPort;
+ property SSHExe: String read FSSHExe write FSSHExe;
+ property WantSSL: Boolean read FWantSSL write FWantSSL;
+ property SSLPrivateKey: String read FSSLPrivateKey write FSSLPrivateKey;
+ property SSLCertificate: String read FSSLCertificate write FSSLCertificate;
+ property SSLCACertificate: String read FSSLCACertificate write FSSLCACertificate;
+ property SSLCipher: String read FSSLCipher write FSSLCipher;
+ property SSLVerification: Integer read FSSLVerification write FSSLVerification;
+ property IgnoreDatabasePattern: String read FIgnoreDatabasePattern write FIgnoreDatabasePattern;
+ property LogFileDdl: Boolean read FLogFileDdl write FLogFileDdl;
+ property LogFileDml: Boolean read FLogFileDml write FLogFileDml;
+ property LogFilePath: String read FLogFilePath write FLogFilePath;
+ end;
+ PConnectionParameters = ^TConnectionParameters;
+
{ TDBConnection }
@@ -22,16 +432,11134 @@ type
TDBLogItems = TObjectList;
TDBLogEvent = procedure(Msg: String; Category: TDBLogCategory=lcInfo; Connection: TDBConnection=nil) of object;
TDBEvent = procedure(Connection: TDBConnection; Database: String) of object;
+ TDBDataTypeArray = Array of TDBDataType;
+ TSQLSpecifityId = (spDatabaseTable, spDatabaseTableId, spDatabaseDrop,
+ spDbObjectsTable, spDbObjectsCreateCol, spDbObjectsUpdateCol, spDbObjectsTypeCol,
+ spEmptyTable, spRenameTable, spRenameView, spCurrentUserHost, spLikeCompare,
+ spAddColumn, spChangeColumn, spRenameColumn, spForeignKeyEventAction,
+ spGlobalStatus, spCommandsCounters, spSessionVariables, spGlobalVariables,
+ spISSchemaCol,
+ spUSEQuery, spKillQuery, spKillProcess,
+ spFuncLength, spFuncCeil, spFuncLeft, spFuncNow, spFuncLastAutoIncNumber,
+ spLockedTables, spDisableForeignKeyChecks, spEnableForeignKeyChecks,
+ spOrderAsc, spOrderDesc,
+ spForeignKeyDrop);
+ TFeatureOrRequirement = (frSrid, frTimezoneVar, frTemporalTypesFraction, frKillQuery,
+ frLockedTables, frShowCreateTrigger, frShowWarnings, frShowCollation, frShowCollationExtended,
+ frShowCharset, frIntegerDisplayWidth, frShowFunctionStatus, frShowProcedureStatus,
+ frShowTriggers, frShowEvents, frColumnDefaultParentheses, frForeignKeyChecksVar,
+ frHelpKeyword, frEditVariables, frCreateView, frCreateProcedure, frCreateFunction,
+ frCreateTrigger, frCreateEvent, frInvisibleColumns);
TDBConnection = class(TComponent)
private
FActive: Boolean;
+ FConnectionStarted: Cardinal;
+ FServerUptime: Integer;
+ FServerDateTimeOnStartup: String;
+ FParameters: TConnectionParameters;
+ FSecureShellCmd: TSecureShellCmd;
+ FLoginPromptDone: Boolean;
+ FDatabase: String;
+ FAllDatabases: TStringList;
+ FLogPrefix: String;
+ FOnLog: TDBLogEvent;
+ FOnConnected: TDBEvent;
+ FOnDatabaseChanged: TDBEvent;
+ FOnObjectnamesChanged: TDBEvent;
+ FRowsFound: Int64;
+ FRowsAffected: Int64;
+ FWarningCount: Cardinal;
+ FServerOS: String;
+ FServerVersionUntouched: String;
+ FRealHostname: String;
+ FLastQueryDuration, FLastQueryNetworkDuration: Cardinal;
+ FLastQuerySQL: String;
+ FIsUnicode: Boolean;
+ FIsSSL: Boolean;
+ FTableEngines: TStringList;
+ FTableEngineDefault: String;
+ FCollationTable: TDBQuery;
+ FCharsetTable: TDBQuery;
+ FSessionVariables: TDBQuery;
+ FInformationSchemaObjects: TStringList;
+ FDatabaseCache: TDatabaseCache;
+ FColumnCache: TColumnCache;
+ FKeyCache: TKeyCache;
+ FForeignKeyCache: TForeignKeyCache;
+ FCheckConstraintCache: TCheckConstraintCache;
+ FCurrentUserHostCombination: String;
+ FAllUserHostCombinations: TStringList;
+ FLockedByThread: TThread;
+ FStringQuoteChar: Char;
+ FQuoteChar: Char;
+ FQuoteChars: String;
+ FDatatypes: TDBDataTypeArray;
+ FThreadID: Int64;
+ FSQLSpecifities: Array[TSQLSpecifityId] of String;
+ FKeepAliveTimer: TTimer;
+ FFavorites: TStringList;
+ FPrefetchResults: TDBQueryList;
+ FForeignKeyQueriesFailed: Boolean;
+ FInfSch: String;
+ FIdentCharsNoQuote: TSysCharSet;
+ FMaxRowsPerInsert: Int64;
+ FCaseSensitivity: Integer;
+ FSQLFunctions: TSQLFunctionList;
+ procedure SetActive(Value: Boolean); virtual; abstract;
+ procedure DoBeforeConnect; virtual;
+ procedure StartSSHTunnel(var FinalHost: String; var FinalPort: Integer);
+ procedure EndSSHTunnel;
+ procedure DoAfterConnect; virtual;
+ procedure DetectUSEQuery(SQL: String); virtual;
+ procedure SetDatabase(Value: String);
+ function GetThreadId: Int64; virtual; abstract;
+ function GetCharacterSet: String; virtual;
+ procedure SetCharacterSet(CharsetName: String); virtual;
+ function GetLastErrorCode: Cardinal; virtual; abstract;
+ function GetLastErrorMsg: String; virtual; abstract;
+ function GetAllDatabases: TStringList; virtual;
+ procedure ApplyIgnoreDatabasePattern(Dbs: TStringList);
+ function GetTableEngines: TStringList; virtual;
+ function GetCollationTable: TDBQuery; virtual;
+ function GetCollationList: TStringList; virtual;
+ function GetCharsetTable: TDBQuery; virtual;
+ function GetCharsetList: TStringList;
+ function GetConnectionUptime: Integer;
+ function GetServerUptime: Integer;
+ function GetServerNow: TDateTime;
+ function GetCurrentUserHostCombination: String;
+ function GetAllUserHostCombinations: TStringList;
+ function DecodeAPIString(a: AnsiString): String;
+ function GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64; virtual;
+ procedure ClearCache(IncludeDBObjects: Boolean);
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); virtual; abstract;
+ procedure KeepAliveTimerEvent(Sender: TObject);
+ procedure Drop(Obj: TDBObject); virtual;
+ procedure PrefetchResults(SQL: String);
+ procedure FreeResults(Results: TDBQuery);
+ function IsTextDefault(Value: String; Tp: TDBDatatype): Boolean;
public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); virtual;
+ procedure Log(Category: TDBLogCategory; Msg: String);
+ function EscapeString(Text: String; ProcessJokerChars: Boolean=False; DoQuote: Boolean=True): String; overload;
+ function EscapeString(Text: String; Datatype: TDBDatatype): String; overload;
+ function EscapeBin(BinValue: String): String; overload;
+ function EscapeBin(var ByteData: TBytes): String; overload;
+ function QuoteIdent(Identifier: String; AlwaysQuote: Boolean=True; Glue: Char=#0): String;
+ function DeQuoteIdent(Identifier: String; Glue: Char=#0): String;
+ function CleanIdent(Identifier: String): String;
+ function QuotedDbAndTableName(DB, Obj: String): String;
+ function FindObject(DB, Obj: String): TDBObject;
+ function escChars(const Text: String; EscChar, Char1, Char2, Char3, Char4: Char): String;
+ function UnescapeString(Text: String): String;
+ function ExtractLiteral(var SQL: String; Prefix: String): String;
+ function GetResults(SQL: String): TDBQuery;
+ function GetCol(SQL: String; Column: Integer=0): TStringList;
+ function GetVar(SQL: String; Column: Integer=0): String; overload;
+ function GetVar(SQL: String; Column: String): String; overload;
+ function Ping(Reconnect: Boolean): Boolean; virtual; abstract;
+ function RefreshAllDatabases: TStringList;
+ function GetDBObjects(db: String; Refresh: Boolean=False; OnlyNodeType: TListNodeType=lntNone): TDBObjectList;
+ function DbObjectsCached(db: String): Boolean;
+ function ParseDateTime(Str: String): TDateTime;
+ function GetKeyColumns(Columns: TTableColumnList; Keys: TTableKeyList): TTableColumnList;
+ function ConnectionInfo: TStringList; virtual;
+ function GetLastResults: TDBQueryList; virtual;
+ function GetCreateCode(Obj: TDBObject): String; virtual;
+ procedure PrefetchCreateCode(Objects: TDBObjectList);
+ function GetSessionVariables(Refresh: Boolean): TDBQuery;
+ function GetSessionVariable(VarName: String; DefaultValue: String=''; Refresh: Boolean=False): String;
+ function MaxAllowedPacket: Int64; virtual;
+ function GetSQLSpecifity(Specifity: TSQLSpecifityId): String; overload;
+ function GetSQLSpecifity(Specifity: TSQLSpecifityId; const Args: array of const): String; overload;
+ function GetDateTimeValue(Input: String; Datatype: TDBDatatypeIndex): String;
+ procedure ClearDbObjects(db: String);
+ procedure ClearAllDbObjects;
+ procedure ParseViewStructure(CreateCode: String; DBObj: TDBObject;
+ var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String);
+ procedure ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList);
+ procedure PurgePrefetchResults;
+ function GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean; Identifier: String=''): TDBDatatype;
+ function GetDatatypeByNativeType(NativeType: Integer; Identifier: String=''): TDBDatatype;
+ function ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Int64): String;
+ function LikeClauseTail: String;
+ property Parameters: TConnectionParameters read FParameters write FParameters;
+ property ThreadId: Int64 read GetThreadId;
+ property ConnectionUptime: Integer read GetConnectionUptime;
+ property ServerUptime: Integer read GetServerUptime;
+ property ServerNow: TDateTime read GetServerNow;
+ property CharacterSet: String read GetCharacterSet write SetCharacterSet;
+ property LastErrorCode: Cardinal read GetLastErrorCode;
+ property LastErrorMsg: String read GetLastErrorMsg;
+ property ServerOS: String read FServerOS;
+ property ServerVersionUntouched: String read FServerVersionUntouched;
+ property ColumnCache: TColumnCache read FColumnCache;
+ property KeyCache: TKeyCache read FKeyCache;
+ property ForeignKeyCache: TForeignKeyCache read FForeignKeyCache;
+ property CheckConstraintCache: TCheckConstraintCache read FCheckConstraintCache;
+ property QuoteChar: Char read FQuoteChar;
+ property QuoteChars: String read FQuoteChars;
+ function ServerVersionStr: String;
+ function ServerVersionInt: Integer;
+ function NdbClusterVersionInt: Integer;
+ property RowsFound: Int64 read FRowsFound;
+ property RowsAffected: Int64 read FRowsAffected;
+ property WarningCount: Cardinal read FWarningCount;
+ procedure ShowWarnings; virtual;
+ property LastQueryDuration: Cardinal read FLastQueryDuration;
+ property LastQueryNetworkDuration: Cardinal read FLastQueryNetworkDuration;
+ property IsUnicode: Boolean read FIsUnicode;
+ property IsSSL: Boolean read FIsSSL;
+ property AllDatabases: TStringList read GetAllDatabases;
+ property TableEngines: TStringList read GetTableEngines;
+ property TableEngineDefault: String read FTableEngineDefault;
+ property CollationTable: TDBQuery read GetCollationTable;
+ property CollationList: TStringList read GetCollationList;
+ property CharsetTable: TDBQuery read GetCharsetTable;
+ property CharsetList: TStringList read GetCharsetList;
+ property InformationSchemaObjects: TStringList read FInformationSchemaObjects;
+ function ResultCount: Integer;
+ property CurrentUserHostCombination: String read GetCurrentUserHostCombination;
+ property AllUserHostCombinations: TStringList read GetAllUserHostCombinations;
+ function IsLockedByThread: Boolean;
+ procedure SetLockedByThread(Value: TThread); virtual;
+ property Datatypes: TDBDataTypeArray read FDatatypes;
+ property Favorites: TStringList read FFavorites;
+ property InfSch: String read FInfSch;
+ function GetLockedTableCount(db: String): Integer;
+ function IdentifierEquals(Ident1, Ident2: String): Boolean;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; virtual;
+ function GetTableKeys(Table: TDBObject): TTableKeyList; virtual;
+ function GetTableForeignKeys(Table: TDBObject): TForeignKeyList; virtual;
+ function GetTableCheckConstraints(Table: TDBObject): TCheckConstraintList; virtual;
+ property MaxRowsPerInsert: Int64 read FMaxRowsPerInsert;
+ property SQLFunctions: TSQLFunctionList read FSQLFunctions;
+ function IsNumeric(Text: String): Boolean;
+ function IsHex(Text: String): Boolean;
+ function Has(Item: TFeatureOrRequirement): Boolean;
+ published
+ property Active: Boolean read FActive write SetActive default False;
+ property Database: String read FDatabase write SetDatabase;
+ property LogPrefix: String read FLogPrefix write FLogPrefix;
+ property OnLog: TDBLogEvent read FOnLog write FOnLog;
+ property OnConnected: TDBEvent read FOnConnected write FOnConnected;
+ property OnDatabaseChanged: TDBEvent read FOnDatabaseChanged write FOnDatabaseChanged;
+ property OnObjectnamesChanged: TDBEvent read FOnObjectnamesChanged write FOnObjectnamesChanged;
end;
TDBConnectionList = TObjectList;
+ { TMySQLConnection }
+
+ TMySQLRawResults = Array of PMYSQL_RES;
+ TMySQLConnection = class(TDBConnection)
+ private
+ FHandle: PMYSQL;
+ FLib: TMySQLLib;
+ FLastRawResults: TMySQLRawResults;
+ FStatementNum: Cardinal;
+ procedure SetActive(Value: Boolean); override;
+ procedure SetOption(Option: Integer; Arg: PAnsiChar);
+ procedure DoBeforeConnect; override;
+ procedure DoAfterConnect; override;
+ function GetThreadId: Int64; override;
+ function GetCharacterSet: String; override;
+ procedure SetCharacterSet(CharsetName: String); override;
+ function GetLastErrorCode: Cardinal; override;
+ function GetLastErrorMsg: String; override;
+ function GetAllDatabases: TStringList; override;
+ function GetTableEngines: TStringList; override;
+ function GetCollationTable: TDBQuery; override;
+ function GetCharsetTable: TDBQuery; override;
+ function GetCreateViewCode(Database, Name: String): String;
+ function GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64; override;
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ property Lib: TMySQLLib read FLib;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override;
+ function Ping(Reconnect: Boolean): Boolean; override;
+ function ConnectionInfo: TStringList; override;
+ function GetCreateCode(Obj: TDBObject): String; override;
+ property LastRawResults: TMySQLRawResults read FLastRawResults;
+ function MaxAllowedPacket: Int64; override;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; override;
+ function GetTableKeys(Table: TDBObject): TTableKeyList; override;
+ procedure ShowWarnings; override;
+ procedure SetLockedByThread(Value: TThread); override;
+ end;
+
+ {TAdoRawResults = Array of String; // _RecordSet;
+ TAdoDBConnection = class(TDBConnection)
+ private
+ FAdoHandle: TStringList; // TAdoConnection;
+ FLastRawResults: TAdoRawResults;
+ FLastError: String;
+ procedure SetActive(Value: Boolean); override;
+ procedure DoAfterConnect; override;
+ function GetThreadId: Int64; override;
+ function GetLastErrorCode: Cardinal; override;
+ function GetLastErrorMsg: String; override;
+ function GetAllDatabases: TStringList; override;
+ function GetCollationTable: TDBQuery; override;
+ function GetCharsetTable: TDBQuery; override;
+ function GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64; override;
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override;
+ function Ping(Reconnect: Boolean): Boolean; override;
+ function ConnectionInfo: TStringList; override;
+ function GetLastResults: TDBQueryList; override;
+ property LastRawResults: TAdoRawResults read FLastRawResults;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; override;
+ function GetTableForeignKeys(Table: TDBObject): TForeignKeyList; override;
+ end;}
+
+ TPGRawResults = Array of PPGresult;
+ TPQerrorfields = (PG_DIAG_SEVERITY, PG_DIAG_SQLSTATE, PG_DIAG_MESSAGE_PRIMARY, PG_DIAG_MESSAGE_DETAIL, PG_DIAG_MESSAGE_HINT, PG_DIAG_STATEMENT_POSITION, PG_DIAG_INTERNAL_POSITION, PG_DIAG_INTERNAL_QUERY, PG_DIAG_CONTEXT, PG_DIAG_SOURCE_FILE, PG_DIAG_SOURCE_LINE, PG_DIAG_SOURCE_FUNCTION);
+ TPgConnection = class(TDBConnection)
+ private
+ FHandle: PPGconn;
+ FLib: TPostgreSQLLib;
+ FLastRawResults: TPGRawResults;
+ FRegClasses: TOidStringPairs;
+ procedure SetActive(Value: Boolean); override;
+ procedure DoBeforeConnect; override;
+ procedure DoAfterConnect; override;
+ function GetThreadId: Int64; override;
+ procedure SetCharacterSet(CharsetName: String); override;
+ function GetLastErrorCode: Cardinal; override;
+ function GetLastErrorMsg: String; override;
+ function GetAllDatabases: TStringList; override;
+ function GetCharsetTable: TDBQuery; override;
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override;
+ procedure Drop(Obj: TDBObject); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ property Lib: TPostgreSQLLib read FLib;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override;
+ function Ping(Reconnect: Boolean): Boolean; override;
+ function ConnectionInfo: TStringList; override;
+ function GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64; override;
+ property LastRawResults: TPGRawResults read FLastRawResults;
+ property RegClasses: TOidStringPairs read FRegClasses;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; override;
+ function GetTableKeys(Table: TDBObject): TTableKeyList; override;
+ function GetTableForeignKeys(Table: TDBObject): TForeignKeyList; override;
+ end;
+
+ TSQLiteConnection = class;
+ TSQLiteGridRows = class(TGridRows)
+ private
+ FConnection: TSQLiteConnection;
+ public
+ Statement: Psqlite3_stmt; // Used for querying result structures
+ constructor Create(AOwner: TSQLiteConnection);
+ destructor Destroy; override;
+ end;
+ TSQLiteRawResults = Array of TSQLiteGridRows;
+ TSQLiteConnection = class(TDBConnection)
+ private
+ FHandle: Psqlite3;
+ FLib: TSQLiteLib;
+ FLastRawResults: TSQLiteRawResults;
+ FMainDbName: UTF8String;
+ procedure SetActive(Value: Boolean); override;
+ procedure DoBeforeConnect; override;
+ function GetThreadId: Int64; override;
+ function GetLastErrorCode: Cardinal; override;
+ function GetLastErrorMsg: String; override;
+ function GetAllDatabases: TStringList; override;
+ function GetCollationList: TStringList; override;
+ function GetCharsetTable: TDBQuery; override;
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ property Lib: TSQLiteLib read FLib;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override;
+ function Ping(Reconnect: Boolean): Boolean; override;
+ function GetCreateCode(Obj: TDBObject): String; override;
+ property LastRawResults: TSQLiteRawResults read FLastRawResults;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; override;
+ function GetTableKeys(Table: TDBObject): TTableKeyList; override;
+ function GetTableForeignKeys(Table: TDBObject): TForeignKeyList; override;
+ end;
+
+ {TInterbaseRawResults = Array of String; // TFDQuery;
+ TIbDrivers = TDictionary; //TFDPhysIBDriverLink>;
+ TFbDrivers = TDictionary; //TFDPhysFBDriverLink>;
+ TInterbaseConnection = class(TDBConnection)
+ private
+ FFDHandle: TObject; // TFDConnection;
+ FLastError: String;
+ FLastErrorCode: Integer;
+ FLastRawResults: TInterbaseRawResults;
+ class var FIbDrivers: TStringList; // TIbDrivers;
+ class var FFbDrivers: TStringList; // TFbDrivers;
+ procedure SetActive(Value: Boolean); override;
+ procedure DoBeforeConnect; override;
+ function GetThreadId: Int64; override;
+ procedure OnFdError(ASender: TObject; AInitiator: TObject; var AException: Exception);
+ function GetLastErrorCode: Cardinal; override;
+ function GetLastErrorMsg: String; override;
+ function GetAllDatabases: TStringList; override;
+ function GetCollationTable: TDBQuery; override;
+ function GetCharsetTable: TDBQuery; override;
+ procedure FetchDbObjects(db: String; var Cache: TDBObjectList); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL); override;
+ function Ping(Reconnect: Boolean): Boolean; override;
+ function GetCreateCode(Obj: TDBObject): String; override;
+ property LastRawResults: TInterbaseRawResults read FLastRawResults;
+ function GetTableColumns(Table: TDBObject): TTableColumnList; override;
+ function GetTableKeys(Table: TDBObject): TTableKeyList; override;
+ function GetTableForeignKeys(Table: TDBObject): TForeignKeyList; override;
+ end;}
+
+
+ { TDBQuery }
+
+ TDBQuery = class(TComponent)
+ private
+ FSQL: String;
+ FConnection: TDBConnection;
+ FRecNo,
+ FRecordCount: Int64;
+ FColumnNames: TStringList;
+ FColumnOrgNames: TStringList;
+ FAutoIncrementColumn: Integer;
+ FColumnTypes: Array of TDBDatatype;
+ FColumnLengths: TIntegerDynArray;
+ FColumnFlags: TCardinalDynArray;
+ FCurrentUpdateRow: TGridRow;
+ FEof: Boolean;
+ FStoreResult: Boolean;
+ FColumns: TTableColumnList;
+ FKeys: TTableKeyList;
+ FForeignKeys: TForeignKeyList;
+ FEditingPrepared: Boolean;
+ FUpdateData: TGridRows;
+ FDBObject: TDBObject;
+ FFormatSettings: TFormatSettings;
+ procedure SetRecNo(Value: Int64); virtual; abstract;
+ function ColumnExists(Column: Integer): Boolean; overload;
+ function ColumnExists(ColumnName: String): Boolean; overload;
+ procedure SetColumnOrgNames(Value: TStringList);
+ procedure SetDBObject(Value: TDBObject);
+ procedure CreateUpdateRow;
+ function GetKeyColumns: TTableColumnList;
+ function GridQuery(QueryType, QueryBody: String): String;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); virtual; abstract;
+ procedure LogMetaInfo(NumResult: Integer);
+ procedure First;
+ procedure Next;
+ function ColumnCount: Integer;
+ function GetColBinData(Column: Integer; var baData: TBytes): Boolean; virtual;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; virtual; abstract;
+ function Col(ColumnName: String; IgnoreErrors: Boolean=False): String; overload;
+ function ColumnLengths(Column: Integer): Int64; virtual;
+ function HexValue(Column: Integer; IgnoreErrors: Boolean=False): String;
+ function DataType(Column: Integer): TDBDataType;
+ function MaxLength(Column: Integer): Int64;
+ function ValueList(Column: Integer): TStringList;
+ // Todo: overload ColumnExists:
+ function ColExists(Column: String): Boolean;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; virtual; abstract;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; virtual; abstract;
+ function ColIsKeyPart(Column: Integer): Boolean; virtual; abstract;
+ function ColIsVirtual(Column: Integer): Boolean;
+ function ColAttributes(Column: Integer): TTableColumn;
+ function IsNull(Column: Integer): Boolean; overload; virtual; abstract;
+ function IsNull(Column: String): Boolean; overload;
+ function IsFunction(Column: Integer): Boolean;
+ function HasResult: Boolean; virtual; abstract;
+ function GetWhereClause: String;
+ procedure CheckEditable;
+ function IsEditable: Boolean;
+ procedure DeleteRow;
+ function InsertRow: Int64;
+ procedure SetCol(Column: Integer; NewText: String; Null: Boolean; IsFunction: Boolean);
+ function EnsureFullRow(Refresh: Boolean): Boolean;
+ function HasFullData: Boolean;
+ function Modified(Column: Integer): Boolean; overload;
+ function Modified: Boolean; overload;
+ function Inserted: Boolean;
+ function SaveModifications: Boolean;
+ function DatabaseName: String; virtual; abstract;
+ function TableName: String; overload;
+ function TableName(Column: Integer): String; overload; virtual; abstract;
+ function ResultName: String;
+ function QuotedDbAndTableName: String;
+ procedure DiscardModifications;
+ procedure PrepareColumnAttributes;
+ procedure PrepareEditing;
+ property RecNo: Int64 read FRecNo write SetRecNo;
+ property Eof: Boolean read FEof;
+ property RecordCount: Int64 read FRecordCount;
+ property ColumnNames: TStringList read FColumnNames;
+ property StoreResult: Boolean read FStoreResult write FStoreResult;
+ property ColumnOrgNames: TStringList read FColumnOrgNames write SetColumnOrgNames;
+ property AutoIncrementColumn: Integer read FAutoIncrementColumn;
+ property DBObject: TDBObject read FDBObject write SetDBObject;
+ property SQL: String read FSQL write FSQL;
+ property Connection: TDBConnection read FConnection;
+ end;
+ PDBQuery = ^TDBQuery;
+
+ { TMySQLQuery }
+
+ TMySQLQuery = class(TDBQuery)
+ private
+ FConnection: TMySQLConnection;
+ FResultList: TMySQLRawResults;
+ FCurrentResults: PMYSQL_RES;
+ FCurrentRow: PMYSQL_ROW;
+ procedure SetRecNo(Value: Int64); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); override;
+ function GetColBinData(Column: Integer; var baData: TBytes): Boolean; override;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; override;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; override;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; override;
+ function ColIsKeyPart(Column: Integer): Boolean; override;
+ function IsNull(Column: Integer): Boolean; overload; override;
+ function HasResult: Boolean; override;
+ function DatabaseName: String; override;
+ function TableName(Column: Integer): String; overload; override;
+ end;
+
+ {TAdoDBQuery = class(TDBQuery)
+ private
+ FCurrentResults: TStringList; //TAdoQuery;
+ FResultList: Array of String; // TAdoQuery;
+ procedure SetRecNo(Value: Int64); override;
+ public
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); override;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; override;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; override;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; override;
+ function ColIsKeyPart(Column: Integer): Boolean; override;
+ function IsNull(Column: Integer): Boolean; overload; override;
+ function HasResult: Boolean; override;
+ function DatabaseName: String; override;
+ function TableName(Column: Integer): String; overload; override;
+ end;}
+
+ TPGQuery = class(TDBQuery)
+ private
+ FConnection: TPgConnection;
+ FCurrentResults: PPGresult;
+ FRecNoLocal: Integer;
+ FResultList: TPGRawResults;
+ procedure SetRecNo(Value: Int64); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); override;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; override;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; override;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; override;
+ function ColIsKeyPart(Column: Integer): Boolean; override;
+ function IsNull(Column: Integer): Boolean; overload; override;
+ function HasResult: Boolean; override;
+ function DatabaseName: String; override;
+ function TableName(Column: Integer): String; overload; override;
+ end;
+
+ TSQLiteQuery = class(TDBQuery)
+ private
+ FConnection: TSQLiteConnection;
+ FCurrentResults: TSQLiteGridRows;
+ FRecNoLocal: Integer;
+ FResultList: TSQLiteRawResults;
+ procedure SetRecNo(Value: Int64); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); override;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; override;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; override;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; override;
+ function ColIsKeyPart(Column: Integer): Boolean; override;
+ function IsNull(Column: Integer): Boolean; overload; override;
+ function HasResult: Boolean; override;
+ function DatabaseName: String; override;
+ function TableName(Column: Integer): String; overload; override;
+ end;
+
+ {TInterbaseQuery = class(TDBQuery)
+ private
+ FConnection: TInterbaseConnection;
+ FCurrentResults: TStringList; //TFDDataSet;
+ FRecNoLocal: Integer;
+ FResultList: TInterbaseRawResults;
+ procedure SetRecNo(Value: Int64); override;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure Execute(AddResult: Boolean=False; UseRawResult: Integer=-1); override;
+ function Col(Column: Integer; IgnoreErrors: Boolean=False): String; overload; override;
+ function ColIsPrimaryKeyPart(Column: Integer): Boolean; override;
+ function ColIsUniqueKeyPart(Column: Integer): Boolean; override;
+ function ColIsKeyPart(Column: Integer): Boolean; override;
+ function IsNull(Column: Integer): Boolean; overload; override;
+ function HasResult: Boolean; override;
+ function DatabaseName: String; override;
+ function TableName(Column: Integer): String; overload; override;
+ end;}
+
+procedure SQLite_CollationNeededCallback(userData:Pointer; ppDb:Psqlite3; eTextRep:integer; zName:PAnsiChar); cdecl;
+function SQLite_Collation(userData: Pointer; lenA: Integer; strA: PAnsiChar; lenB: Integer; strB: PAnsiChar): Integer; cdecl;
+
+function mysql_authentication_dialog_ask(
+ Handle: PMYSQL;
+ _type: Integer;
+ prompt: PAnsiChar;
+ buf: PAnsiChar;
+ buf_len: Integer
+ ): PAnsiChar; cdecl;
+
+{exports
+ mysql_authentication_dialog_ask;}
+
+
+{$I const.inc}
+
+
+
implementation
-end.
+uses apphelpers, loginform, change_password;
+
+
+{ TProcessPipe }
+
+constructor TProcessPipe.Create;
+var
+ Success: Boolean;
+begin
+ inherited;
+ {Success := CreatePipe(ReadHandle, WriteHandle, nil, 8192);
+ if Success then
+ Success := DuplicateHandle(
+ GetCurrentProcess, ReadHandle,
+ GetCurrentProcess, @ReadHandle, 0, True,
+ DUPLICATE_CLOSE_SOURCE OR DUPLICATE_SAME_ACCESS
+ );
+ if Success then
+ Success := DuplicateHandle(
+ GetCurrentProcess, WriteHandle,
+ GetCurrentProcess, @WriteHandle, 0, True,
+ DUPLICATE_CLOSE_SOURCE OR DUPLICATE_SAME_ACCESS
+ );}
+ if not Success then
+ raise EDbError.Create(_('Error creating I/O pipes'));
+end;
+
+
+destructor TProcessPipe.Destroy;
+begin
+ //CloseHandle(ReadHandle);
+ //CloseHandle(WriteHandle);
+ inherited;
+end;
+
+
+
+{ TSecureShellCmd }
+
+constructor TSecureShellCmd.Create(Connection: TDBConnection);
+begin
+ inherited Create;
+ FConnection := Connection;
+ FInPipe := TProcessPipe.Create;
+ FOutPipe := TProcessPipe.Create;
+ FErrorPipe := TProcessPipe.Create;
+end;
+
+
+destructor TSecureShellCmd.Destroy;
+begin
+ {FConnection.Log(lcInfo, f_('Closing SSH process #%d ...', [FProcessInfo.dwProcessId]));
+ TerminateProcess(FProcessInfo.hProcess, 0);
+ CloseHandle(FProcessInfo.hProcess);
+ CloseHandle(FProcessInfo.hThread);}
+ FInPipe.Free;
+ FOutPipe.Free;
+ FErrorPipe.Free;
+ inherited;
+end;
+
+
+procedure TSecureShellCmd.Connect;
+var
+ SshCmd, SshCmdDisplay, DialogTitle: String;
+ OutText, ErrorText, AllPipesText, UserInput: String;
+ rx: TRegExpr;
+ StartupInfo: TStartupInfo;
+ ExitCode: LongWord;
+ PortChecks: Integer;
+ CheckIntervalMs: Integer;
+ IsPlink: Boolean;
+ TimeStartedMs, WaitedMs, WaitedLeftMs, TimeOutMs: Int64;
+begin
+ // Check if local port is open
+ {PortChecks := 0;
+ while not PortOpen(FConnection.Parameters.SSHLocalPort) do begin
+ Inc(PortChecks);
+ if PortChecks >= 20 then
+ raise EDbError.CreateFmt(_('Could not execute SSH command: Port %d already in use.'), [FConnection.Parameters.SSHLocalPort]);
+ FConnection.Log(lcInfo, f_('Port #%d in use. Checking if #%d is available...', [FConnection.Parameters.SSHLocalPort, FConnection.Parameters.SSHLocalPort+1]));
+ FConnection.Parameters.SSHLocalPort := FConnection.Parameters.SSHLocalPort + 1;
+ end;}
+
+ // Build SSH command line
+ // plink bob@domain.com -pw myPassw0rd1 -P 22 -i "keyfile.pem" -L 55555:localhost:3306
+ IsPlink := ExecRegExprI('([pk]link|putty)', FConnection.Parameters.SSHExe);
+ SshCmd := FConnection.Parameters.SSHExe;
+ if IsPlink then
+ SshCmd := SshCmd + ' -ssh';
+ SshCmd := SshCmd + ' ';
+ if FConnection.Parameters.SSHUser.Trim <> '' then
+ SshCmd := SshCmd + FConnection.Parameters.SSHUser.Trim + '@';
+ if FConnection.Parameters.SSHHost.Trim <> '' then
+ SshCmd := SshCmd + FConnection.Parameters.SSHHost.Trim
+ else
+ SshCmd := SshCmd + FConnection.Parameters.Hostname;
+ if FConnection.Parameters.SSHPassword <> '' then begin
+ // Escape double quote with backslash, see issue #261
+ SshCmd := SshCmd + ' -pw "' + StringReplace(FConnection.Parameters.SSHPassword, '"', '\"', [rfReplaceAll]) + '"';
+ end;
+ if FConnection.Parameters.SSHPort > 0 then
+ SshCmd := SshCmd + IfThen(IsPlink, ' -P ', ' -p ') + IntToStr(FConnection.Parameters.SSHPort);
+ if FConnection.Parameters.SSHPrivateKey <> '' then
+ SshCmd := SshCmd + ' -i "' + FConnection.Parameters.SSHPrivateKey + '"';
+ SshCmd := SshCmd + ' -N -L ' + IntToStr(FConnection.Parameters.SSHLocalPort) + ':' + FConnection.Parameters.Hostname + ':' + IntToStr(FConnection.Parameters.Port);
+ rx := TRegExpr.Create;
+ rx.Expression := '(-pw\s+")[^"]*(")';
+ SshCmdDisplay := rx.Replace(SshCmd, '${1}******${2}', True);
+ FConnection.Log(lcInfo, f_('Attempt to create SSH process, waiting %ds for response ...', [FConnection.Parameters.SSHTimeout]));
+ FConnection.Log(lcInfo, SshCmdDisplay);
+
+ // Prepare process
+ {FillChar(StartupInfo, SizeOf(StartupInfo), 0);
+ StartupInfo.cb := SizeOf(StartupInfo);
+ StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
+ StartupInfo.wShowWindow := SW_HIDE;
+ StartupInfo.hStdInput:= FInPipe.ReadHandle;
+ StartupInfo.hStdError:= FErrorPipe.WriteHandle;
+ StartupInfo.hStdOutput:= FOutPipe.WriteHandle;
+
+ // Create plink.exe process
+ FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);
+ if not CreateProcess(
+ nil,
+ PChar(SshCmd),
+ nil,
+ nil,
+ true,
+ CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
+ nil,
+ PChar(GetCurrentDir),
+ StartupInfo,
+ FProcessInfo) then begin
+ ErrorText := CRLF + CRLF + SshCmdDisplay + CRLF + CRLF + 'System message: ' + SysErrorMessage(GetLastError);
+ ErrorText := f_('Could not execute SSH command: %s', [ErrorText]);
+ raise EDbError.Create(ErrorText);
+ end;
+
+ // Wait until timeout has finished.
+ // Todo: Find a way to wait only until connection is established
+ // Parse pipe output and probably show some message in a dialog.
+ WaitedMs := 0;
+ DialogTitle := ExtractFileName(FConnection.Parameters.SSHExe);
+ TimeOutMs := FConnection.Parameters.SSHTimeout * 1000;
+ CheckIntervalMs := FConnection.Parameters.SSHTimeout * 100;
+ TimeStartedMs := GetTickCount64;
+ while WaitedMs < TimeOutMs do begin
+ WaitForSingleObject(FProcessInfo.hProcess, CheckIntervalMs);
+ WaitedMs := GetTickCount64 - TimeStartedMs;
+ // On Wine, WaitForSingleObject does not really seem to wait. See #1771
+ WaitedLeftMs := TimeStartedMs + WaitedMs - GetTickCount64;
+ if WaitedLeftMs > 0 then begin
+ FConnection.Log(lcDebug, 'Wait additional '+WaitedLeftMs.ToString+'ms (see issue #1771)...');
+ Sleep(WaitedLeftMs);
+ end;
+ GetExitCodeProcess(FProcessInfo.hProcess, ExitCode);
+ if ExitCode <> STILL_ACTIVE then begin
+ FConnection.Log(lcError, 'SSH process exited after '+WaitedMs.ToString+'ms with code '+ExitCode.ToString+'. Should be '+STILL_ACTIVE.ToString+' (STILL_ACTIVE)');
+ raise EDbError.CreateFmt(_('SSH exited unexpected. Command line was: %s'), [CRLF+SshCmdDisplay]);
+ end;
+
+ OutText := Trim(ReadPipe(FOutPipe));
+ ErrorText := ReadPipe(FErrorPipe);
+ if (OutText <> '') or (ErrorText <> '') then begin
+ FConnection.Log(lcDebug, Format('SSH output after %d ms. OutPipe: "%s" ErrorPipe: "%s"', [WaitedMs, OutText, ErrorText]));
+ end;
+
+ if OutText <> '' then begin
+ // Prepend error text in the dialog, e.g. "Unable to use keyfile"
+ AllPipesText := OutText;
+ if not ErrorText.IsEmpty then begin
+ FConnection.Log(lcError, 'SSH: '+ErrorText);
+ AllPipesText := Trim('Error: ' + ErrorText + sLineBreak + AllPipesText);
+ end;
+ if ExecRegExpr('login as\s*\:', OutText) then begin
+ // Prompt for username
+ UserInput := InputBox(DialogTitle, AllPipesText, '');
+ SendText(UserInput + CRLF);
+ end else if ExecRegExpr('(password|Passphrase for key "[^"]+")\s*\:', OutText) then begin
+ // Prompt for sensitive input. Send * as first char of prompt param so InputBox hides input characters
+ UserInput := InputBox(DialogTitle, #31+AllPipesText, '');
+ SendText(UserInput + CRLF);
+ end else begin
+ // Informational message box
+ rx.Expression := '^[^\.]+\.';
+ if rx.Exec(OutText) then begin // First words end with a dot - use it as caption
+ MessageDialog(DialogTitle + ': ' + rx.Match[0], AllPipesText, mtInformation, [mbOK])
+ end else begin
+ MessageDialog(DialogTitle, AllPipesText, mtInformation, [mbOK]);
+ end;
+ end;
+ end
+
+ else if ErrorText <> '' then begin
+ rx.Expression := '([^\.]+\?)(\s*\(y\/n\s*(,[^\)]+)?\)\s*)$';
+ if rx.Exec(ErrorText) then begin
+ // Prompt user with question
+ case MessageDialog(Trim(rx.Match[1]), Copy(ErrorText, 1, Length(ErrorText)-rx.MatchLen[2]), mtConfirmation, [mbYes, mbNo, mbCancel]) of
+ mrYes:
+ SendText('y');
+ mrNo:
+ SendText('n');
+ mrCancel: begin
+ Destroy;
+ raise EDbError.Create(_('SSH command cancelled'));
+ end;
+ end;
+ end else if
+ ErrorText.StartsWith('Using username ', True) // see issue #577 - new plink version sends this informational text to error pipe
+ or ErrorText.StartsWith('Pre-authentication banner ', True) // see issue #704
+ or ErrorText.StartsWith('Access granted. Press Return to begin session', True) // see issue #1114
+ then begin
+ FConnection.Log(lcError, 'SSH: '+ErrorText);
+ SendText(CRLF);
+ end else begin
+ // Any other error message goes here.
+ if ErrorText.Contains('Access denied') then begin
+ // This is a final connection error - end loop in this case
+ Destroy;
+ raise EDbError.Create(ErrorText);
+ end else begin
+ // Just show error text and proceed looping
+ MessageDialog(DialogTitle, ErrorText, mtError, [mbOK]);
+ end;
+ end;
+ end;
+
+ // Crashes in TMainForm.DBtreeGetText:12, but most likely not required anyway:
+ //Application.ProcessMessages;
+ end; }
+ rx.Free;
+end;
+
+
+function TSecureShellCmd.ReadPipe(const Pipe: TProcessPipe): String;
+var
+ BufferReadCount, OutLen: Cardinal;
+ BytesRemaining: Cardinal;
+ Buffer: array [0..1023] of AnsiChar;
+ R: AnsiString;
+begin
+ Result := '';
+ {if Pipe.ReadHandle = INVALID_HANDLE_VALUE then
+ raise EDbError.Create(_('Error reading I/O pipes'));
+
+ // Check if there is data to read from stdout
+ PeekNamedPipe(Pipe.ReadHandle, nil, 0, nil, @BufferReadCount, nil);
+
+ if BufferReadCount <> 0 then begin
+ FillChar(Buffer, sizeof(Buffer), 'z');
+ // Read by 1024 bytes chunks
+ BytesRemaining := BufferReadCount;
+ OutLen := 0;
+ while BytesRemaining >= 1024 do begin
+ // Read stdout pipe
+ ReadFile(Pipe.ReadHandle, Buffer, 1024, BufferReadCount, nil);
+ Dec(BytesRemaining, BufferReadCount);
+
+ SetLength(R, OutLen + BufferReadCount);
+ Move(Buffer, R[OutLen + 1], BufferReadCount);
+ Inc(OutLen, BufferReadCount);
+ end;
+
+ if BytesRemaining > 0 then begin
+ ReadFile(Pipe.ReadHandle, Buffer, BytesRemaining, BufferReadCount, nil);
+ SetLength(R, OutLen + BufferReadCount);
+ Move(Buffer, R[OutLen + 1], BufferReadCount);
+ end;
+
+ R := AsciiToAnsi(R);
+ $WARNINGS OFF
+ Result := AnsiToUtf8(R);
+ $WARNINGS ON
+
+ Result := CleanEscSeq(Result);
+ end;}
+
+ Result := StringReplace(Result, #13+CRLF, CRLF, [rfReplaceAll]);
+end;
+
+
+function TSecureShellCmd.AsciiToAnsi(Text: AnsiString): AnsiString;
+const
+ cMaxLength = 255;
+var
+ PText: PAnsiChar;
+begin
+ Result := '';
+ {PText := AnsiStrAlloc(cMaxLength);
+ while Text <> '' do begin
+ System.AnsiStrings.StrPCopy(PText, copy(Text, 1, cMaxLength-1));
+ OemToAnsi(PText, PText);
+ Result := Result + System.AnsiStrings.StrPas(PText);
+ Delete(Text, 1, cMaxLength-1);
+ end;
+ System.AnsiStrings.StrDispose(PText);}
+end;
+
+
+function TSecureShellCmd.CleanEscSeq(const Buffer: String): String;
+var
+ i: Integer;
+ chr: Char;
+ EscFlag, Process: Boolean;
+ EscBuffer: String[80];
+begin
+ Result := '';
+ EscFlag := False;
+ for i:=1 to Length(Buffer) do begin
+ chr := buffer[I];
+ if EscFLag then begin
+ Process := False;
+ if (Length(EscBuffer) = 0) and CharInSet(Chr, ['D', 'M', 'E', 'H', '7', '8', '=', '>', '<']) then
+ Process := True
+ else if (Length(EscBuffer) = 1) and (EscBuffer[1] in ['(', ')', '*', '+']) then
+ Process := True
+ else if CharInSet(Chr, ['0'..'9', ';', '?', ' '])
+ or ((Length(EscBuffer) = 0) and CharInSet(chr, ['[', '(', ')', '*', '+']))
+ then begin
+ {$WARNINGS OFF}
+ EscBuffer := EscBuffer + Chr;
+ {$WARNINGS ON}
+ if Length(EscBuffer) >= High(EscBuffer) then begin
+ SysUtils.Beep;
+ EscBuffer := '';
+ EscFlag := FALSE;
+ end;
+ end else
+ Process := True;
+
+ if Process then begin
+ EscBuffer := '';
+ EscFlag := False;
+ end;
+ end else if chr = #27 then begin
+ EscBuffer := '';
+ EscFlag := True;
+ end;
+ Result := Result + chr;
+ end;
+end;
+
+
+procedure TSecureShellCmd.SendText(Text: String);
+var
+ WrittenBytes: Cardinal;
+ TextA: AnsiString;
+begin
+ {$WARNINGS OFF}
+ TextA := Utf8ToAnsi(Text);
+ {$WARNINGS ON}
+ //if TextA <> '' then
+ // WriteFile(FInPipe.WriteHandle, TextA[1], Length(TextA), WrittenBytes, nil);
+end;
+
+
+
+{ TConnectionParameters }
+
+constructor TConnectionParameters.Create;
+begin
+ inherited Create;
+ FIsFolder := False;
+
+ FNetType := TNetType(AppSettings.GetDefaultInt(asNetType));
+ FHostname := DefaultHost;
+ FLoginPrompt := AppSettings.GetDefaultBool(asLoginPrompt);
+ FWindowsAuth := AppSettings.GetDefaultBool(asWindowsAuth);
+ FCleartextPluginEnabled := AppSettings.GetDefaultBool(asCleartextPluginEnabled);
+ FUsername := DefaultUsername;
+ FPassword := AppSettings.GetDefaultString(asPassword);
+ FPort := DefaultPort;
+ FCompressed := AppSettings.GetDefaultBool(asCompressed);
+ FAllDatabases := AppSettings.GetDefaultString(asDatabases);
+ FLibraryOrProvider := DefaultLibrary;
+ FComment := AppSettings.GetDefaultString(asComment);
+
+ FSSHActive := DefaultSshActive;
+ FSSHExe := AppSettings.GetDefaultString(asSshExecutable);
+ FSSHHost := AppSettings.GetDefaultString(asSSHtunnelHost);
+ FSSHPort := AppSettings.GetDefaultInt(asSSHtunnelHostPort);
+ FSSHUser := AppSettings.GetDefaultString(asSSHtunnelUser);
+ FSSHPassword := AppSettings.GetDefaultString(asSSHtunnelPassword);
+ FSSHTimeout := AppSettings.GetDefaultInt(asSSHtunnelTimeout);
+ FSSHPrivateKey := AppSettings.GetDefaultString(asSSHtunnelPrivateKey);
+ FSSHLocalPort := FPort + 1;
+
+ FWantSSL := AppSettings.GetDefaultBool(asSSLActive);
+ FSSLPrivateKey := AppSettings.GetDefaultString(asSSLKey);
+ FSSLCertificate := AppSettings.GetDefaultString(asSSLCert);
+ FSSLCACertificate := AppSettings.GetDefaultString(asSSLCA);
+ FSSLCipher := AppSettings.GetDefaultString(asSSLCipher);
+ FSSLVerification := AppSettings.GetDefaultInt(asSSLVerification);
+ FStartupScriptFilename := AppSettings.GetDefaultString(asStartupScriptFilename);
+ FQueryTimeout := AppSettings.GetDefaultInt(asQueryTimeout);
+ FKeepAlive := AppSettings.GetDefaultInt(asKeepAlive);
+ FLocalTimeZone := AppSettings.GetDefaultBool(asLocalTimeZone);
+ FFullTableStatus := AppSettings.GetDefaultBool(asFullTableStatus);
+
+ FSessionColor := AppSettings.GetDefaultInt(asTreeBackground);
+ FIgnoreDatabasePattern := DefaultIgnoreDatabasePattern;
+ FLogFileDdl := AppSettings.GetDefaultBool(asLogFileDdl);
+ FLogFileDml := AppSettings.GetDefaultBool(asLogFileDml);
+ FLogFilePath := AppSettings.GetDefaultString(asLogFilePath);
+
+ FLastConnect := 0;
+ FCounter := 0;
+ FServerVersion := '';
+end;
+
+
+constructor TConnectionParameters.Create(SessionRegPath: String);
+var
+ DummyDate: TDateTime;
+begin
+ // Parameters from stored registry key
+ Create;
+
+ if not AppSettings.SessionPathExists(SessionRegPath) then
+ raise Exception.Create(f_('Error: Session "%s" not found in registry.', [SessionRegPath]));
+
+ FSessionPath := SessionRegPath;
+ AppSettings.SessionPath := SessionRegPath;
+
+ if AppSettings.ValueExists(asSessionFolder) then begin
+ FIsFolder := True;
+ end else begin
+ FSessionColor := AppSettings.ReadInt(asTreeBackground);
+ FNetType := TNetType(AppSettings.ReadInt(asNetType));
+ if (FNetType > High(TNetType)) or (FNetType < Low(TNetType)) then begin
+ ErrorDialog(f_('Unsupported "NetType" value (%d) found in settings for session "%s".', [Integer(FNetType), FSessionPath])
+ +CRLF+CRLF+
+ _('Loaded as MySQL/MariaDB session.')
+ );
+ FNetType := ntMySQL_TCPIP;
+ end;
+ FHostname := AppSettings.ReadString(asHost);
+ FUsername := AppSettings.ReadString(asUser);
+ FPassword := decrypt(AppSettings.ReadString(asPassword));
+ FLoginPrompt := AppSettings.ReadBool(asLoginPrompt);
+ FWindowsAuth := AppSettings.ReadBool(asWindowsAuth);
+ FCleartextPluginEnabled := AppSettings.ReadBool(asCleartextPluginEnabled);
+ FPort := MakeInt(AppSettings.ReadString(asPort));
+ FCompressed := AppSettings.ReadBool(asCompressed);
+ FAllDatabases := AppSettings.ReadString(asDatabases);
+ FLibraryOrProvider := AppSettings.ReadString(asLibrary, '', DefaultLibrary);
+ FComment := AppSettings.ReadString(asComment);
+
+ // Auto-activate SSH for sessions created before asSSHtunnelActive was introduced
+ FSSHActive := AppSettings.ReadBool(asSSHtunnelActive, '', DefaultSshActive);
+ FSSHExe := AppSettings.ReadString(asSshExecutable);
+ FSSHHost := AppSettings.ReadString(asSSHtunnelHost);
+ FSSHPort := AppSettings.ReadInt(asSSHtunnelHostPort);
+ FSSHUser := AppSettings.ReadString(asSSHtunnelUser);
+ FSSHPassword := decrypt(AppSettings.ReadString(asSSHtunnelPassword));
+ FSSHTimeout := AppSettings.ReadInt(asSSHtunnelTimeout);
+ if FSSHTimeout < 1 then
+ FSSHTimeout := 1;
+ FSSHPrivateKey := AppSettings.ReadString(asSSHtunnelPrivateKey);
+ FSSHLocalPort := AppSettings.ReadInt(asSSHtunnelPort);
+
+ FSSLPrivateKey := AppSettings.ReadString(asSSLKey);
+ // Auto-activate SSL for sessions created before UseSSL was introduced:
+ FWantSSL := AppSettings.ReadBool(asSSLActive, '', FSSLPrivateKey<>'');
+ FSSLCertificate := AppSettings.ReadString(asSSLCert);
+ FSSLCACertificate := AppSettings.ReadString(asSSLCA);
+ FSSLCipher := AppSettings.ReadString(asSSLCipher);
+ FSSLVerification := AppSettings.ReadInt(asSSLVerification);
+ FStartupScriptFilename := AppSettings.ReadString(asStartupScriptFilename);
+ FQueryTimeout := AppSettings.ReadInt(asQueryTimeout);
+ FKeepAlive := AppSettings.ReadInt(asKeepAlive);
+ FLocalTimeZone := AppSettings.ReadBool(asLocalTimeZone);
+ FFullTableStatus := AppSettings.ReadBool(asFullTableStatus);
+ FIgnoreDatabasePattern := AppSettings.ReadString(asIgnoreDatabasePattern);
+ FLogFileDdl := AppSettings.ReadBool(asLogFileDdl);
+ FLogFileDml := AppSettings.ReadBool(asLogFileDml);
+ FLogFilePath := AppSettings.ReadString(asLogFilePath);
+
+ FServerVersion := AppSettings.ReadString(asServerVersionFull);
+ DummyDate := 0;
+ FLastConnect := StrToDateTimeDef(AppSettings.ReadString(asLastConnect), DummyDate);
+ FCounter := AppSettings.ReadInt(asConnectCount);
+ AppSettings.ResetPath;
+
+ if FSSHExe.IsEmpty then begin
+ // Legacy support: was a global setting
+ // Globals must be read without session path
+ FSSHExe := AppSettings.ReadString(asPlinkExecutable);
+ end;
+ end;
+end;
+
+
+procedure TConnectionParameters.SaveToRegistry;
+var
+ IsNew: Boolean;
+begin
+ // Save current values to registry
+ IsNew := not AppSettings.SessionPathExists(FSessionPath);
+ AppSettings.SessionPath := FSessionPath;
+ if IsNew then
+ AppSettings.WriteString(asSessionCreated, DateTimeToStr(Now));
+ if FIsFolder then
+ AppSettings.WriteBool(asSessionFolder, True)
+ else begin
+ AppSettings.WriteString(asHost, FHostname);
+ AppSettings.WriteBool(asWindowsAuth, FWindowsAuth);
+ AppSettings.WriteBool(asCleartextPluginEnabled, FCleartextPluginEnabled);
+ AppSettings.WriteString(asUser, FUsername);
+ AppSettings.WriteString(asPassword, encrypt(FPassword));
+ AppSettings.WriteBool(asLoginPrompt, FLoginPrompt);
+ AppSettings.WriteString(asPort, IntToStr(FPort));
+ AppSettings.WriteInt(asNetType, Integer(FNetType));
+ AppSettings.WriteBool(asCompressed, FCompressed);
+ AppSettings.WriteBool(asLocalTimeZone, FLocalTimeZone);
+ AppSettings.WriteInt(asQueryTimeout, FQueryTimeout);
+ AppSettings.WriteInt(asKeepAlive, FKeepAlive);
+ AppSettings.WriteBool(asFullTableStatus, FFullTableStatus);
+ AppSettings.WriteString(asDatabases, FAllDatabases);
+ AppSettings.WriteString(asLibrary, FLibraryOrProvider);
+ AppSettings.WriteString(asComment, FComment);
+ AppSettings.WriteString(asStartupScriptFilename, FStartupScriptFilename);
+ AppSettings.WriteInt(asTreeBackground, FSessionColor);
+ AppSettings.WriteBool(asSSHtunnelActive, FSSHActive);
+ AppSettings.WriteString(asSshExecutable, FSSHExe);
+ AppSettings.WriteString(asSSHtunnelHost, FSSHHost);
+ AppSettings.WriteInt(asSSHtunnelHostPort, FSSHPort);
+ AppSettings.WriteString(asSSHtunnelUser, FSSHUser);
+ AppSettings.WriteString(asSSHtunnelPassword, encrypt(FSSHPassword));
+ AppSettings.WriteInt(asSSHtunnelTimeout, FSSHTimeout);
+ AppSettings.WriteString(asSSHtunnelPrivateKey, FSSHPrivateKey);
+ AppSettings.WriteInt(asSSHtunnelPort, FSSHLocalPort);
+ AppSettings.WriteBool(asSSLActive, FWantSSL);
+ AppSettings.WriteString(asSSLKey, FSSLPrivateKey);
+ AppSettings.WriteString(asSSLCert, FSSLCertificate);
+ AppSettings.WriteString(asSSLCA, FSSLCACertificate);
+ AppSettings.WriteString(asSSLCipher, FSSLCipher);
+ AppSettings.WriteInt(asSSLVerification, FSSLVerification);
+ AppSettings.WriteString(asIgnoreDatabasePattern, FIgnoreDatabasePattern);
+ AppSettings.WriteBool(asLogFileDdl, FLogFileDdl);
+ AppSettings.WriteBool(asLogFileDml, FLogFileDml);
+ AppSettings.WriteString(asLogFilePath, FLogFilePath);
+ AppSettings.ResetPath;
+ end;
+end;
+
+
+function TConnectionParameters.CreateConnection(AOwner: TComponent): TDBConnection;
+begin
+ case NetTypeGroup of
+ ngMySQL:
+ Result := TMySQLConnection.Create(AOwner);
+ //ngMSSQL:
+ // Result := TAdoDBConnection.Create(AOwner);
+ ngPgSQL:
+ Result := TPgConnection.Create(AOwner);
+ ngSQLite:
+ Result := TSQLiteConnection.Create(AOwner);
+ //ngInterbase:
+ // Result := TInterbaseConnection.Create(AOwner);
+ else
+ raise Exception.CreateFmt(_(MsgUnhandledNetType), [Integer(FNetType)]);
+ end;
+ Result.Parameters := Self;
+end;
+
+
+function TConnectionParameters.CreateQuery(Connection: TDbConnection): TDBQuery;
+begin
+ case NetTypeGroup of
+ ngMySQL:
+ Result := TMySQLQuery.Create(Connection);
+ //ngMSSQL:
+ // Result := TAdoDBQuery.Create(Connection);
+ ngPgSQL:
+ Result := TPGQuery.Create(Connection);
+ ngSQLite:
+ Result := TSQLiteQuery.Create(Connection);
+ //ngInterbase:
+ // Result := TInterbaseQuery.Create(Connection);
+ else
+ raise Exception.CreateFmt(_(MsgUnhandledNetType), [Integer(FNetType)]);
+ end;
+end;
+
+
+function TConnectionParameters.NetTypeName(LongFormat: Boolean): String;
+const
+ PrefixMysql = 'MariaDB or MySQL';
+ PrefixProxysql = 'ProxySQL Admin';
+ PrefixMssql = 'Microsoft SQL Server';
+ PrefixPostgres = 'PostgreSQL';
+ PrefixRedshift = 'Redshift PG';
+ PrefixSqlite = 'SQLite';
+ PrefixInterbase = 'Interbase';
+ PrefixFirebird = 'Firebird';
+begin
+ // Return the name of a net type, either in short or long format
+ Result := 'Unknown';
+
+ if LongFormat then begin
+ case FNetType of
+ ntMySQL_TCPIP: Result := PrefixMysql+' (TCP/IP)';
+ ntMySQL_NamedPipe: Result := PrefixMysql+' (named pipe)';
+ ntMySQL_SSHtunnel: Result := PrefixMysql+' (SSH tunnel)';
+ ntMySQL_ProxySQLAdmin: Result := PrefixProxysql+' (Experimental)';
+ ntMySQL_RDS: Result := 'MySQL on RDS';
+ ntMSSQL_NamedPipe: Result := PrefixMssql+' (named pipe)';
+ ntMSSQL_TCPIP: Result := PrefixMssql+' (TCP/IP)';
+ ntMSSQL_SPX: Result := PrefixMssql+' (SPX/IPX)';
+ ntMSSQL_VINES: Result := PrefixMssql+' (Banyan VINES)';
+ ntMSSQL_RPC: Result := PrefixMssql+' (Windows RPC)';
+ ntPgSQL_TCPIP: Result := PrefixPostgres+' (TCP/IP)';
+ ntPgSQL_SSHtunnel: Result := PrefixPostgres+' (SSH tunnel)';
+ ntSQLite: Result := PrefixSqlite;
+ ntSQLiteEncrypted: Result := PrefixSqlite+' (Encrypted)';
+ ntInterbase_TCPIP: Result := PrefixInterbase+' (TCP/IP, experimental)';
+ ntInterbase_Local: Result := PrefixInterbase+' (Local, experimental)';
+ ntFirebird_TCPIP: Result := PrefixFirebird+' (TCP/IP, experimental)';
+ ntFirebird_Local: Result := PrefixFirebird+' (Local, experimental)';
+ end;
+ end
+ else begin
+ case NetTypeGroup of
+ ngMySQL: begin
+ if IsMariaDB then Result := 'MariaDB'
+ else if IsPercona then Result := 'Percona'
+ else if IsTokudb then Result := 'TokuDB'
+ else if IsInfiniDB then Result := 'InfiniDB'
+ else if IsInfobright then Result := 'Infobright'
+ else if IsMemSQL then Result := 'MemSQL'
+ else if IsProxySQLAdmin then Result := 'ProxySQL Admin'
+ else if IsMySQL(True) then Result := 'MySQL'
+ else Result := PrefixMysql;
+ end;
+ ngMSSQL: Result := 'MS SQL';
+ ngPgSQL: begin
+ if IsRedshift then Result := PrefixRedshift
+ else Result := PrefixPostgres;
+ end;
+ ngSQLite: Result := PrefixSqlite;
+ ngInterbase: Result := PrefixInterbase;
+ end;
+ end;
+end;
+
+
+function TConnectionParameters.GetNetTypeGroup: TNetTypeGroup;
+begin
+ case FNetType of
+ ntMySQL_TCPIP, ntMySQL_NamedPipe, ntMySQL_SSHtunnel, ntMySQL_ProxySQLAdmin, ntMySQL_RDS:
+ Result := ngMySQL;
+ ntMSSQL_NamedPipe, ntMSSQL_TCPIP, ntMSSQL_SPX, ntMSSQL_VINES, ntMSSQL_RPC:
+ Result := ngMSSQL;
+ ntPgSQL_TCPIP, ntPgSQL_SSHtunnel:
+ Result := ngPgSQL;
+ ntSQLite, ntSQLiteEncrypted:
+ Result := ngSQLite;
+ ntInterbase_TCPIP, ntInterbase_Local, ntFirebird_TCPIP, ntFirebird_Local:
+ Result := ngInterbase;
+ else begin
+ // Return default net group here. Raising an exception lets the app die for some reason.
+ // Reproduction: click drop-down button on "Database(s)" session setting
+ //raise EDbError.CreateFmt(_(MsgUnhandledNetType), [Integer(FNetType)]);
+ Result := ngMySQL;
+ end;
+ end;
+end;
+
+
+function TConnectionParameters.SshSupport: Boolean;
+begin
+ Result := FNetType in [ntMySQL_SSHtunnel, ntMySQL_RDS, ntPgSQL_SSHtunnel, ntMSSQL_TCPIP];
+end;
+
+
+function TConnectionParameters.IsAnyMySQL: Boolean;
+begin
+ Result := NetTypeGroup = ngMySQL;
+end;
+
+
+function TConnectionParameters.IsAnyMSSQL: Boolean;
+begin
+ Result := NetTypeGroup = ngMSSQL;
+end;
+
+
+function TConnectionParameters.IsAnyPostgreSQL: Boolean;
+begin
+ Result := NetTypeGroup = ngPgSQL;
+end;
+
+
+function TConnectionParameters.IsAnySQLite;
+begin
+ Result := NetTypeGroup = ngSQLite;
+end;
+
+
+function TConnectionParameters.IsAnyInterbase;
+begin
+ Result := NetTypeGroup = ngInterbase;
+end;
+
+
+function TConnectionParameters.IsMariaDB: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('-mariadb', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsMySQL(StrictDetect: Boolean): Boolean;
+var
+ MajorVersionNum: String;
+begin
+ if StrictDetect then begin
+ MajorVersionNum := RegExprGetMatch('\b(\d+)\.\d+\.\d+', ServerVersion, 1);
+ Result := IsAnyMySQL and (not IsMariaDB) and (
+ (ContainsText(ServerVersion, 'mysql') or IsMySQLonRDS) // RDS is always MySQL, but does not contain "mysql"
+ or (StrToIntDef(MajorVersionNum, -1) in [3,4,5,8]) // MySQL 8.0 does not contain "mysql", but major version only exists in MySQL
+ );
+ end else begin
+ Result := IsAnyMySQL
+ and (not IsMariaDB)
+ and (not IsPercona)
+ and (not IsTokudb)
+ and (not IsInfiniDB)
+ and (not IsInfobright)
+ and (not IsProxySQLAdmin)
+ and (not IsMemSQL);
+ end;
+end;
+
+
+function TConnectionParameters.IsPercona: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('percona server', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsTokudb: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('tokudb', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsInfiniDB: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('infinidb', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsInfobright: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('infobright', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsProxySQLAdmin: Boolean;
+begin
+ Result := NetType = ntMySQL_ProxySQLAdmin;
+end;
+
+
+function TConnectionParameters.IsMySQLonRDS: Boolean;
+begin
+ Result := NetType = ntMySQL_RDS;
+end;
+
+
+function TConnectionParameters.IsAzure: Boolean;
+begin
+ Result := IsAnyMSSQL and (Pos('azure', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsMemSQL: Boolean;
+begin
+ Result := IsAnyMySQL and (Pos('memsql', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsRedshift: Boolean;
+begin
+ Result := IsAnyPostgreSQL and (Pos('redshift', LowerCase(ServerVersion)) > 0);
+end;
+
+
+function TConnectionParameters.IsInterbase: Boolean;
+begin
+ Result := NetType in [ntInterbase_TCPIP, ntInterbase_Local];
+end;
+
+
+function TConnectionParameters.IsFirebird: Boolean;
+begin
+ Result := NetType in [ntFirebird_TCPIP, ntFirebird_Local];
+end;
+
+
+function TConnectionParameters.GetImageIndex: Integer;
+begin
+ if IsFolder then
+ Result := 174
+ else case NetTypeGroup of
+ ngMySQL: begin
+ if IsPercona then Result := 169
+ else if IsTokudb then Result := 171
+ else if IsInfiniDB then Result := 172
+ else if IsInfobright then Result := 173
+ else if IsMemSQL then Result := 194
+ else if IsProxySQLAdmin then Result := 197
+ else if IsMySQLonRDS then Result := 205
+ else if IsMariaDB then Result := 166
+ else Result := 164;
+ end;
+ ngMSSQL: begin
+ Result := 123;
+ if IsAzure then Result := 188;
+ end;
+ ngPgSQL: begin
+ Result := 187;
+ if IsRedshift then Result := 195;
+ end;
+ ngSQLite: Result := 196;
+ ngInterbase: begin
+ Result := 203;
+ if IsFirebird then Result := 204;
+ end
+ else Result := ICONINDEX_SERVER;
+ end;
+end;
+
+
+function TConnectionParameters.DefaultPort: Integer;
+begin
+ case NetTypeGroup of
+ ngMySQL: begin
+ if IsProxySQLAdmin then
+ Result := 6032
+ else
+ Result := 3306;
+ end;
+ ngMSSQL: Result := 0; // => autodetection by driver (previously 1433)
+ ngPgSQL: Result := 5432;
+ ngInterbase: Result := 3050;
+ else Result := 0;
+ end;
+end;
+
+
+function TConnectionParameters.DefaultUsername: String;
+begin
+ case NetTypeGroup of
+ ngMySQL: Result := 'root';
+ ngMSSQL: Result := 'sa';
+ ngPgSQL: Result := 'postgres';
+ ngInterbase: Result := 'sysdba';
+ else Result := '';
+ end;
+end;
+
+
+function TConnectionParameters.DefaultLibrary: String;
+begin
+ Result := '';
+ case NetTypeGroup of
+ ngMySQL: Result := 'libmariadb.' + GetDynLibExtension;
+ ngMSSQL: Result := 'MSOLEDBSQL'; // Prefer MSOLEDBSQL provider on newer systems
+ ngPgSQL: Result := 'libpq.' + GetDynLibExtension;
+ ngSQLite: begin
+ if NetType = ntSQLite then
+ Result := 'sqlite3.' + GetDynLibExtension
+ else
+ Result := 'sqlite3mc.' + GetDynLibExtension;
+ end;
+ ngInterbase: begin
+ if IsInterbase then
+ Result := IfThen(GetExecutableBits=64, 'ibclient64.', 'gds32.') + GetDynLibExtension
+ else if IsFirebird then
+ Result := 'fbclient.' + GetDynLibExtension;
+ end
+ end;
+end;
+
+
+function TConnectionParameters.DefaultHost: string;
+begin
+ // See issue #1602: SQLite connecting to IP causes out-of-memory crash
+ Result := '';
+ case NetTypeGroup of
+ ngSQLite: Result := '';
+ else Result := '127.0.0.1';
+ end;
+end;
+
+
+function TConnectionParameters.DefaultIgnoreDatabasePattern: String;
+begin
+ case NetTypeGroup of
+ ngPgSQL: Result := '^pg_temp_\d';
+ else Result := '';
+ end;
+end;
+
+
+function TConnectionParameters.DefaultSshActive: Boolean;
+begin
+ Result := FNetType in [ntMySQL_SSHtunnel, ntMySQL_RDS, ntPgSQL_SSHtunnel];
+end;
+
+
+function TConnectionParameters.GetExternalCliArguments(Connection: TDBConnection; ReplacePassword: TThreeStateBoolean): String;
+var
+ Args: TStringList;
+begin
+ // for mysql(dump)
+ Args := TStringList.Create;
+ Result := '';
+ if WantSSL then
+ Args.Add('--ssl');
+ if not SSLPrivateKey.IsEmpty then
+ Args.Add('--ssl-key="'+SSLPrivateKey+'"');
+ if not SSLCertificate.IsEmpty then
+ Args.Add('--ssl-cert="'+SSLCertificate+'"');
+ if not SSLCACertificate.IsEmpty then
+ Args.Add('--ssl-ca="'+SSLCACertificate+'"');
+
+ case NetType of
+ ntMySQL_NamedPipe: begin
+ Args.Add('--pipe');
+ Args.Add('--socket="'+Hostname+'"');
+ end;
+ ntMySQL_SSHtunnel, ntMySQL_RDS: begin
+ Args.Add('--host="localhost"');
+ Args.Add('--port='+IntToStr(SSHLocalPort));
+ end;
+ else begin
+ Args.Add('--host="'+Hostname+'"');
+ Args.Add('--port='+IntToStr(Port));
+ end;
+ end;
+
+ Args.Add('--user="'+Username+'"');
+ if Password <> '' then begin
+ case ReplacePassword of
+ nbTrue: Args.Add('--password="***"');
+ nbFalse: Args.Add('--password="'+StringReplace(Password, '"', '\"', [rfReplaceAll])+'"');
+ nbUnset: Args.Add('--password'); // will prompt
+ end;
+ end;
+ if Compressed then
+ Args.Add('--compress');
+ if Assigned(Connection) and (Connection.Database <> '') then
+ Args.Add('--database="' + Connection.Database + '"');
+
+ Result := ' ' + Implode(' ', Args);
+ Args.Free;
+end;
+
+
+function TConnectionParameters.GetLibraries: TStringList;
+var
+ rx: TRegExpr;
+ DllPath, DllFile: String;
+ Dlls, FoundLibs, Providers: TStringList;
+ Provider: String;
+begin
+ if not Assigned(FLibraries) then begin
+ FLibraries := TNetTypeLibs.Create;
+ end;
+
+ if not FLibraries.ContainsKey(NetType) then begin
+ FoundLibs := TStringList.Create;
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ case NetTypeGroup of
+ ngMySQL:
+ rx.Expression := '^lib(mysql|mariadb).*\.' + GetDynLibExtension;
+ ngMSSQL: // Allow unsupported ADODB providers per registry hack
+ rx.Expression := IfThen(AppSettings.ReadBool(asAllProviders), '^', '^(MSOLEDBSQL|SQLOLEDB)');
+ ngPgSQL:
+ rx.Expression := '^libpq.*\.' + GetDynLibExtension;
+ ngSQLite: begin
+ if NetType = ntSQLite then
+ rx.Expression := '^sqlite.*\.' + GetDynLibExtension
+ else
+ rx.Expression := '^sqlite3mc.*\.' + GetDynLibExtension;
+ end;
+ ngInterbase:
+ rx.Expression := '^(gds32|ibclient|fbclient).*\.' + GetDynLibExtension;
+ end;
+ case NetTypeGroup of
+ ngMySQL, ngPgSQL, ngSQLite, ngInterbase: begin
+ Dlls := FindAllFiles(ExtractFilePath(ParamStr(0)), '*.' + GetDynLibExtension, False);
+ for DllPath in Dlls do begin
+ DllFile := ExtractFileName(DllPath);
+ if rx.Exec(DllFile) then begin
+ FoundLibs.Add(DllFile);
+ end;
+ end;
+ end;
+ ngMSSQL: begin
+ try
+ Providers := TStringList.Create;
+ {GetProviderNames(Providers);
+ for Provider in Providers do begin
+ if rx.Exec(Provider) then begin
+ FoundLibs.Add(Provider);
+ end;
+ end;}
+ Providers.Free;
+ except
+ //on E:EOleSysError do
+ // ErrorDialog('OLE provider names not available.' + sLineBreak + E.Message);
+ end;
+ end;
+ end;
+ rx.Free;
+ FLibraries.Add(NetType, FoundLibs);
+ end;
+ FLibraries.TryGetValue(NetType, Result);
+end;
+
+
+function TConnectionParameters.GetSessionName: String;
+var
+ LastBackSlash: Integer;
+begin
+ LastBackSlash := LastDelimiter('\', FSessionPath);
+ if LastBackSlash > 0 then
+ Result := Copy(FSessionPath, LastBackSlash+1, MaxInt)
+ else
+ Result := FSessionPath;
+end;
+
+
+function TConnectionParameters.GetAllDatabasesList: TStringList;
+var
+ rx: TRegExpr;
+ dbname: String;
+begin
+ Result := TStringList.Create;
+ if FAllDatabases <> '' then begin
+ rx := TRegExpr.Create;
+ rx.Expression := '[^;]+';
+ rx.ModifierG := True;
+ if rx.Exec(FAllDatabases) then while true do begin
+ // Add if not a duplicate
+ dbname := Trim(rx.Match[0]);
+ if Result.IndexOf(dbname) = -1 then
+ Result.Add(dbname);
+ if not rx.ExecNext then
+ break;
+ end;
+ rx.Free;
+ end;
+end;
+
+
+
+{ TMySQLConnection }
+
+constructor TDBConnection.Create(AOwner: TComponent);
+begin
+ inherited;
+ FParameters := TConnectionParameters.Create;
+ FRowsFound := 0;
+ FRowsAffected := 0;
+ FWarningCount := 0;
+ FConnectionStarted := 0;
+ FDatabase := '';
+ FLastQueryDuration := 0;
+ FLastQueryNetworkDuration := 0;
+ FThreadID := 0;
+ FLogPrefix := '';
+ FIsUnicode := True;
+ FSecureShellCmd := nil;
+ FIsSSL := False;
+ FDatabaseCache := TDatabaseCache.Create(True);
+ FColumnCache := TColumnCache.Create;
+ FKeyCache := TKeyCache.Create;
+ FForeignKeyCache := TForeignKeyCache.Create;
+ FCheckConstraintCache := TCheckConstraintCache.Create;
+ FLoginPromptDone := False;
+ FCurrentUserHostCombination := '';
+ FKeepAliveTimer := TTimer.Create(Self);
+ FFavorites := TStringList.Create;
+ FForeignKeyQueriesFailed := False;
+ // System database/schema, should be uppercase on MSSQL only, see #855
+ FInfSch := 'information_schema';
+ FInformationSchemaObjects := TStringList.Create;
+ FInformationSchemaObjects.CaseSensitive := False;
+ // Characters in identifiers which don't need to be quoted
+ FIdentCharsNoQuote := ['A'..'Z', 'a'..'z', '0'..'9', '_'];
+ FMaxRowsPerInsert := 10000;
+ FCaseSensitivity := 0;
+ FStringQuoteChar := '''';
+ FCollationTable := nil;
+end;
+
+
+constructor TMySQLConnection.Create(AOwner: TComponent);
+var
+ i: Integer;
+begin
+ inherited;
+ FQuoteChar := '`';
+ FQuoteChars := '`"';
+ FStatementNum := 0;
+ // The compiler complains that dynamic and static arrays are incompatible, so this does not work:
+ // FDatatypes := MySQLDatatypes
+ SetLength(FDatatypes, Length(MySQLDatatypes));
+ for i:=0 to High(MySQLDatatypes) do
+ FDatatypes[i] := MySQLDatatypes[i];
+end;
+
+
+{constructor TAdoDBConnection.Create(AOwner: TComponent);
+var
+ i: Integer;
+begin
+ inherited;
+ FQuoteChar := '"';
+ FQuoteChars := '"[]';
+ SetLength(FDatatypes, Length(MSSQLDatatypes));
+ for i:=0 to High(MSSQLDatatypes) do
+ FDatatypes[i] := MSSQLDatatypes[i];
+ FInfSch := 'INFORMATION_SCHEMA';
+ FMaxRowsPerInsert := 1000;
+end;}
+
+
+constructor TPgConnection.Create(AOwner: TComponent);
+var
+ i: Integer;
+begin
+ inherited;
+ FQuoteChar := '"';
+ FQuoteChars := '"';
+ SetLength(FDatatypes, Length(PostGreSQLDatatypes));
+ for i:=0 to High(PostGreSQLDatatypes) do
+ FDatatypes[i] := PostGreSQLDatatypes[i];
+ // cache for 123::regclass queries:
+ FRegClasses := TOidStringPairs.Create;
+ // Identifiers with uppercase characters must be quoted, see #1072
+ FIdentCharsNoQuote := ['a'..'z', '0'..'9', '_'];
+end;
+
+
+constructor TSQLiteConnection.Create(AOwner: TComponent);
+var
+ i: Integer;
+begin
+ inherited;
+ FQuoteChar := '"';
+ FQuoteChars := '"[]';
+ SetLength(FDatatypes, Length(SQLiteDatatypes));
+ for i:=0 to High(SQLiteDatatypes) do
+ FDatatypes[i] := SQLiteDatatypes[i];
+ // SQLite does not have IS:
+ FInfSch := '';
+end;
+
+
+{constructor TInterbaseConnection.Create(AOwner: TComponent);
+var
+ i: Integer;
+begin
+ inherited;
+ FQuoteChar := '"';
+ FQuoteChars := '"[]';
+ SetLength(FDatatypes, Length(InterbaseDatatypes));
+ for i:=0 to High(InterbaseDatatypes) do
+ FDatatypes[i] := InterbaseDatatypes[i];
+ // Interbase does not have IS:
+ FInfSch := '';
+end;}
+
+
+destructor TDBConnection.Destroy;
+begin
+ ClearCache(True);
+ FKeepAliveTimer.Free;
+ FFavorites.Free;
+ FInformationSchemaObjects.Free;
+ inherited;
+end;
+
+destructor TMySQLConnection.Destroy;
+begin
+ if Active then Active := False;
+ FLib.Free;
+ inherited;
+end;
+
+
+{destructor TAdoDBConnection.Destroy;
+begin
+ if Active then Active := False;
+ try
+ FreeAndNil(FAdoHandle);
+ except
+ on E:Exception do begin
+ // Destroy > ClearRefs > GetDataSetCount throws some error, but max in Delphi 11.2 yet
+ Log(lcError, E.Message);
+ end;
+ end;
+ inherited;
+end;}
+
+
+destructor TPgConnection.Destroy;
+begin
+ if Active then Active := False;
+ FRegClasses.Free;
+ FLib.Free;
+ inherited;
+end;
+
+
+destructor TSQLiteConnection.Destroy;
+begin
+ if Active then Active := False;
+ FLib.Free;
+ inherited;
+end;
+
+
+{destructor TInterbaseConnection.Destroy;
+begin
+ if Active then Active := False;
+ FreeAndNil(FFdHandle);
+ inherited;
+end;}
+
+
+function TDBConnection.GetDatatypeByName(var DataType: String; DeleteFromSource: Boolean; Identifier: String=''): TDBDatatype;
+var
+ i, MatchLen: Integer;
+ Match: Boolean;
+ rx: TRegExpr;
+ Types, tmp: String;
+ TypesSorted: TStringList;
+begin
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ MatchLen := 0;
+ for i:=0 to High(FDatatypes) do begin
+ Types := FDatatypes[i].Name;
+ if FDatatypes[i].Names <> '' then begin
+ Types := Types + '|' + FDatatypes[i].Names;
+ // Move more exact (longer) types to the beginning
+ TypesSorted := Explode('|', Types);
+ TypesSorted.CustomSort(StringListCompareByLength);
+ Types := Implode('|', TypesSorted);
+ TypesSorted.Free;
+ end;
+
+ rx.Expression := '^('+Types+')\b(\[\])?';
+ Match := rx.Exec(DataType);
+ // Prefer a later match which is longer than the one found before.
+ // See http://www.heidisql.com/forum.php?t=17061
+ if Match and (rx.MatchLen[1] > MatchLen) then begin
+ Log(lcDebug, 'GetDatatypeByName: "'+DataType+'" : '+rx.Match[1]);
+ if (FParameters.NetTypeGroup = ngPgSQL) and (rx.MatchLen[2] > 0) then begin
+ // TODO: detect array style datatypes, e.g. TEXT[]
+ end else begin
+ MatchLen := rx.MatchLen[1];
+ Result := FDatatypes[i];
+ end;
+ end;
+ end;
+
+ if (MatchLen > 0) and DeleteFromSource then begin
+ Delete(DataType, 1, MatchLen);
+ end;
+
+ if MatchLen = 0 then begin
+ // Fall back to unknown type
+ Result := Datatypes[0];
+ rx.Expression := '^(\S+)';
+ if rx.Exec(DataType) then
+ tmp := rx.Match[1]
+ else
+ tmp := DataType;
+ if Identifier <> '' then
+ Log(lcError, f_('Unknown datatype "%0:s" for "%1:s". Fall back to %2:s.', [tmp, Identifier, Result.Name]))
+ else
+ Log(lcError, f_('Unknown datatype "%0:s". Fall back to %1:s.', [tmp, Result.Name]));
+ end;
+ rx.Free;
+end;
+
+
+function TDBConnection.GetDatatypeByNativeType(NativeType: Integer; Identifier: String=''): TDBDatatype;
+var
+ i: Integer;
+ rx: TRegExpr;
+ TypeFound: Boolean;
+ TypeOid: String;
+begin
+ rx := TRegExpr.Create;
+ TypeFound := False;
+ for i:=0 to High(Datatypes) do begin
+ if Datatypes[i].NativeTypes = '?' then begin
+ // PG oid is set to be populated via '?'
+ Datatypes[i].NativeTypes := '';
+ try
+ TypeOid := GetVar('SELECT '+EscapeString(Datatypes[i].Name.ToLower)+'::regtype::oid');
+ if IsNumeric(TypeOid) then begin
+ Datatypes[i].NativeTypes := TypeOid;
+ Log(lcInfo, 'Found oid/NativeTypes of '+Datatypes[i].Name+' data type: '+Datatypes[i].NativeTypes);
+ end;
+ except
+ end;
+ end;
+ // Skip if native ids / oid's are (still) empty
+ if Datatypes[i].NativeTypes.IsEmpty then
+ Continue;
+ rx.Expression := '\b('+Datatypes[i].NativeTypes+')\b';
+ if rx.Exec(IntToStr(NativeType)) then begin
+ Result := Datatypes[i];
+ TypeFound := True;
+ break;
+ end;
+ end;
+
+ { Dynamically retrieve data type from pg_type.
+ Problematic because we would not know which TDBDatatypeIndex to assign.
+ if (not TypeFound) and Parameters.IsAnyPostgreSQL then begin
+ PgType := GetResults('SELECT * FROM '+QuoteIdent('pg_type')+' WHERE '+QuoteIdent('oid')+'='+NativeType.ToString);
+ if PgType.RecordCount = 1 then begin
+ SetLength(FDatatypes, Length(FDatatypes)+1);
+ end;
+ end;}
+
+ if not TypeFound then begin
+ // Fall back to unknown type
+ Result := Datatypes[0];
+ if Identifier <> '' then
+ Log(lcError, f_('Unknown datatype oid #%0:d for "%1:s". Fall back to %2:s.', [NativeType, Identifier, Result.Name]))
+ else
+ Log(lcError, f_('Unknown datatype oid #%0:d. Fall back to %1:s.', [NativeType, Result.Name]));
+ end;
+end;
+
+
+procedure TDBConnection.SetLockedByThread(Value: TThread);
+begin
+ FLockedByThread := Value;
+end;
+
+
+procedure TMySQLConnection.SetLockedByThread(Value: TThread);
+begin
+ if Value <> FLockedByThread then begin
+ if Value <> nil then begin
+ // We're running in a thread already. Ensure that Log() is able to detect that.
+ FLockedByThread := Value;
+ Log(lcDebug, 'mysql_thread_init, thread id #'+IntToStr(Value.ThreadID));
+ FLib.mysql_thread_init;
+ end else begin
+ FLib.mysql_thread_end;
+ Log(lcDebug, 'mysql_thread_end, thread id #'+IntToStr(FLockedByThread.ThreadID));
+ FLockedByThread := Value;
+ end;
+ end;
+end;
+
+function TDBConnection.IsLockedByThread: Boolean;
+begin
+ Result := FLockedByThread <> nil;
+end;
+
+
+{**
+ (Dis-)Connect to/from server
+}
+procedure TMySQLConnection.SetActive( Value: Boolean );
+var
+ Connected: PMYSQL;
+ ClientFlags, FinalPort, SSLoption: Integer;
+ VerifyServerCert: Byte;
+ Error, StatusName: String;
+ FinalHost, FinalSocket, FinalUsername, FinalPassword: String;
+ ErrorHint: String;
+ PluginDir, TlsVersions: AnsiString;
+ Status: TDBQuery;
+ PasswordChangeDialog: TfrmPasswordChange;
+ UserNameSize: DWORD;
+begin
+ if Value and (FHandle = nil) then begin
+
+ DoBeforeConnect;
+
+ // Get handle
+ FHandle := FLib.mysql_init(nil);
+
+ // Prepare special stuff for SSL and SSH tunnel
+ FinalHost := FParameters.Hostname;
+ FinalSocket := '';
+ FinalPort := FParameters.Port;
+
+ if FParameters.WantSSL then begin
+ // Define which TLS protocol versions are allowed.
+ // See https://www.heidisql.com/forum.php?t=27158
+ // See https://mariadb.com/kb/en/library/mysql_optionsv/
+ // See issue #1768
+ TlsVersions := 'TLSv1,TLSv1.1,TLSv1.2,TLSv1.3';
+ //TlsVersions := 'TLSv1.1';
+ if FLib.MARIADB_OPT_TLS_VERSION <> FLib.INVALID_OPT then
+ SetOption(FLib.MARIADB_OPT_TLS_VERSION, PAnsiChar(TlsVersions));
+ SetOption(FLib.MYSQL_OPT_TLS_VERSION, PAnsiChar(TlsVersions));
+ if FParameters.SSLPrivateKey <> '' then
+ SetOption(FLib.MYSQL_OPT_SSL_KEY, PAnsiChar(AnsiString(FParameters.SSLPrivateKey)));
+ if FParameters.SSLCertificate <> '' then
+ SetOption(FLib.MYSQL_OPT_SSL_CERT, PAnsiChar(AnsiString(FParameters.SSLCertificate)));
+ if FParameters.SSLCACertificate <> '' then
+ SetOption(FLib.MYSQL_OPT_SSL_CA, PAnsiChar(AnsiString(FParameters.SSLCACertificate)));
+ if FParameters.SSLCipher <> '' then
+ SetOption(FLib.MYSQL_OPT_SSL_CIPHER, PAnsiChar(AnsiString(FParameters.SSLCipher)));
+ if FLib.MYSQL_OPT_SSL_MODE <> TMySQLLib.INVALID_OPT then begin
+ // MySQL
+ Log(lcInfo, 'SSL parameters for MySQL');
+ case FParameters.SSLVerification of
+ 0: SSLoption := FLib.SSL_MODE_PREFERRED;
+ 1: SSLoption := FLib.SSL_MODE_VERIFY_CA;
+ 2: SSLoption := FLib.SSL_MODE_VERIFY_IDENTITY;
+ end;
+ SetOption(FLib.MYSQL_OPT_SSL_MODE, @SSLoption);
+ end
+ else begin
+ // MariaDB
+ Log(lcInfo, 'SSL parameters for MariaDB');
+ case FParameters.SSLVerification of
+ 0: VerifyServerCert := FLib.MYBOOL_FALSE;
+ 1,2: VerifyServerCert := FLib.MYBOOL_TRUE;
+ end;
+ SetOption(FLib.MYSQL_OPT_SSL_VERIFY_SERVER_CERT, @VerifyServerCert);
+ end;
+ end;
+
+ // libmariadb v3.4.0+ enables MYSQL_OPT_SSL_VERIFY_SERVER_CERT by default, so we have to disable it.
+ // See https://mariadb.com/kb/en/mariadb-connector-c-3-4-0-release-notes/
+ if not FParameters.WantSSL then begin
+ SetOption(FLib.MYSQL_OPT_SSL_VERIFY_SERVER_CERT, @(FLib.MYBOOL_FALSE));
+ end;
+
+ case FParameters.NetType of
+ ntMySQL_TCPIP, ntMySQL_ProxySQLAdmin: begin
+ end;
+
+ ntMySQL_NamedPipe: begin
+ FinalHost := '.';
+ FinalSocket := FParameters.Hostname;
+ end;
+
+ ntMySQL_SSHtunnel, ntMySQL_RDS: begin
+ StartSSHTunnel(FinalHost, FinalPort);
+ end;
+ end;
+
+ // User/Password
+ if FParameters.WindowsAuth then begin
+ // Send Windows system user name and blank password, see #991
+ {UserNameSize := 1024;
+ SetLength(FinalUsername, UserNameSize);
+ if GetUserName(PChar(FinalUsername), UserNameSize) then
+ SetLength(FinalUsername, UserNameSize-1)
+ else
+ RaiseLastOSError;}
+ FinalPassword := '';
+ end else begin
+ // Normal mode, send user specified user/password
+ FinalUsername := FParameters.Username;
+ FinalPassword := FParameters.Password;
+ end;
+
+ // Gather client options
+ ClientFlags := CLIENT_LOCAL_FILES
+ or CLIENT_INTERACTIVE
+ or CLIENT_PROTOCOL_41
+ or CLIENT_MULTI_STATEMENTS
+ or CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS
+ or CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA;
+ if Parameters.Compressed then
+ ClientFlags := ClientFlags or CLIENT_COMPRESS;
+ if Parameters.WantSSL then
+ ClientFlags := ClientFlags or CLIENT_SSL;
+
+ // Point libmysql to the folder with client plugins
+ PluginDir := AnsiString(ExtractFilePath(ParamStr(0))+'plugins');
+ SetOption(FLib.MYSQL_PLUGIN_DIR, PAnsiChar(PluginDir));
+
+ // Enable cleartext plugin
+ if Parameters.CleartextPluginEnabled then
+ SetOption(FLib.MYSQL_ENABLE_CLEARTEXT_PLUGIN, @(FLib.MYBOOL_TRUE));
+
+ // Tell server who we are
+ if Assigned(FLib.mysql_optionsv) then
+ FLib.mysql_optionsv(FHandle, FLib.MYSQL_OPT_CONNECT_ATTR_ADD, 'program_name', APPNAME);
+
+ // Seems to be still required on some systems, for importing CSV files
+ SetOption(FLib.MYSQL_OPT_LOCAL_INFILE, @(FLib.MYBOOL_TRUE));
+
+ // Ensure we have some connection timeout
+ SetOption(FLib.MYSQL_OPT_CONNECT_TIMEOUT, @(FParameters.QueryTimeout));
+
+ Connected := FLib.mysql_real_connect(
+ FHandle,
+ PAnsiChar(Utf8Encode(FinalHost)),
+ PAnsiChar(Utf8Encode(FinalUsername)),
+ PAnsiChar(Utf8Encode(FinalPassword)),
+ nil,
+ FinalPort,
+ PAnsiChar(Utf8Encode(FinalSocket)),
+ ClientFlags
+ );
+ if Connected = nil then begin
+ Error := LastErrorMsg;
+ Log(lcError, Error);
+ FConnectionStarted := 0;
+ FHandle := nil;
+ EndSSHTunnel;
+ if Error.Contains('SEC_E_ALGORITHM_MISMATCH') then begin
+ ErrorHint := f_('This is a known issue with older libraries. Try a newer %s in the session settings.',
+ ['libmysql']
+ );
+ end
+ else if Error.Contains('certificate verif') then begin
+ ErrorHint := _('You might need to lower the certificate verification in the SSL settings.');
+ end
+ else if (FParameters.DefaultLibrary <> '') and (FParameters.LibraryOrProvider <> FParameters.DefaultLibrary) then begin
+ ErrorHint := f_('You could try the default library %s in your session settings. (Current: %s)',
+ [FParameters.DefaultLibrary, FParameters.LibraryOrProvider]
+ );
+ end
+ else begin
+ ErrorHint := '';
+ end;
+ raise EDbError.Create(Error, LastErrorCode, ErrorHint);
+ end else begin
+ FActive := True;
+ // Catch late init_connect error by firing mysql_ping(), which detects a broken
+ // connection without running into some access violation. See issue #3464.
+ Ping(False);
+ if not FActive then
+ raise EDbError.CreateFmt(_('Connection closed immediately after it was established. '+
+ 'This is mostly caused by an "%s" server variable which has errors in itself, '+
+ 'or your user account does not have the required privileges for it to run.'+CRLF+CRLF+
+ 'You may ask someone with SUPER privileges'+CRLF+
+ '* either to fix the "%s" variable,'+CRLF+
+ '* or to grant you missing privileges.'),
+ ['init_connect', 'init_connect']);
+ // Try to fire the very first query against the server, which probably run into the following error:
+ // "Error 1820: You must SET PASSWORD before executing this statement"
+ try
+ ThreadId;
+ except
+ on E:EDbError do begin
+ if GetLastErrorCode = ER_MUST_CHANGE_PASSWORD then begin
+ PasswordChangeDialog := TfrmPasswordChange.Create(Self);
+ PasswordChangeDialog.lblHeading.Caption := GetLastErrorMsg;
+ PasswordChangeDialog.ShowModal;
+ if PasswordChangeDialog.ModalResult = mrOk then begin
+ if ExecRegExpr('\sALTER USER\s', GetLastErrorMsg) then
+ Query('ALTER USER USER() IDENTIFIED BY '+EscapeString(PasswordChangeDialog.editPassword.Text))
+ else
+ Query('SET PASSWORD=PASSWORD('+EscapeString(PasswordChangeDialog.editPassword.Text)+')');
+ end else // Dialog cancelled
+ Raise;
+ PasswordChangeDialog.Free;
+ end else
+ Raise;
+ end;
+ end;
+
+ // We need the server version before checking the current character set
+ FServerVersionUntouched := GetSessionVariable('version') + ' - ' + GetSessionVariable('version_comment');
+ FServerVersionUntouched := FServerVersionUntouched.Trim([' ', '-']);
+ if FServerVersionUntouched.IsEmpty then begin
+ FServerVersionUntouched := DecodeAPIString(FLib.mysql_get_server_info(FHandle));
+ end;
+ // mysql_character_set_name() reports utf8* if in fact we're on some latin* charset on v5.1 servers
+ // See https://www.heidisql.com/forum.php?t=39278
+ FIsUnicode := CharacterSet.StartsWith('utf', True) and (ServerVersionInt >= 50500);
+ if not IsUnicode then
+ try
+ CharacterSet := 'utf8mb4';
+ except
+ // older servers without *mb4 support go here
+ on E:EDbError do try
+ Log(lcError, E.Message);
+ CharacterSet := 'utf8';
+ except
+ // v5.1 returned "Unknown character set: 'utf8mb3'" with libmariadb
+ on E:EDbError do try
+ Log(lcError, E.Message);
+ Query('SET NAMES utf8');
+ except
+ // give up
+ on E:EDbError do
+ Log(lcError, E.Message);
+ end;
+ end;
+ end;
+ Log(lcInfo, _('Characterset')+': '+CharacterSet);
+ FConnectionStarted := GetTickCount div 1000;
+ FServerUptime := -1;
+ Status := GetResults(GetSQLSpecifity(spGlobalStatus));
+ while not Status.Eof do begin
+ StatusName := LowerCase(Status.Col(0));
+ if (StatusName = 'uptime') or (StatusName = 'proxysql_uptime') then
+ FServerUptime := StrToIntDef(Status.Col(1), FServerUptime)
+ else if StatusName = 'ssl_cipher' then
+ FIsSSL := Status.Col(1) <> '';
+ Status.Next;
+ end;
+ FServerDateTimeOnStartup := GetVar('SELECT ' + GetSQLSpecifity(spFuncNow));
+ FServerOS := GetSessionVariable('version_compile_os');
+ FRealHostname := GetSessionVariable('hostname');
+ FCaseSensitivity := MakeInt(GetSessionVariable('lower_case_table_names', IntToStr(FCaseSensitivity)));
+
+ // Triggers OnDatabaseChange event for
+ Database := '';
+ DoAfterConnect;
+ end;
+ end
+
+ else if (not Value) and (FHandle <> nil) then begin
+ try
+ FLib.mysql_close(FHandle);
+ except
+ on E:Exception do // sometimes fails with libmysql-6.1.dll, see #980
+ Log(lcError, 'Error while closing handle: '+E.Message);
+ end;
+ FActive := False;
+ ClearCache(False);
+ FConnectionStarted := 0;
+ FHandle := nil;
+ EndSSHTunnel;
+ Log(lcInfo, f_(MsgDisconnect, [FParameters.Hostname, DateTimeToStr(Now)]));
+ end;
+
+end;
+
+
+{procedure TAdoDBConnection.SetActive(Value: Boolean);
+var
+ Error, NetLib, DataSource, QuotedPassword, ServerVersion, ErrorHint: String;
+ FinalHost: String;
+ rx: TRegExpr;
+ FinalPort, i: Integer;
+ IsOldProvider: Boolean;
+begin
+ if Value then begin
+ DoBeforeConnect;
+ FinalHost := Parameters.Hostname;
+ FinalPort := Parameters.Port;
+ StartSSHTunnel(FinalHost, FinalPort);
+
+ try
+ // Creating the ADO object throws exceptions if MDAC is missing, especially on Wine
+ FAdoHandle := TStringList.Create; // TAdoConnection.Create(Owner);
+ except
+ on E:Exception do
+ raise EDbError.Create(E.Message+CRLF+CRLF+
+ _('On Wine, you can try to install MDAC:')+CRLF+
+ '> wget http://winetricks.org/winetricks'+CRLF+
+ '> chmod +x winetricks'+CRLF+
+ '> sh winetricks mdac28'+CRLF+
+ '> sh winetricks native_mdac');
+ end;
+
+ IsOldProvider := Parameters.LibraryOrProvider = 'SQLOLEDB';
+ if IsOldProvider then begin
+ MessageDialog(
+ f_('Security issue: Using %s %s with insecure %s.',
+ [Parameters.LibraryOrProvider, 'ADO provider', 'TLS 1.0']) +
+ f_('You should install %s from %s',
+ ['Microsoft OLE DB Driver', 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=56730']),
+ mtWarning, [mbOK]);
+ end;
+
+ NetLib := '';
+ case Parameters.NetType of
+ ntMSSQL_NamedPipe: NetLib := 'DBNMPNTW';
+ ntMSSQL_TCPIP: NetLib := 'DBMSSOCN';
+ ntMSSQL_SPX: NetLib := 'DBMSSPXN';
+ ntMSSQL_VINES: NetLib := 'DBMSVINN';
+ ntMSSQL_RPC: NetLib := 'DBMSRPCN';
+ end;
+
+ DataSource := FinalHost;
+ if (Parameters.NetType = ntMSSQL_TCPIP) and (FinalPort <> 0) then
+ DataSource := DataSource + ','+IntToStr(FinalPort);
+
+ // Quote password, just in case there is a semicolon or a double quote in it.
+ // See http://forums.asp.net/t/1957484.aspx?Passwords+ending+with+semi+colon+as+the+terminal+element+in+connection+strings+
+ if Pos('"', Parameters.Password) > 0 then
+ QuotedPassword := ''''+Parameters.Password+''''
+ else
+ QuotedPassword := '"'+Parameters.Password+'"';
+
+ FAdoHandle.ConnectionString := 'Provider='+Parameters.LibraryOrProvider+';'+
+ 'Password='+QuotedPassword+';'+
+ 'Persist Security Info=True;'+
+ 'User ID='+Parameters.Username+';'+
+ 'Network Library='+NetLib+';'+
+ 'Data Source='+DataSource+';'+
+ 'Application Name='+AppName+';'
+ ;
+ if Parameters.LibraryOrProvider.StartsWith('MSOLEDBSQL', true) then begin
+ // Issue #423: MSOLEDBSQL compatibility with new column types
+ // See https://docs.microsoft.com/en-us/sql/connect/oledb/applications/using-ado-with-oledb-driver-for-sql-server?view=sql-server-2017
+ // Do not use with old driver, see https://www.heidisql.com/forum.php?t=35208
+ FAdoHandle.ConnectionString := FAdoHandle.ConnectionString +
+ 'DataTypeCompatibility=80;';
+ end;
+
+ // Pass Database setting to connection string. Required on MS Azure?
+ if (not Parameters.AllDatabasesStr.IsEmpty) and (Pos(';', Parameters.AllDatabasesStr)=0) then
+ FAdoHandle.ConnectionString := FAdoHandle.ConnectionString + 'Database='+Parameters.AllDatabasesStr+';';
+
+ if Parameters.WindowsAuth then begin
+ if IsOldProvider then
+ FAdoHandle.ConnectionString := FAdoHandle.ConnectionString + 'Integrated Security=SSPI;'
+ else
+ FAdoHandle.ConnectionString := FAdoHandle.ConnectionString + 'Trusted_Connection=yes;'
+ end;
+
+ try
+ FAdoHandle.Connected := True;
+ FConnectionStarted := GetTickCount div 1000;
+ FActive := True;
+ // No need to set a charset for MS SQL
+ // CharacterSet := 'utf8';
+ // CurCharset := CharacterSet;
+ // Log(lcDebug, 'Characterset: '+CurCharset);
+ FAdoHandle.CommandTimeout := Parameters.QueryTimeout;
+ try
+ // Gracefully accept failure on MS Azure (SQL Server 11), which does not have a sysprocesses table
+ FServerUptime := StrToIntDef(GetVar('SELECT DATEDIFF(SECOND, '+QuoteIdent('login_time')+', CURRENT_TIMESTAMP) FROM '+QuoteIdent('master')+'.'+QuoteIdent('dbo')+'.'+QuoteIdent('sysprocesses')+' WHERE '+QuoteIdent('spid')+'=1'), -1);
+ except
+ FServerUptime := -1;
+ end;
+ FServerDateTimeOnStartup := GetVar('SELECT ' + GetSQLSpecifity(spFuncNow));
+ // Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (Intel X86)
+ // Apr 2 2010 15:53:02
+ // Copyright (c) Microsoft Corporation
+ // Express Edition with Advanced Services on Windows NT 6.1 (Build 7600: )
+ FServerVersionUntouched := Trim(GetVar('SELECT @@VERSION'));
+ rx := TRegExpr.Create;
+ rx.ModifierI := False;
+ // Extract server OS
+ rx.Expression := '\s+on\s+([^\r\n]+)';
+ if rx.Exec(FServerVersionUntouched) then
+ FServerOS := rx.Match[1];
+ // Cut at first line break
+ rx.Expression := '^([^\r\n]+)';
+ if rx.Exec(FServerVersionUntouched) then
+ FServerVersionUntouched := rx.Match[1];
+ try
+ // Try to get more exact server version to avoid displaying "20.14" in some cases
+ ServerVersion := GetVar('SELECT SERVERPROPERTY('+EscapeString('ProductVersion')+')');
+ if ExecRegExpr('(\d+)\.(\d+)\.(\d+)\.(\d+)', ServerVersion) then
+ FServerVersionUntouched := Copy(FServerVersionUntouched, 1, Pos(' - ', FServerVersionUntouched)+2) + ServerVersion;
+ except
+ // Above query only works on SQL Server 2008 and newer
+ // Keep value from SELECT @@VERSION on older servers
+ end;
+ rx.Free;
+ // See http://www.heidisql.com/forum.php?t=19779
+ Query('SET TEXTSIZE 2147483647');
+ FRealHostname := Parameters.Hostname;
+
+ // Show up dynamic connection properties, probably useful for debugging
+ for i:=0 to FAdoHandle.Properties.Count-1 do
+ Log(lcDebug, f_('OLE DB property "%s": %s', [FAdoHandle.Properties[i].Name, String(FAdoHandle.Properties[i].Value)]));
+
+ // Triggers OnDatabaseChange event for
+ Database := '';
+ DoAfterConnect;
+
+ // Reopen closed datasets after reconnecting
+ // ... does not work for some reason. Still getting "not allowed on a closed object" errors in grid.
+ //for i:=0 to FAdoHandle.DataSetCount-1 do
+ // FAdoHandle.DataSets[i].Open;
+
+ except
+ on E:Exception do begin
+ FLastError := E.Message;
+ Error := LastErrorMsg;
+ Log(lcError, Error);
+ FConnectionStarted := 0;
+ if (FParameters.DefaultLibrary <> '') and (FParameters.LibraryOrProvider <> FParameters.DefaultLibrary) then begin
+ ErrorHint := f_('You could try the default library %s in your session settings. (Current: %s)',
+ [FParameters.DefaultLibrary, FParameters.LibraryOrProvider]
+ );
+ end else begin
+ ErrorHint := '';
+ end;
+ raise EDbError.Create(Error, LastErrorCode, ErrorHint);
+ end;
+ end;
+ end else begin
+ //FAdoHandle.Connected := False;
+ FActive := False;
+ ClearCache(False);
+ FConnectionStarted := 0;
+ EndSSHTunnel;
+ Log(lcInfo, f_(MsgDisconnect, [FParameters.Hostname, DateTimeToStr(Now)]));
+ end;
+end; }
+
+
+procedure TPgConnection.SetActive(Value: Boolean);
+var
+ dbname, ConnectionString, OptionValue, Error: String;
+ ConnectOptions: TStringList;
+ FinalHost, ErrorHint: String;
+ FinalPort, i: Integer;
+begin
+ if Value then begin
+ DoBeforeConnect;
+ // Simon Riggs:
+ // "You should connect as "postgres" database by default, with an option to change. Don't use template1"
+ dbname := FParameters.AllDatabasesStr;
+ if dbname = '' then
+ dbname := 'postgres';
+
+ // Prepare special stuff for SSH tunnel
+ FinalHost := FParameters.Hostname;
+ FinalPort := FParameters.Port;
+
+ StartSSHTunnel(FinalHost, FinalPort);
+
+ // Compose connection string
+ ConnectOptions := TStringList.Create;
+ ConnectOptions.Duplicates := dupIgnore;
+ ConnectOptions
+ .AddPair('host', FinalHost)
+ .AddPair('port', IntToStr(FinalPort))
+ .AddPair('user', FParameters.Username)
+ .AddPair('password', FParameters.Password)
+ .AddPair('dbname', dbname)
+ .AddPair('application_name', APPNAME)
+ .AddPair('sslmode', 'disable');
+ if FParameters.WantSSL then begin
+ // Be aware .AddPair would add duplicates
+ case FParameters.SSLVerification of
+ 0: ConnectOptions.Values['sslmode'] := 'require';
+ 1: ConnectOptions.Values['sslmode'] := 'verify-ca';
+ 2: ConnectOptions.Values['sslmode'] := 'verify-full';
+ end;
+ if FParameters.SSLPrivateKey <> '' then
+ ConnectOptions.AddPair('sslkey', FParameters.SSLPrivateKey);
+ if FParameters.SSLCertificate <> '' then
+ ConnectOptions.AddPair('sslcert', FParameters.SSLCertificate);
+ if FParameters.SSLCACertificate <> '' then
+ ConnectOptions.AddPair('sslrootcert', FParameters.SSLCACertificate);
+ //if FParameters.SSLCipher <> '' then ??
+ end;
+ ConnectionString := '';
+ for i:=0 to ConnectOptions.Count-1 do begin
+ // Escape values. See issue #704 and #1417, and docs: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
+ OptionValue := ConnectOptions.ValueFromIndex[i];
+ OptionValue := StringReplace(OptionValue, '\', '\\', [rfReplaceAll]);
+ OptionValue := StringReplace(OptionValue, '''', '\''', [rfReplaceAll]);
+ ConnectionString := ConnectionString + ConnectOptions.Names[i] + '=''' + OptionValue + ''' ';
+ end;
+ ConnectOptions.Free;
+ ConnectionString := ConnectionString.TrimRight;
+
+ FHandle := FLib.PQconnectdb(PAnsiChar(AnsiString(ConnectionString)));
+ if FLib.PQstatus(FHandle) = CONNECTION_BAD then begin
+ Error := LastErrorMsg;
+ Log(lcError, Error);
+ FConnectionStarted := 0;
+ try
+ FLib.PQfinish(FHandle); // free the memory
+ except
+ on E:EAccessViolation do;
+ end;
+ FHandle := nil;
+ EndSSHTunnel;
+ if (FParameters.DefaultLibrary <> '') and (FParameters.LibraryOrProvider <> FParameters.DefaultLibrary) then begin
+ ErrorHint := f_('You could try the default library %s in your session settings. (Current: %s)',
+ [FParameters.DefaultLibrary, FParameters.LibraryOrProvider]
+ );
+ end else begin
+ ErrorHint := '';
+ end;
+ raise EDbError.Create(Error, LastErrorCode, ErrorHint);
+ end;
+ FActive := True;
+ FServerDateTimeOnStartup := GetVar('SELECT ' + GetSQLSpecifity(spFuncNow));
+ FServerVersionUntouched := GetVar('SELECT VERSION()');
+ FConnectionStarted := GetTickCount div 1000;
+ Query('SET statement_timeout TO '+IntToStr(Parameters.QueryTimeout*1000));
+ try
+ FServerUptime := StrToIntDef(GetVar('SELECT EXTRACT(EPOCH FROM CURRENT_TIMESTAMP - pg_postmaster_start_time())::INTEGER'), -1);
+ except
+ FServerUptime := -1;
+ end;
+ try
+ FIsSSL := LowerCase(GetVar('SHOW ssl')) = 'on';
+ except
+ FIsSSL := False;
+ end;
+
+ // Triggers OnDatabaseChange event for
+ Database := '';
+ DoAfterConnect;
+ end else begin
+ try
+ FLib.PQfinish(FHandle);
+ except
+ on E:EAccessViolation do;
+ end;
+ FActive := False;
+ ClearCache(False);
+ FConnectionStarted := 0;
+ EndSSHTunnel;
+ Log(lcInfo, f_(MsgDisconnect, [FParameters.Hostname, DateTimeToStr(Now)]));
+ end;
+end;
+
+
+procedure TSQLiteConnection.SetActive(Value: Boolean);
+var
+ ConnectResult: Integer;
+ RawPassword: AnsiString;
+ ErrorHint: String;
+ FileNames, EncryptionParams: TStringList;
+ MainFile, DbAlias, Param, ParamName: String;
+ i, SplitPos, ParamValue: Integer;
+ CipherIndex, ConfigResult: Integer;
+ ParamWasSet: Boolean;
+begin
+ // Support multiple filenames, and use first one as main database
+ FileNames := Explode(DELIM, Parameters.Hostname);
+ MainFile := IfThen(FileNames.Count>=1, FileNames[0], '');
+
+ if Value then begin
+ DoBeforeConnect;
+
+ ConnectResult := FLib.sqlite3_open(
+ PAnsiChar(Utf8Encode(MainFile)),
+ FHandle);
+
+ if ConnectResult = SQLITE_OK then begin
+ FActive := True;
+ if Parameters.NetType = ntSQLiteEncrypted then begin
+ // Use encryption key
+ CipherIndex := FLib.sqlite3mc_cipher_index(PAnsiChar(AnsiString(Parameters.Username)));
+ //Log(lcinfo, 'CipherIndex:'+CipherIndex.ToString);
+ if CipherIndex = -1 then
+ raise EDbError.Create(f_('Warning: Given cipher scheme name "%s" could not be found', [Parameters.Username]));
+ ConfigResult := FLib.sqlite3mc_config(FHandle, PAnsiChar('default:cipher'), CipherIndex);
+ if ConfigResult = -1 then
+ raise EDbError.Create(f_('Warning: Configuring with cipher index %d failed', [CipherIndex]));
+ // Set encryption parameters:
+ EncryptionParams := Parameters.AllDatabasesList;
+ for Param in EncryptionParams do begin
+ Log(lcDebug, 'Cipher encryption parameter: "'+Param+'"');
+ SplitPos := Param.IndexOf('=');
+ ParamWasSet := False;
+ if SplitPos > -1 then begin
+ ParamName := Copy(Param, 1, SplitPos);
+ ParamValue := StrToIntDef(Copy(Param, SplitPos+2, Length(Param)), -1);
+ if ParamValue > -1 then begin
+ ConfigResult := FLib.sqlite3mc_config_cipher(
+ FHandle,
+ PAnsiChar(AnsiString(Parameters.Username)),
+ PAnsiChar(AnsiString(ParamName)),
+ ParamValue
+ );
+ if ConfigResult <> -1 then
+ ParamWasSet := True;
+ end
+ end;
+ if not ParamWasSet then
+ Log(lcError, f_('Warning: Failed to set cipher encryption parameter "%s"', [Param]))
+ else
+ Log(lcInfo, f_('Info: Cipher encryption parameter "%s" set', [Param]));
+ end;
+ // Set the main database key
+ RawPassword := AnsiString(Parameters.Password);
+ FLib.sqlite3_key(FHandle, Pointer(RawPassword), Length(RawPassword));
+ // See https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_capi/
+ // "These functions return SQLITE_OK even if the provided key isn’t correct. This is because the key isn’t
+ // actually used until a subsequent attempt to read or write the database is made. To check whether the
+ // provided key was actually correct, you must execute a simple query like e.g. SELECT * FROM sqlite_master;
+ // and check whether that succeeds."
+ try
+ Query(ApplyLimitClause('SELECT', '* FROM sqlite_master', 1, 0));
+ except
+ on E:EDbError do
+ raise EDbError.Create(E.Message, 0, _('You have activated encryption on a probably non-encrypted database.'));
+ end;
+ end;
+
+ FLib.sqlite3_collation_needed(FHandle, Self, SQLite_CollationNeededCallback);
+ Query('PRAGMA busy_timeout='+(Parameters.QueryTimeout*1000).ToString);
+ // Override "main" database name with custom one
+ FMainDbName := GetFileNameWithoutExtension(MainFile);
+ if FLib.sqlite3_db_config(FHandle, SQLITE_DBCONFIG_MAINDBNAME, PAnsiChar(FMainDbName)) <> SQLITE_OK then begin
+ Log(lcError, 'Could not set custom name of "main" database to "' + UTF8ToString(FMainDbName) + '"');
+ end;
+ // Attach additional databases
+ for i:=1 to FileNames.Count-1 do begin
+ DbAlias := GetFileNameWithoutExtension(FileNames[i]);
+ Query('ATTACH DATABASE '+EscapeString(FileNames[i])+' AS '+QuoteIdent(DbAlias));
+ end;
+ // See issue #1186:
+ if FLib.sqlite3_enable_load_extension(FHandle, 1) <> SQLITE_OK then begin
+ Log(lcError, 'Could not enable load_extension()');
+ end;
+
+ FServerDateTimeOnStartup := GetVar('SELECT ' + GetSQLSpecifity(spFuncNow));
+ FServerVersionUntouched := GetVar('SELECT sqlite_version()');
+ FConnectionStarted := GetTickCount div 1000;
+ FServerUptime := -1;
+
+ // Triggers OnDatabaseChange event for
+ Database := '';
+ DoAfterConnect;
+
+ end else begin
+ Log(lcError, LastErrorMsg);
+ FConnectionStarted := 0;
+ FHandle := nil;
+ if (FParameters.DefaultLibrary <> '') and (FParameters.LibraryOrProvider <> FParameters.DefaultLibrary) then begin
+ ErrorHint := f_('You could try the default library %s in your session settings. (Current: %s)',
+ [FParameters.DefaultLibrary, FParameters.LibraryOrProvider]
+ );
+ end else begin
+ ErrorHint := '';
+ end;
+ raise EDbError.Create(LastErrorMsg);
+ end;
+ end else begin
+ if FHandle <> nil then begin
+ ClearCache(False);
+ FLib.sqlite3_close(FHandle);
+ FHandle := nil;
+ FActive := False;
+ Log(lcInfo, f_(MsgDisconnect, [MainFile, DateTimeToStr(Now)]));
+ end;
+ end;
+end;
+
+
+{procedure TInterbaseConnection.SetActive(Value: Boolean);
+var
+ DriverId: String;
+ IbDriver: TFDPhysIBDriverLink;
+ FbDriver: TFDPhysFBDriverLink;
+begin
+ if Value then begin
+ DoBeforeConnect;
+
+ FFDHandle := TFDConnection.Create(Owner);
+ FFDHandle.OnError := OnFdError;
+ //FFDHandle.DriverName := Parameters.LibraryOrProvider; // Auto-sets Params.DriverID
+ FFDHandle.LoginPrompt := False;
+
+ // Create virtual Interbase or Firebird driver id, once
+ DriverId := Parameters.LibraryOrProvider;
+ if Parameters.IsInterbase then begin
+ if not Assigned(FIbDrivers) then begin
+ FIbDrivers := TIbDrivers.Create;
+ end;
+ if not FIbDrivers.ContainsKey(DriverId) then begin
+ Log(lcInfo, 'Creating virtual driver id with '+Parameters.LibraryOrProvider);
+ IbDriver := TFDPhysIBDriverLink.Create(Owner);
+ IbDriver.VendorLib := Parameters.LibraryOrProvider;
+ IbDriver.DriverID := DriverId;
+ FIbDrivers.Add(DriverId, IbDriver);
+ end;
+ FIbDrivers.TryGetValue(DriverId, IbDriver);
+ FFDHandle.Params.Values['DriverID'] := IbDriver.DriverID;
+ end
+ else if Parameters.IsFirebird then begin
+ if not Assigned(FFbDrivers) then begin
+ FFbDrivers := TFbDrivers.Create;
+ end;
+ if not FFbDrivers.ContainsKey(DriverId) then begin
+ Log(lcInfo, 'Creating virtual driver id link with '+Parameters.LibraryOrProvider);
+ FbDriver := TFDPhysFBDriverLink.Create(Owner);
+ FbDriver.VendorLib := Parameters.LibraryOrProvider;
+ FbDriver.DriverID := DriverId;
+ FFbDrivers.Add(DriverId, FbDriver);
+ end;
+ FFbDrivers.TryGetValue(DriverId, FbDriver);
+ FFDHandle.Params.Values['DriverID'] := FbDriver.DriverID;
+ end;
+
+ // TCP/IP or local?
+ case Parameters.NetType of
+ ntInterbase_TCPIP, ntFirebird_TCPIP: begin
+ FFDHandle.Params.Values['Protocol'] := 'ipTCPIP';
+ FFDHandle.Params.Values['Server'] := Parameters.Hostname;
+ FFDHandle.Params.Values['Port'] := Parameters.Port.ToString;
+ end;
+ ntInterbase_Local, ntFirebird_Local: begin
+ FFDHandle.Params.Values['Protocol'] := 'ipLocal';
+ end;
+ end;
+
+ FFDHandle.Params.Values['Database'] := Parameters.AllDatabasesStr;
+ FFDHandle.Params.Values['User_Name'] := Parameters.Username;
+ FFDHandle.Params.Values['Password'] := Parameters.Password;
+ FFDHandle.Params.Values['CharacterSet'] := 'UTF8';
+ FFDHandle.Params.Values['ExtendedMetadata'] := 'True';
+
+ try
+ FFDHandle.Connected := True;
+ except
+ // Let OnFdError set FLastError
+ end;
+
+ if FFDHandle.Connected then begin
+ FActive := True;
+ //! Query('PRAGMA busy_timeout='+(Parameters.QueryTimeout*1000).ToString);
+
+ FServerDateTimeOnStartup := GetVar('SELECT ' + GetSQLSpecifity(spFuncNow));
+
+ if Parameters.IsInterbase then
+ FServerVersionUntouched := ''
+ else
+ FServerVersionUntouched := GetVar('SELECT rdb$get_context(''SYSTEM'', ''ENGINE_VERSION'') as version from rdb$database');
+ FConnectionStarted := GetTickCount div 1000;
+ FServerUptime := -1;
+
+ // Triggers OnDatabaseChange event for
+ Database := '';
+ DoAfterConnect;
+
+ end else begin
+ Log(lcError, LastErrorMsg);
+ FConnectionStarted := 0;
+ raise EDbError.Create(LastErrorMsg);
+ end;
+ end else begin
+ if FFdHandle <> nil then begin
+ ClearCache(False);
+ FFdHandle.Connected := False;
+ FActive := False;
+ Log(lcInfo, f_(MsgDisconnect, [Parameters.Hostname, DateTimeToStr(Now)]));
+ end;
+ end;
+end;}
+
+
+procedure TMySQLConnection.SetOption(Option: Integer; Arg: PAnsiChar);
+var
+ SetOptionResult: Integer;
+ RttiContext: TRttiContext;
+ LibType: TRttiType;
+ //LibField: TRttiField;
+ FieldName: String;
+begin
+ // Set one of the MYSQL_* option and log a warning if that failed
+ SetOptionResult := FLib.mysql_options(FHandle, Option, Arg);
+ if SetOptionResult <> 0 then begin
+ FieldName := Option.ToString;
+ // Attempt to find readable name of option constant
+ {RttiContext := TRttiContext.Create;
+ LibType := RttiContext.GetType(TypeInfo(TMySQLLib));
+ for LibField in LibType.GetFields do begin
+ // Skip assigned procedures
+ if LibField.FieldType = nil then
+ Continue;
+ if LibField.DataType.TypeKind = tkInteger then begin
+ if LibField.GetValue(FLib).AsInteger = Option then begin
+ FieldName := LibField.Name;
+ end;
+ end;
+ end;
+ RttiContext.Free;}
+ Log(lcError, _(SLogPrefixWarning) + ': mysql_options(' + FieldName + ', ...) failed!');
+ end;
+end;
+
+
+procedure TDBConnection.DoBeforeConnect;
+var
+ UsingPass: String;
+ Dialog: TfrmLogin;
+begin
+ // Prompt for password on initial connect
+ if FParameters.LoginPrompt and (not FLoginPromptDone) then begin
+ Dialog := TfrmLogin.Create(Self);
+ Dialog.Caption := APPNAME + ' - ' + FParameters.SessionName;
+ Dialog.lblPrompt.Caption := f_('Login to %s:', [FParameters.Hostname]);
+ Dialog.editUsername.Text := FParameters.Username;
+ Dialog.editPassword.Text := FParameters.Password;
+ Dialog.ShowModal;
+ FParameters.Username := Dialog.editUsername.Text;
+ FParameters.Password := Dialog.editPassword.Text;
+ Dialog.Free;
+ FLoginPromptDone := True;
+ end;
+
+ // Prepare connection
+ UsingPass := IfThen(FParameters.Password.IsEmpty, 'No', 'Yes');
+ case FParameters.NetTypeGroup of
+ ngSQLite: begin
+ Log(lcInfo, f_('Connecting to %s via %s, cipher %s, using encryption key: %s ...',
+ [FParameters.Hostname, FParameters.NetTypeName(True), FParameters.Username, UsingPass]
+ ));
+ end;
+ else begin
+ Log(lcInfo, f_('Connecting to %s via %s, username %s, using password: %s ...',
+ [FParameters.Hostname, FParameters.NetTypeName(True), FParameters.Username, UsingPass]
+ ));
+ end;
+ end;
+
+ FSQLSpecifities[spOrderAsc] := 'ASC';
+ FSQLSpecifities[spOrderDesc] := 'DESC';
+ FSQLSpecifities[spForeignKeyEventAction] := 'RESTRICT,CASCADE,SET NULL,NO ACTION';
+
+ case Parameters.NetTypeGroup of
+ ngMySQL: begin
+ FSQLSpecifities[spDatabaseDrop] := 'DROP DATABASE %s';
+ FSQLSpecifities[spEmptyTable] := 'TRUNCATE ';
+ FSQLSpecifities[spRenameTable] := 'RENAME TABLE %s TO %s';
+ FSQLSpecifities[spRenameView] := FSQLSpecifities[spRenameTable];
+ FSQLSpecifities[spCurrentUserHost] := 'SELECT CURRENT_USER()';
+ FSQLSpecifities[spLikeCompare] := '%s LIKE %s';
+ FSQLSpecifities[spAddColumn] := 'ADD COLUMN %s';
+ FSQLSpecifities[spChangeColumn] := 'CHANGE COLUMN %s %s';
+ FSQLSpecifities[spGlobalStatus] := IfThen(
+ Parameters.IsProxySQLAdmin,
+ 'SELECT * FROM stats_mysql_global',
+ 'SHOW /*!50002 GLOBAL */ STATUS'
+ );
+ FSQLSpecifities[spCommandsCounters] := IfThen(
+ Parameters.IsProxySQLAdmin,
+ 'SELECT * FROM stats_mysql_commands_counters',
+ 'SHOW /*!50002 GLOBAL */ STATUS LIKE ''Com\_%'''
+ );
+ FSQLSpecifities[spSessionVariables] := 'SHOW VARIABLES';
+ FSQLSpecifities[spGlobalVariables] := 'SHOW GLOBAL VARIABLES';
+ FSQLSpecifities[spISSchemaCol] := '%s_SCHEMA';
+ FSQLSpecifities[spUSEQuery] := 'USE %s';
+ if Parameters.NetType = ntMySQL_RDS then begin
+ FSQLSpecifities[spKillQuery] := 'CALL mysql.rds_kill_query(%d)';
+ FSQLSpecifities[spKillProcess] := 'CALL mysql.rds_kill(%d)'
+ end
+ else begin
+ FSQLSpecifities[spKillQuery] := 'KILL %d'; // may be overwritten in DoAfterConnect
+ FSQLSpecifities[spKillProcess] := 'KILL %d';
+ end;
+ FSQLSpecifities[spFuncLength] := 'LENGTH';
+ FSQLSpecifities[spFuncCeil] := 'CEIL';
+ FSQLSpecifities[spFuncLeft] := IfThen(Parameters.IsProxySQLAdmin, 'SUBSTR(%s, 1, %d)', 'LEFT(%s, %d)');
+ FSQLSpecifities[spFuncNow] := IfThen(Parameters.IsProxySQLAdmin, 'CURRENT_TIMESTAMP', 'NOW()');
+ FSQLSpecifities[spFuncLastAutoIncNumber] := 'LAST_INSERT_ID()';
+ FSQLSpecifities[spLockedTables] := '';
+ FSQLSpecifities[spDisableForeignKeyChecks] := 'SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0';
+ FSQLSpecifities[spEnableForeignKeyChecks] := 'SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1)';
+ FSQLSpecifities[spForeignKeyDrop] := 'DROP FOREIGN KEY %s';
+ end;
+ ngMSSQL: begin
+ FSQLSpecifities[spDatabaseDrop] := 'DROP DATABASE %s';
+ FSQLSpecifities[spEmptyTable] := 'DELETE FROM ';
+ FSQLSpecifities[spRenameTable] := 'EXEC sp_rename %s, %s';
+ FSQLSpecifities[spRenameView] := FSQLSpecifities[spRenameTable];
+ FSQLSpecifities[spCurrentUserHost] := 'SELECT SYSTEM_USER';
+ FSQLSpecifities[spLikeCompare] := '%s LIKE %s';
+ FSQLSpecifities[spAddColumn] := 'ADD %s';
+ FSQLSpecifities[spChangeColumn] := 'ALTER COLUMN %s %s';
+ FSQLSpecifities[spSessionVariables] := 'SELECT '+QuoteIdent('comment')+', '+QuoteIdent('value')+' FROM '+QuoteIdent('master')+'.'+QuoteIdent('dbo')+'.'+QuoteIdent('syscurconfigs')+' ORDER BY '+QuoteIdent('comment');
+ FSQLSpecifities[spGlobalVariables] := FSQLSpecifities[spSessionVariables];
+ FSQLSpecifities[spISSchemaCol] := '%s_CATALOG';
+ FSQLSpecifities[spUSEQuery] := 'USE %s';
+ FSQLSpecifities[spKillQuery] := 'KILL %d';
+ FSQLSpecifities[spKillProcess] := 'KILL %d';
+ FSQLSpecifities[spFuncLength] := 'LEN';
+ FSQLSpecifities[spFuncCeil] := 'CEILING';
+ FSQLSpecifities[spFuncLeft] := 'LEFT(%s, %d)';
+ FSQLSpecifities[spFuncNow] := 'GETDATE()';
+ FSQLSpecifities[spFuncLastAutoIncNumber] := 'LAST_INSERT_ID()';
+ FSQLSpecifities[spLockedTables] := '';
+ FSQLSpecifities[spDisableForeignKeyChecks] := '';
+ FSQLSpecifities[spEnableForeignKeyChecks] := '';
+ FSQLSpecifities[spForeignKeyDrop] := 'DROP FOREIGN KEY %s';
+ end;
+ ngPgSQL: begin
+ FSQLSpecifities[spDatabaseDrop] := 'DROP SCHEMA %s';
+ FSQLSpecifities[spEmptyTable] := 'DELETE FROM ';
+ FSQLSpecifities[spRenameTable] := 'ALTER TABLE %s RENAME TO %s';
+ FSQLSpecifities[spRenameView] := 'ALTER VIEW %s RENAME TO %s';
+ FSQLSpecifities[spCurrentUserHost] := 'SELECT CURRENT_USER';
+ FSQLSpecifities[spLikeCompare] := '%s ILIKE %s';
+ FSQLSpecifities[spAddColumn] := 'ADD %s';
+ FSQLSpecifities[spChangeColumn] := 'ALTER COLUMN %s %s';
+ FSQLSpecifities[spRenameColumn] := 'RENAME COLUMN %s TO %s';
+ FSQLSpecifities[spForeignKeyEventAction] := 'RESTRICT,CASCADE,SET NULL,NO ACTION,SET DEFAULT';
+ FSQLSpecifities[spSessionVariables] := 'SHOW ALL';
+ FSQLSpecifities[spGlobalVariables] := FSQLSpecifities[spSessionVariables];
+ FSQLSpecifities[spISSchemaCol] := '%s_schema';
+ FSQLSpecifities[spUSEQuery] := 'SET search_path TO %s';
+ FSQLSpecifities[spKillQuery] := 'SELECT pg_cancel_backend(%d)';
+ FSQLSpecifities[spKillProcess] := 'SELECT pg_cancel_backend(%d)';
+ FSQLSpecifities[spFuncLength] := 'LENGTH';
+ FSQLSpecifities[spFuncCeil] := 'CEIL';
+ FSQLSpecifities[spFuncLeft] := 'SUBSTRING(%s, 1, %d)';
+ FSQLSpecifities[spFuncNow] := 'NOW()';
+ FSQLSpecifities[spFuncLastAutoIncNumber] := 'LASTVAL()';
+ FSQLSpecifities[spLockedTables] := '';
+ FSQLSpecifities[spDisableForeignKeyChecks] := '';
+ FSQLSpecifities[spEnableForeignKeyChecks] := '';
+ FSQLSpecifities[spForeignKeyDrop] := 'DROP CONSTRAINT %s';
+ end;
+ ngSQLite: begin
+ FSQLSpecifities[spDatabaseDrop] := 'DROP DATABASE %s';
+ FSQLSpecifities[spEmptyTable] := 'DELETE FROM ';
+ FSQLSpecifities[spRenameTable] := 'ALTER TABLE %s RENAME TO %s';
+ FSQLSpecifities[spRenameView] := FSQLSpecifities[spRenameTable];
+ FSQLSpecifities[spCurrentUserHost] := ''; // unsupported
+ FSQLSpecifities[spLikeCompare] := '%s LIKE %s';
+ FSQLSpecifities[spAddColumn] := 'ADD COLUMN %s';
+ FSQLSpecifities[spChangeColumn] := ''; // SQLite only supports renaming
+ FSQLSpecifities[spRenameColumn] := 'RENAME COLUMN %s TO %s';
+ FSQLSpecifities[spSessionVariables] := 'SELECT null, null'; // Todo: combine "PRAGMA pragma_list" + "PRAGMA a; PRAGMY b; ..."?
+ FSQLSpecifities[spGlobalVariables] := 'SHOW GLOBAL VARIABLES';
+ FSQLSpecifities[spISSchemaCol] := '%s_SCHEMA';
+ FSQLSpecifities[spUSEQuery] := '';
+ FSQLSpecifities[spKillQuery] := 'KILL %d';
+ FSQLSpecifities[spKillProcess] := 'KILL %d';
+ FSQLSpecifities[spFuncLength] := 'LENGTH';
+ FSQLSpecifities[spFuncCeil] := 'CEIL';
+ FSQLSpecifities[spFuncLeft] := 'SUBSTR(%s, 1, %d)';
+ FSQLSpecifities[spFuncNow] := 'DATETIME()';
+ FSQLSpecifities[spFuncLastAutoIncNumber] := 'LAST_INSERT_ID()';
+ FSQLSpecifities[spLockedTables] := '';
+ FSQLSpecifities[spDisableForeignKeyChecks] := '';
+ FSQLSpecifities[spEnableForeignKeyChecks] := '';
+ FSQLSpecifities[spForeignKeyDrop] := 'DROP FOREIGN KEY %s';
+ end;
+ ngInterbase: begin
+ FSQLSpecifities[spDatabaseDrop] := 'DROP DATABASE %s';
+ FSQLSpecifities[spEmptyTable] := 'TRUNCATE ';
+ FSQLSpecifities[spRenameTable] := 'RENAME TABLE %s TO %s';
+ FSQLSpecifities[spRenameView] := FSQLSpecifities[spRenameTable];
+ if Self.Parameters.LibraryOrProvider = 'IB' then
+ FSQLSpecifities[spCurrentUserHost] := 'select user from rdb$database'
+ else
+ FSQLSpecifities[spCurrentUserHost] := 'select current_user || ''@'' || mon$attachments.mon$remote_host from mon$attachments where mon$attachments.mon$attachment_id = current_connection';
+ FSQLSpecifities[spLikeCompare] := '%s LIKE %s';
+ FSQLSpecifities[spAddColumn] := 'ADD COLUMN %s';
+ FSQLSpecifities[spChangeColumn] := 'CHANGE COLUMN %s %s';
+ FSQLSpecifities[spRenameColumn] := '';
+ FSQLSpecifities[spSessionVariables] := 'SHOW VARIABLES';
+ FSQLSpecifities[spGlobalVariables] := 'SHOW GLOBAL VARIABLES';
+ FSQLSpecifities[spISSchemaCol] := '%s_SCHEMA';
+ FSQLSpecifities[spUSEQuery] := '';
+ FSQLSpecifities[spKillQuery] := 'KILL %d';
+ FSQLSpecifities[spKillProcess] := 'KILL %d';
+ FSQLSpecifities[spFuncLength] := 'LENGTH';
+ FSQLSpecifities[spFuncCeil] := 'CEIL';
+ FSQLSpecifities[spFuncLeft] := 'SUBSTR(%s, 1, %d)';
+ FSQLSpecifities[spFuncNow] := ' cast(''now'' as timestamp) from rdb$database';
+ FSQLSpecifities[spFuncLastAutoIncNumber] := 'LAST_INSERT_ID()';
+ FSQLSpecifities[spLockedTables] := '';
+ FSQLSpecifities[spDisableForeignKeyChecks] := '';
+ FSQLSpecifities[spEnableForeignKeyChecks] := '';
+ FSQLSpecifities[spForeignKeyDrop] := 'DROP FOREIGN KEY %s';
+ end;
+
+ end;
+
+end;
+
+
+procedure TMySQLConnection.DoBeforeConnect;
+var
+ LibraryPath: String;
+begin
+ // Init libmysql before actually connecting.
+ LibraryPath := ExtractFilePath(ParamStr(0)) + Parameters.LibraryOrProvider;
+ Log(lcDebug, f_('Loading library file %s ...', [LibraryPath]));
+ // Throws EDbError on any failure:
+ FLib := TMySQLLib.Create(LibraryPath, Parameters.DefaultLibrary);
+ Log(lcDebug, FLib.DllFile + ' v' + DecodeApiString(FLib.mysql_get_client_info) + ' loaded.');
+ inherited;
+end;
+
+
+procedure TPgConnection.DoBeforeConnect;
+var
+ LibraryPath,
+ msg: String;
+begin
+ // Init lib before actually connecting.
+ LibraryPath := ExtractFilePath(ParamStr(0)) + Parameters.LibraryOrProvider;
+ Log(lcDebug, f_('Loading library file %s ...', [LibraryPath]));
+ try
+ FLib := TPostgreSQLLib.Create(LibraryPath, Parameters.DefaultLibrary);
+ Log(lcDebug, FLib.DllFile + ' v' + IntToStr(FLib.PQlibVersion) + ' loaded.');
+ except
+ on E:EDbError do begin
+ // Try to explain what may cause this error
+ msg := E.Message;
+ if E.ErrorCode = TDbLib.LIB_PROC_ERROR then begin
+ msg := msg + sLineBreak + sLineBreak +
+ f_('Your %s is incompatible to %s, or your system is missing a dependent library.',
+ [Parameters.LibraryOrProvider, APPNAME]);
+ end;
+ // In any case:
+ msg := msg + sLineBreak + sLineBreak +
+ f_('Installing %s might help. Please download from %s',
+ ['VC Redistributable', 'https://support.microsoft.com/en-us/help/3179560/update-for-visual-c-2013-and-visual-c-redistributable-package']
+ );
+ raise EDbError.Create(msg, E.ErrorCode);
+ end;
+ end;
+ inherited;
+end;
+
+
+procedure TSQLiteConnection.DoBeforeConnect;
+var
+ LibraryPath: String;
+begin
+ // Init lib before actually connecting.
+ LibraryPath := ExtractFilePath(ParamStr(0)) + Parameters.LibraryOrProvider;
+ Log(lcDebug, f_('Loading library file %s ...', [LibraryPath]));
+ // Throws EDbError on any failure:
+ if Parameters.NetType = ntSQLite then
+ FLib := TSQLiteLib.Create(LibraryPath, Parameters.DefaultLibrary)
+ else
+ FLib := TSQLiteLib.CreateWithMultipleCipherFunctions(LibraryPath, Parameters.DefaultLibrary);
+ Log(lcDebug, FLib.DllFile + ' v' + ServerVersionUntouched + ' loaded.');
+ inherited;
+end;
+
+
+{procedure TInterbaseConnection.DoBeforeConnect;
+begin
+ // Todo
+ inherited;
+end;}
+
+
+procedure TDBConnection.StartSSHTunnel(var FinalHost: String; var FinalPort: Integer);
+begin
+ // Create SSH process
+ if Parameters.SSHActive and (FSecureShellCmd = nil) then begin
+ FSecureShellCmd := TSecureShellCmd.Create(Self);
+ FSecureShellCmd.Connect;
+ FinalHost := '127.0.0.1';
+ FinalPort := FParameters.SSHLocalPort;
+ end;
+end;
+
+
+procedure TDBConnection.EndSSHTunnel;
+begin
+ if FSecureShellCmd <> nil then begin
+ FSecureShellCmd.Free;
+ FSecureShellCmd := nil;
+ end;
+end;
+
+
+procedure TDBConnection.DoAfterConnect;
+var
+ SQLFunctionsFileOrder: String;
+ MajorMinorVer, MajorVer: String;
+begin
+ AppSettings.SessionPath := FParameters.SessionPath;
+ AppSettings.WriteString(asServerVersionFull, FServerVersionUntouched);
+ FParameters.ServerVersion := FServerVersionUntouched;
+ Log(lcInfo, f_('Connected. Thread-ID: %d', [ThreadId]));
+ if Assigned(FOnConnected) then
+ FOnConnected(Self, FDatabase);
+ if FParameters.KeepAlive > 0 then begin
+ FKeepAliveTimer.Interval := FParameters.KeepAlive * 1000;
+ FKeepAliveTimer.OnTimer := KeepAliveTimerEvent;
+ end;
+
+ MajorMinorVer := RegExprGetMatch('^(\d+\.\d+)', ServerVersionStr, 1);
+ MajorVer := RegExprGetMatch('^(\d+)\.', ServerVersionStr, 1);
+
+ if FParameters.IsMariaDB then
+ SQLFunctionsFileOrder := 'mariadb'+MajorMinorVer+',mariadb'+MajorVer+',mariadb,mysql'
+ else if FParameters.IsAnyMySQL then
+ SQLFunctionsFileOrder := 'mysql'+MajorMinorVer+',mysql'+MajorVer+',mysql'
+ else if FParameters.IsRedshift then
+ SQLFunctionsFileOrder := 'redshift'+MajorMinorVer+',redshift'+MajorVer+',redshift,postgresql'
+ else if FParameters.IsAnyPostgreSQL then
+ SQLFunctionsFileOrder := 'postgresql'+MajorMinorVer+',postgresql'+MajorVer+',postgresql'
+ else if FParameters.IsAnyMSSQL then
+ SQLFunctionsFileOrder := 'mssql'+MajorMinorVer+',mssql'+MajorVer+',mssql'
+ else if FParameters.IsAnySQLite then
+ SQLFunctionsFileOrder := 'sqlite'+MajorMinorVer+',sqlite'+MajorVer+',sqlite'
+ else if FParameters.IsAnyInterbase then
+ SQLFunctionsFileOrder := 'interbase'+MajorMinorVer+',interbase'+MajorVer+',interbase'
+ else
+ SQLFunctionsFileOrder := '';
+ FSQLFunctions := TSQLFunctionList.Create(Self, SQLFunctionsFileOrder);
+end;
+
+
+procedure TMySQLConnection.DoAfterConnect;
+var
+ TZI: TTimeZoneInformation;
+ Minutes, Hours, i: Integer;
+ Offset: String;
+ ObjNames: TStringList;
+begin
+ inherited;
+
+ // Set timezone offset to UTC
+ if Has(frTimezoneVar) and Parameters.LocalTimeZone then begin
+ Minutes := 0;
+ case GetTimeZoneInformation(TZI) of
+ TIME_ZONE_ID_STANDARD: Minutes := (TZI.Bias + TZI.StandardBias);
+ TIME_ZONE_ID_DAYLIGHT: Minutes := (TZI.Bias + TZI.DaylightBias);
+ TIME_ZONE_ID_UNKNOWN: Minutes := TZI.Bias;
+ else RaiseLastOSError;
+ end;
+ Hours := Minutes div 60;
+ Minutes := Minutes mod 60;
+ if Hours < 0 then
+ Offset := '+'
+ else
+ Offset := '-';
+ Offset := Offset + Format('%.2d:%.2d', [Abs(Hours), Abs(Minutes)]);
+ Query('SET time_zone='+EscapeString(Offset));
+ end;
+
+ // Support microseconds in some temporal datatypes of MariaDB 5.3+ and MySQL 5.6
+ if Has(frTemporalTypesFraction) then begin
+ for i:=Low(FDatatypes) to High(FDatatypes) do begin
+ if FDatatypes[i].Index in [dbdtDatetime, dbdtDatetime2, dbdtTime, dbdtTimestamp] then
+ FDatatypes[i].HasLength := True;
+ end;
+ end;
+
+ if Has(frKillQuery) then begin
+ FSQLSpecifities[spKillQuery] := 'KILL QUERY %d';
+ end;
+
+ // List of IS tables
+ try
+ ObjNames := GetCol('SHOW TABLES FROM '+QuoteIdent(FInfSch));
+ FInformationSchemaObjects.CommaText := ObjNames.CommaText;
+ ObjNames.Free;
+ except // silently fail if IS does not exist, on super old servers
+ end;
+
+ if Has(frLockedTables) then
+ FSQLSpecifities[spLockedTables] := 'SHOW OPEN TABLES FROM %s WHERE '+QuoteIdent('in_use')+'!=0';
+end;
+
+
+{procedure TAdoDBConnection.DoAfterConnect;
+begin
+ inherited;
+ // See http://sqlserverbuilds.blogspot.de/
+ case ServerVersionInt of
+ 0..899: begin
+ FSQLSpecifities[spDatabaseTable] := QuoteIdent('master')+'..'+QuoteIdent('sysdatabases');
+ FSQLSpecifities[spDatabaseTableId] := QuoteIdent('dbid');
+ FSQLSpecifities[spDbObjectsTable] := '..'+QuoteIdent('sysobjects');
+ FSQLSpecifities[spDbObjectsCreateCol] := 'crdate';
+ FSQLSpecifities[spDbObjectsUpdateCol] := '';
+ FSQLSpecifities[spDbObjectsTypeCol] := 'xtype';
+ end;
+ else begin
+ FSQLSpecifities[spDatabaseTable] := QuoteIdent('sys')+'.'+QuoteIdent('databases');
+ FSQLSpecifities[spDatabaseTableId] := QuoteIdent('database_id');
+ FSQLSpecifities[spDbObjectsTable] := '.'+QuoteIdent('sys')+'.'+QuoteIdent('objects');
+ FSQLSpecifities[spDbObjectsCreateCol] := 'create_date';
+ FSQLSpecifities[spDbObjectsUpdateCol] := 'modify_date';
+ FSQLSpecifities[spDbObjectsTypeCol] := 'type';
+ end;
+ end;
+ // List of known IS tables
+ FInformationSchemaObjects.CommaText := 'CHECK_CONSTRAINTS,'+
+ 'COLUMN_DOMAIN_USAGE,'+
+ 'COLUMN_PRIVILEGES,'+
+ 'COLUMNS,'+
+ 'CONSTRAINT_COLUMN_USAGE,'+
+ 'CONSTRAINT_TABLE_USAGE,'+
+ 'DOMAIN_CONSTRAINTS,'+
+ 'DOMAINS,'+
+ 'KEY_COLUMN_USAGE,'+
+ 'PARAMETERS,'+
+ 'REFERENTIAL_CONSTRAINTS,'+
+ 'ROUTINES,'+
+ 'ROUTINE_COLUMNS,'+
+ 'SCHEMATA,'+
+ 'TABLE_CONSTRAINTS,'+
+ 'TABLE_PRIVILEGES,'+
+ 'TABLES,'+
+ 'VIEW_COLUMN_USAGE,'+
+ 'VIEW_TABLE_USAGE,'+
+ 'VIEWS';
+end;}
+
+
+procedure TPgConnection.DoAfterConnect;
+var
+ ObjNames: TStringList;
+begin
+ inherited;
+ // List of known IS tables
+ ObjNames := GetCol('SELECT table_name FROM information_schema.tables WHERE table_schema='+EscapeString(FInfSch));
+ FInformationSchemaObjects.CommaText := ObjNames.CommaText;
+ ObjNames.Free;
+end;
+
+
+function TMySQLConnection.Ping(Reconnect: Boolean): Boolean;
+var
+ IsDead: Boolean;
+begin
+ Log(lcDebug, 'Ping server ...');
+ IsDead := True;
+ try
+ IsDead := (FHandle=nil) or (FLib.mysql_ping(FHandle) <> 0);
+ except
+ // silence dumb exceptions from mysql_ping
+ on E:Exception do
+ Log(lcError, E.Message);
+ end;
+
+ if IsDead then begin
+ // Be sure to release some stuff before reconnecting
+ Active := False;
+ if Reconnect then
+ Active := True;
+ end;
+ Result := FActive;
+ // Restart keep-alive timer
+ FKeepAliveTimer.Enabled := False;
+ FKeepAliveTimer.Enabled := True;
+end;
+
+
+{function TAdoDBConnection.Ping(Reconnect: Boolean): Boolean;
+begin
+ Log(lcDebug, 'Ping server ...');
+ if FActive then try
+ FAdoHandle.Execute('SELECT 1');
+ except
+ on E:EOleException do begin
+ FLastError := E.Message;
+ Log(lcError, E.Message);
+ Active := False;
+ if Reconnect then
+ Active := True;
+ end;
+ end;
+ Result := FActive;
+ // Restart keep-alive timer
+ FKeepAliveTimer.Enabled := False;
+ FKeepAliveTimer.Enabled := True;
+end;}
+
+
+function TPGConnection.Ping(Reconnect: Boolean): Boolean;
+var
+ PingResult: PPGResult;
+ IsBroken: Boolean;
+ PingStatus: Integer;
+begin
+ Log(lcDebug, 'Ping server ...');
+ if FActive then begin
+ IsBroken := FHandle = nil;
+ if not IsBroken then begin
+ PingStatus := FLib.PQsendQuery(FHandle, PAnsiChar(''));
+ IsBroken := PingStatus <> 1;
+ PingResult := FLib.PQgetResult(FHandle);
+ while PingResult <> nil do begin
+ FLib.PQclear(PingResult);
+ PingResult := FLib.PQgetResult(FHandle);
+ end;
+ end;
+
+ if IsBroken then begin
+ // Be sure to release some stuff before reconnecting
+ Active := False;
+ if Reconnect then
+ Active := True;
+ end;
+ end;
+ Result := FActive;
+ // Restart keep-alive timer
+ FKeepAliveTimer.Enabled := False;
+ FKeepAliveTimer.Enabled := True;
+end;
+
+
+function TSQLiteConnection.Ping(Reconnect: Boolean): Boolean;
+begin
+ Log(lcDebug, 'Ping server ...');
+ if FActive then try
+ FLib.sqlite3_exec(FHandle, nil, 0, nil, nil);
+ except
+ on E:Exception do begin
+ Log(lcError, E.Message);
+ Active := False;
+ if Reconnect then
+ Active := True;
+ end;
+ end;
+ Result := FActive;
+ // Restart keep-alive timer
+ FKeepAliveTimer.Enabled := False;
+ FKeepAliveTimer.Enabled := True;
+end;
+
+
+{function TInterbaseConnection.Ping(Reconnect: Boolean): Boolean;
+begin
+ Log(lcDebug, 'Ping server ...');
+ if FActive then begin
+ FFDHandle.Ping;
+ end;
+ Result := FActive;
+ // Restart keep-alive timer
+ FKeepAliveTimer.Enabled := False;
+ FKeepAliveTimer.Enabled := True;
+end;}
+
+
+procedure TDBConnection.KeepAliveTimerEvent(Sender: TObject);
+begin
+ // Ping server in intervals, without automatically reconnecting
+ if Active and (not IsLockedByThread) then
+ Ping(False);
+end;
+
+
+{**
+ Executes a query
+}
+procedure TDBConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+begin
+ if IsLockedByThread and (FLockedByThread.ThreadID <> GetCurrentThreadID) then begin
+ Log(lcDebug, _('Waiting for running query to finish ...'));
+ try
+ FLockedByThread.WaitFor;
+ except
+ on E:EThread do;
+ end;
+ end;
+ Ping(True);
+ Log(LogCategory, SQL);
+ FLastQuerySQL := SQL;
+ FRowsFound := 0;
+ FRowsAffected := 0;
+ FWarningCount := 0;
+end;
+
+
+procedure TMySQLConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+var
+ QueryStatus: Integer;
+ NativeSQL: AnsiString;
+ TimerStart: Cardinal;
+ QueryResult: PMYSQL_RES;
+begin
+ inherited;
+
+ if IsUnicode then
+ NativeSQL := UTF8Encode(SQL)
+ else
+ NativeSQL := AnsiString(SQL);
+ TimerStart := GetTickCount;
+ SetLength(FLastRawResults, 0);
+ FStatementNum := 1;
+ QueryStatus := FLib.mysql_real_query(FHandle, PAnsiChar(NativeSQL), Length(NativeSQL));
+ FLastQueryDuration := GetTickCount - TimerStart;
+ FLastQueryNetworkDuration := 0;
+ if QueryStatus <> 0 then begin
+ // Most errors will show up here, some others slightly later, after mysql_store_result()
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg, GetLastErrorCode);
+ end else begin
+ // We must call mysql_store_result() + mysql_free_result() to unblock the connection
+ // See: http://dev.mysql.com/doc/refman/5.0/en/mysql-store-result.html
+ FWarningCount := FLib.mysql_warning_count(FHandle);
+ TimerStart := GetTickCount;
+ QueryResult := FLib.mysql_store_result(FHandle);
+ FLastQueryNetworkDuration := GetTickCount - TimerStart;
+
+ if (QueryResult = nil) and (FLib.mysql_affected_rows(FHandle) = -1) then begin
+ // Indicates a late error, e.g. triggered by mysql_store_result(), after selecting a stored
+ // function with invalid SQL body. Also SHOW TABLE STATUS on older servers.
+ // See http://dev.mysql.com/doc/refman/5.0/en/mysql-affected-rows.html
+ // "An integer greater than zero indicates the number of rows affected or
+ // retrieved. Zero indicates that no records were updated for an UPDATE statement, no rows
+ // matched the WHERE clause in the query or that no query has yet been executed. -1
+ // indicates that the query returned an error or that, for a SELECT query,
+ // mysql_affected_rows() was called prior to calling mysql_store_result()."
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+
+ if QueryResult = nil then
+ DetectUSEQuery(SQL);
+
+ while QueryStatus=0 do begin
+ if QueryResult <> nil then begin
+ // Statement returned a result set
+ Inc(FRowsFound, FLib.mysql_num_rows(QueryResult));
+ if DoStoreResult then begin
+ SetLength(FLastRawResults, Length(FLastRawResults)+1);
+ FLastRawResults[Length(FLastRawResults)-1] := QueryResult;
+ end else begin
+ FLib.mysql_free_result(QueryResult);
+ end;
+ end else begin
+ // No result, but probably affected rows
+ Inc(FRowsAffected, FLib.mysql_affected_rows(FHandle));
+ end;
+ // more results? -1 = no, >0 = error, 0 = yes (keep looping)
+ Inc(FStatementNum);
+ TimerStart := GetTickCount;
+ QueryStatus := FLib.mysql_next_result(FHandle);
+ Inc(FLastQueryDuration, GetTickCount - TimerStart);
+ if QueryStatus = 0 then
+ QueryResult := FLib.mysql_store_result(FHandle)
+ else if QueryStatus > 0 then begin
+ // MySQL stops executing a multi-query when an error occurs. So do we here by raising an exception.
+ SetLength(FLastRawResults, 0);
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ end;
+
+ end;
+end;
+
+
+{procedure TAdoDBConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+var
+ TimerStart: Cardinal;
+ VarRowsAffected: OleVariant;
+ QueryResult, NextResult: _RecordSet;
+ Affected: Int64;
+begin
+ inherited;
+
+ TimerStart := GetTickCount;
+ SetLength(FLastRawResults, 0);
+ try
+ QueryResult := FAdoHandle.ConnectionObject.Execute(SQL, VarRowsAffected, 1);
+ FLastQueryDuration := GetTickCount - TimerStart;
+ FLastQueryNetworkDuration := 0;
+
+ // Handle multiple results
+ while(QueryResult <> nil) do begin
+ Affected := VarRowsAffected;
+ Affected := Max(Affected, 0);
+ Inc(FRowsAffected, Affected);
+ NextResult := QueryResult.NextRecordset(VarRowsAffected);
+ if QueryResult.Fields.Count > 0 then begin
+ Inc(FRowsFound, QueryResult.RecordCount);
+ if DoStoreResult then begin
+ SetLength(FLastRawResults, Length(FLastRawResults)+1);
+ FLastRawResults[Length(FLastRawResults)-1] := QueryResult;
+ end else
+ QueryResult := nil;
+ end else
+ QueryResult := nil;
+ QueryResult := NextResult;
+ end;
+
+ DetectUSEQuery(SQL);
+ except
+ on E:EOleException do begin
+ FLastError := E.Message;
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ end;
+end;}
+
+
+procedure TPGConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+var
+ TimerStart: Cardinal;
+ QueryResult: PPGresult;
+ QueryStatus: Integer;
+ NativeSQL: AnsiString;
+begin
+ inherited;
+
+ if IsUnicode then
+ NativeSQL := UTF8Encode(SQL)
+ else
+ NativeSQL := AnsiString(SQL);
+ TimerStart := GetTickCount;
+ SetLength(FLastRawResults, 0);
+
+ QueryStatus := FLib.PQsendQuery(FHandle, PAnsiChar(NativeSQL));
+
+ FLastQueryDuration := GetTickCount - TimerStart;
+ FLastQueryNetworkDuration := 0;
+ if QueryStatus <> 1 then begin
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg);
+ end else begin
+ FRowsAffected := 0;
+ FRowsFound := 0;
+ TimerStart := GetTickCount;
+ QueryResult := FLib.PQgetResult(FHandle);
+ FLastQueryNetworkDuration := GetTickCount - TimerStart;
+
+ DetectUSEQuery(SQL);
+
+ while QueryResult <> nil do begin
+ if FLib.PQnfields(QueryResult) > 0 then begin
+ // Statement returned a result set
+ Inc(FRowsFound, FLib.PQntuples(QueryResult));
+ if DoStoreResult then begin
+ SetLength(FLastRawResults, Length(FLastRawResults)+1);
+ FLastRawResults[Length(FLastRawResults)-1] := QueryResult;
+ end else begin
+ FLib.PQclear(QueryResult);
+ end;
+ end else begin
+ Inc(FRowsAffected, StrToIntDef(String(FLib.PQcmdTuples(QueryResult)), 0));
+ end;
+ if LastErrorMsg <> '' then begin
+ SetLength(FLastRawResults, 0);
+ Log(lcError, GetLastErrorMsg);
+ // Clear remaining results, to avoid "another command is already running"
+ while QueryResult <> nil do begin
+ FLib.PQclear(QueryResult);
+ QueryResult := FLib.PQgetResult(FHandle);
+ end;
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ // more results?
+ QueryResult := FLib.PQgetResult(FHandle);
+ end;
+
+ end;
+
+end;
+
+
+procedure TSQLiteConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+var
+ TimerStart, PrepareFlags: Cardinal;
+ Rows: TSQLiteGridRows;
+ Row: TGridRow;
+ Value: TGridValue;
+ QueryResult: Psqlite3_stmt;
+ QueryStatus: Integer;
+ i, OldRowsAffected: Integer;
+ CurrentSQL, NextSQL: PAnsiChar;
+ StepResult: Integer;
+begin
+ inherited;
+
+ CurrentSQL := PAnsiChar(UTF8Encode(SQL));
+ TimerStart := GetTickCount;
+ SetLength(FLastRawResults, 0);
+ OldRowsAffected := FLib.sqlite3_total_changes(FHandle); // Temporary: substract these later from total num
+
+ QueryResult := nil;
+ NextSQL := nil;
+ PrepareFlags := SQLITE_PREPARE_PERSISTENT;
+
+ while True do begin
+ QueryStatus := FLib.sqlite3_prepare_v3(FHandle, CurrentSQL, -1, PrepareFlags, QueryResult, NextSQL);
+ FLastQueryDuration := GetTickCount - TimerStart;
+ FLastQueryNetworkDuration := 0;
+
+ if QueryStatus <> SQLITE_OK then begin
+ Log(lcError, GetLastErrorMsg);
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ FRowsFound := 0;
+ if DoStoreResult and (FLib.sqlite3_column_count(QueryResult) > 0) then begin
+ Rows := TSQLiteGridRows.Create(Self);
+ StepResult := FLib.sqlite3_step(QueryResult);
+ while StepResult = SQLITE_ROW do begin
+ Row := TGridRow.Create;
+ for i:=0 to FLib.sqlite3_column_count(QueryResult)-1 do begin
+ Value := TGridValue.Create;
+ Value.OldText := DecodeAPIString(FLib.sqlite3_column_text(QueryResult, i));
+ Value.OldIsNull := FLib.sqlite3_column_text(QueryResult, i) = nil;
+ Row.Add(Value);
+ end;
+ Rows.Add(Row);
+ StepResult := FLib.sqlite3_step(QueryResult);
+ end;
+ Inc(FRowsFound, Rows.Count);
+ Rows.Statement := QueryResult;
+ SetLength(FLastRawResults, Length(FLastRawResults)+1);
+ FLastRawResults[Length(FLastRawResults)-1] := Rows;
+ end else begin
+ // Make one step through this non-result, otherwise SQLite does not seem to execute this query
+ StepResult := FLib.sqlite3_step(QueryResult);
+ FLib.sqlite3_finalize(QueryResult);
+ end;
+ FRowsAffected := FLib.sqlite3_total_changes(FHandle) - OldRowsAffected;
+ if not (StepResult in [SQLITE_OK, SQLITE_ROW, SQLITE_DONE, SQLITE_MISUSE]) then begin
+ SetLength(FLastRawResults, 0);
+ Log(lcError, GetLastErrorMsg);
+ // Todo: Step through and clear remaining results?
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ DetectUSEQuery(SQL);
+ CurrentSQL := NextSQL;
+ if Trim(CurrentSQL) = '' then
+ Break;
+ end;
+ FLastQueryNetworkDuration := GetTickCount - TimerStart;
+end;
+
+
+{procedure TInterbaseConnection.Query(SQL: String; DoStoreResult: Boolean=False; LogCategory: TDBLogCategory=lcSQL);
+var
+ TimerStart: Cardinal;
+ FdQuery: TFDQuery;
+begin
+ inherited;
+
+ TimerStart := GetTickCount;
+ SetLength(FLastRawResults, 0);
+ FdQuery := TFDQuery.Create(Self);
+ FdQuery.Connection := FFDHandle;
+ // Todo: suppress mouse cursor updates
+ try
+ FdQuery.ResourceOptions.CmdExecTimeout := Parameters.QueryTimeout;
+ if DoStoreResult then begin
+ FdQuery.SQL.Text := SQL;
+ if FdQuery.OpenOrExecute then begin
+ FRowsFound := FdQuery.RecordCount;
+ SetLength(FLastRawResults, Length(FLastRawResults)+1);
+ FLastRawResults[Length(FLastRawResults)-1] := FdQuery;
+ end;
+ end else begin
+ FdQuery.ExecSQL(SQL);
+ FRowsAffected := FdQuery.RowsAffected;
+ FdQuery.Free;
+ end;
+ FLastQueryDuration := GetTickCount - TimerStart;
+ FLastQueryNetworkDuration := 0;
+ except
+ on E:EFDDBEngineException do begin
+ SetLength(FLastRawResults, 0);
+ Log(lcError, GetLastErrorMsg + ' :: ' + E.Message);
+ raise EDbError.Create(GetLastErrorMsg);
+ end;
+ end;
+ FLastQueryNetworkDuration := GetTickCount - TimerStart;
+end;}
+
+
+function TDBConnection.GetLastResults: TDBQueryList;
+var
+ r: TDBQuery;
+ i: Integer;
+begin
+ Result := TDBQueryList.Create(False);
+ for i:=0 to ResultCount-1 do begin
+ r := Parameters.CreateQuery(Self);
+ r.SQL := FLastQuerySQL;
+ r.Execute(False, i);
+ Result.Add(r);
+ end;
+end;
+
+
+{function TAdoDBConnection.GetLastResults: TDBQueryList;
+var
+ r: TDBQuery;
+ i: Integer;
+ Batch: TSQLBatch;
+begin
+ Result := TDBQueryList.Create(False);
+ Batch := TSQLBatch.Create;
+ Batch.SQL := FLastQuerySQL;
+ for i:=Low(FLastRawResults) to High(FLastRawResults) do begin
+ r := Parameters.CreateQuery(Self);
+ if Batch.Count > i then
+ r.SQL := Batch[i].SQL
+ else // See http://www.heidisql.com/forum.php?t=21036
+ r.SQL := Batch.SQL;
+ r.Execute(False, i);
+ Result.Add(r);
+ end;
+ Batch.Free;
+end; }
+
+
+function TMySQLConnection.GetCreateCode(Obj: TDBObject): String;
+var
+ ColIdx: Integer;
+begin
+ if Obj.NodeType = lntView then begin
+ // Use our own baked CREATE VIEW code
+ Result := GetCreateViewCode(Obj.Database, Obj.Name);
+ Exit;
+ end;
+ case Obj.NodeType of
+ lntTable: ColIdx := 1;
+ lntFunction, lntProcedure, lntTrigger: ColIdx := 2;
+ lntEvent: ColIdx := 3;
+ else raise EDbError.CreateFmt(_('Unhandled list node type in %s.%s'), [ClassName, 'GetCreateCode']);
+ end;
+ Result := GetVar('SHOW CREATE '+Obj.ObjType.ToUpperInvariant+' '+QuoteIdent(Obj.Database)+'.'+QuoteIdent(Obj.Name), ColIdx);
+end;
+
+
+function TSQLiteConnection.GetCreateCode(Obj: TDBObject): String;
+begin
+ // PRAGMA table_info(customers):
+ // cid name type notnull dflt_value pk
+ // 0 CustomerId INTEGER 1 null 1
+ // 1 FirstName NVARCHAR(40) 1 null 0
+ case Obj.NodeType of
+ lntTable: begin
+ Result := GetVar('SELECT '+QuoteIdent('sql')+' FROM '+QuoteIdent(Obj.Database)+'.sqlite_master'+
+ ' WHERE '+QuoteIdent('type')+'='+EscapeString('table')+
+ ' AND name='+EscapeString(Obj.Name));
+ end;
+ else begin
+ // Let the generic method try to return code, which will most likely fail on SQLite
+ Result := inherited;
+ end;
+ end;
+end;
+
+
+{function TInterbaseConnection.GetCreateCode(Obj: TDBObject): String;
+begin
+ // Todo
+end;}
+
+
+function TMySQLConnection.GetCreateViewCode(Database, Name: String): String;
+var
+ ViewIS: TDBQuery;
+ Algorithm, CheckOption, SelectCode, Definer, SQLSecurity: String;
+ AlternativeSelectCode: String;
+ rx: TRegExpr;
+ Obj: TDBObject;
+begin
+ // Get CREATE VIEW code, which can throw privilege errors and errors due to
+ // references to renamed or deleted columns
+ try
+ Result := GetVar('SHOW CREATE VIEW '+QuoteIdent(Database)+'.'+QuoteIdent(Name), 1);
+ except
+ on E:EDbError do begin
+ ViewIS := GetResults('SELECT * FROM '+InfSch+'.VIEWS WHERE '+
+ 'TABLE_SCHEMA='+EscapeString(Database)+' AND TABLE_NAME='+EscapeString(Name));
+ Result := 'CREATE ';
+ if ViewIS.Col('DEFINER') <> '' then
+ Result := Result + 'DEFINER='+QuoteIdent(ViewIS.Col('DEFINER'), True, '@')+' ';
+ Result := Result + 'VIEW '+QuoteIdent(Name)+' AS '+ViewIS.Col('VIEW_DEFINITION')+' ';
+ if ViewIS.Col('CHECK_OPTION') <> 'NONE' then
+ Result := Result + 'WITH '+Uppercase(ViewIS.Col('CHECK_OPTION'))+' CHECK OPTION';
+ end;
+ end;
+ try
+ // Try to fetch original VIEW code from .frm file
+ AlternativeSelectCode := GetVar('SELECT CAST(LOAD_FILE('+
+ 'CONCAT('+
+ 'IFNULL(@@GLOBAL.datadir, CONCAT(@@GLOBAL.basedir, '+EscapeString('data/')+')), '+
+ EscapeString(Database+'/'+Name+'.frm')+')'+
+ ') AS CHAR CHARACTER SET utf8)');
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ rx.ModifierG := False;
+ rx.Expression := '\nsource\=(.+)\n\w+\=';
+ if rx.Exec(AlternativeSelectCode) then begin
+ // Put pieces of CREATE VIEW together
+ Obj := FindObject(Database, Name);
+ ParseViewStructure(Result, Obj, Algorithm, Definer, SQLSecurity, CheckOption, SelectCode);
+ AlternativeSelectCode := UnescapeString(rx.Match[1]);
+ Result := 'CREATE ';
+ if Algorithm <> '' then
+ Result := Result + 'ALGORITHM='+Uppercase(Algorithm)+' ';
+ if Definer <> '' then
+ Result := Result + 'DEFINER='+QuoteIdent(Definer, True, '@')+' ';
+ if not SQLSecurity.IsEmpty then
+ Result := Result + 'SQL SECURITY '+SQLSecurity+' ';
+ Result := Result + 'VIEW '+Obj.QuotedName+' AS '+AlternativeSelectCode+' ';
+ // WITH .. CHECK OPTION is already contained in the source
+ end;
+ rx.Free;
+ except
+ // Do not raise if that didn't work
+ on E:EDbError do;
+ end;
+end;
+
+
+function TDBConnection.GetCreateCode(Obj: TDBObject): String;
+var
+ ProcDetails: TDBQuery;
+ DataType: String;
+ ArgNames, ArgTypes, Arguments: TStringList;
+ Rows: TStringList;
+ i: Integer;
+ TableCols: TTableColumnList;
+ TableCol: TTableColumn;
+ TableKeys: TTableKeyList;
+ TableKey: TTableKey;
+ TableForeignKeys: TForeignKeyList;
+ TableForeignKey: TForeignKey;
+ TableCheckConstraints: TCheckConstraintList;
+ TableCheckConstraint: TCheckConstraint;
+begin
+ case Obj.NodeType of
+ lntTable: begin
+ Result := 'CREATE TABLE '+QuoteIdent(Obj.Name)+' (';
+ TableCols := Obj.GetTableColumns;
+ for TableCol in TableCols do begin
+ Result := Result + sLineBreak + CodeIndent + TableCol.SQLCode + ',';
+ end;
+ TableCols.Free;
+
+ TableKeys := Obj.GetTableKeys;
+ for TableKey in TableKeys do begin
+ if TableKey.InsideCreateCode then
+ Result := Result + sLineBreak + CodeIndent + TableKey.SQLCode + ',';
+ end;
+ TableKeys.Free;
+
+ TableForeignKeys := Obj.GetTableForeignKeys;
+ for TableForeignKey in TableForeignKeys do begin
+ Result := Result + sLineBreak + CodeIndent + TableForeignKey.SQLCode(True) + ',';
+ end;
+ TableForeignKeys.Free;
+
+ TableCheckConstraints := Obj.GetTableCheckConstraints;
+ for TableCheckConstraint in TableCheckConstraints do begin
+ Result := Result + sLineBreak + CodeIndent + TableCheckConstraint.SQLCode + ',';
+ end;
+ TableCheckConstraints.Free;
+
+ Delete(Result, Length(Result), 1);
+ Result := Result + sLineBreak + ')';
+
+ TableKeys := Obj.GetTableKeys;
+ for TableKey in TableKeys do begin
+ if not TableKey.InsideCreateCode then begin
+ if TableKeys.IndexOf(TableKey) = 0 then
+ Result := Result + ';';
+ Result := Result + sLineBreak + TableKey.SQLCode + ';';
+ end;
+ end;
+ TableKeys.Free;
+
+ end;
+
+ lntView: begin
+ case FParameters.NetTypeGroup of
+ ngPgSQL: begin
+ // Prefer pg_catalog tables. See http://www.heidisql.com/forum.php?t=16213#p16685
+ Result := 'CREATE VIEW ' + QuoteIdent(Obj.Name) + ' AS ' + GetVar('SELECT '+QuoteIdent('definition')+
+ ' FROM '+QuoteIdent('pg_views')+
+ ' WHERE '+QuoteIdent('viewname')+'='+EscapeString(Obj.Name)+
+ ' AND '+QuoteIdent('schemaname')+'='+EscapeString(Obj.Schema)
+ );
+ end;
+ ngMSSQL: begin
+ // Overcome 4000 character limit in IS.VIEW_DEFINITION
+ // See http://www.heidisql.com/forum.php?t=21097
+ Result := GetVar('SELECT '+QuoteIdent('MODS')+'.'+QuoteIdent('DEFINITION')+
+ ' FROM '+QuoteIdent('SYS')+'.'+QuoteIdent('OBJECTS')+' '+QuoteIdent('OBJ')+
+ ' JOIN '+QuoteIdent('SYS')+'.'+QuoteIdent('SQL_MODULES')+' AS '+QuoteIdent('MODS')+' ON '+QuoteIdent('OBJ')+'.'+QuoteIdent('OBJECT_ID')+'='+QuoteIdent('MODS')+'.'+QuoteIdent('OBJECT_ID')+
+ ' JOIN '+QuoteIdent('SYS')+'.'+QuoteIdent('SCHEMAS')+' AS '+QuoteIdent('SCHS')+' ON '+QuoteIdent('OBJ')+'.'+QuoteIdent('SCHEMA_ID')+'='+QuoteIdent('SCHS')+'.'+QuoteIdent('SCHEMA_ID')+
+ ' WHERE '+QuoteIdent('OBJ')+'.'+QuoteIdent('TYPE')+'='+EscapeString('V')+
+ ' AND '+QuoteIdent('SCHS')+'.'+QuoteIdent('NAME')+'='+EscapeString(Obj.Schema)+
+ ' AND '+QuoteIdent('OBJ')+'.'+QuoteIdent('NAME')+'='+EscapeString(Obj.Name)
+ );
+ end;
+ else begin
+ if not Obj.FCreateCode.IsEmpty then begin
+ // SQlite views go here
+ Result := Obj.FCreateCode;
+ end
+ else begin
+ Result := GetVar('SELECT VIEW_DEFINITION'+
+ ' FROM '+InfSch+'.VIEWS'+
+ ' WHERE TABLE_NAME='+EscapeString(Obj.Name)+
+ ' AND '+Obj.SchemaClauseIS('TABLE')
+ );
+ end;
+ end;
+ end;
+ end;
+
+ lntFunction: begin
+ case Parameters.NetTypeGroup of
+ ngMSSQL: begin
+ // Tested on MS SQL 8.0 and 11.0
+ // See http://www.heidisql.com/forum.php?t=12495
+ if not Obj.Schema.IsEmpty then
+ Rows := GetCol('EXEC sp_helptext '+EscapeString(Obj.Schema+'.'+Obj.Name))
+ else
+ Rows := GetCol('EXEC sp_helptext '+EscapeString(Obj.Database+'.'+Obj.Name));
+ // Do not use Rows.Text, as the rows already include a trailing linefeed
+ Result := Implode('', Rows);
+ Rows.Free;
+ end;
+ ngPgSQL: begin
+ Result := 'CREATE FUNCTION '+QuoteIdent(Obj.Name);
+ ProcDetails := GetResults('SELECT '+
+ QuoteIdent('p')+'.'+QuoteIdent('prosrc')+', '+
+ QuoteIdent('p')+'.'+QuoteIdent('proargnames')+', '+
+ QuoteIdent('p')+'.'+QuoteIdent('proargtypes')+', '+
+ QuoteIdent('p')+'.'+QuoteIdent('prorettype')+' '+
+ 'FROM '+QuoteIdent('pg_catalog')+'.'+QuoteIdent('pg_namespace')+' AS '+QuoteIdent('n')+' '+
+ 'JOIN '+QuoteIdent('pg_catalog')+'.'+QuoteIdent('pg_proc')+' AS '+QuoteIdent('p')+' ON '+QuoteIdent('p')+'.'+QuoteIdent('pronamespace')+' = '+QuoteIdent('n')+'.'+QuoteIdent('oid')+' '+
+ 'WHERE '+
+ QuoteIdent('n')+'.'+QuoteIdent('nspname')+'='+EscapeString(Obj.Database)+
+ 'AND '+QuoteIdent('p')+'.'+QuoteIdent('proname')+'='+EscapeString(Obj.Name)+
+ 'AND '+QuoteIdent('p')+'.'+QuoteIdent('proargtypes')+'='+EscapeString(Obj.ArgTypes)
+ );
+ ArgNames := Explode(',', Copy(ProcDetails.Col('proargnames'), 2, Length(ProcDetails.Col('proargnames'))-2));
+ ArgTypes := Explode(' ', Copy(ProcDetails.Col('proargtypes'), 1, Length(ProcDetails.Col('proargtypes'))));
+ Arguments := TStringList.Create;
+ for i:=0 to ArgNames.Count-1 do begin
+ if ArgTypes.Count > i then
+ DataType := GetDatatypeByNativeType(MakeInt(ArgTypes[i]), ArgNames[i]).Name
+ else
+ DataType := '';
+ Arguments.Add(ArgNames[i] + ' ' + DataType);
+ end;
+ Result := Result + '(' + Implode(', ', Arguments) + ') '+
+ 'RETURNS '+GetDatatypeByNativeType(MakeInt(ProcDetails.Col('prorettype'))).Name+' '+
+ 'AS $$ '+ProcDetails.Col('prosrc')+' $$'
+ // TODO: 'LANGUAGE SQL IMMUTABLE STRICT'
+ ;
+ end;
+ else begin
+ Result := GetVar('SELECT ROUTINE_DEFINITION'+
+ ' FROM '+InfSch+'.ROUTINES'+
+ ' WHERE ROUTINE_NAME='+EscapeString(Obj.Name)+
+ ' AND ROUTINE_TYPE='+EscapeString('FUNCTION')+
+ ' AND '+Obj.SchemaClauseIS('ROUTINE')
+ );
+ end;
+ end;
+ end;
+
+ lntProcedure: begin
+ case Parameters.NetTypeGroup of
+ ngMSSQL: begin
+ // See comments above
+ if not Obj.Schema.IsEmpty then
+ Rows := GetCol('EXEC sp_helptext '+EscapeString(Obj.Schema+'.'+Obj.Name))
+ else
+ Rows := GetCol('EXEC sp_helptext '+EscapeString(Obj.Database+'.'+Obj.Name));
+ Result := Implode('', Rows);
+ Rows.Free;
+ end;
+ else begin
+ Result := GetVar('SELECT ROUTINE_DEFINITION'+
+ ' FROM '+InfSch+'.ROUTINES'+
+ ' WHERE ROUTINE_NAME='+EscapeString(Obj.Name)+
+ ' AND ROUTINE_TYPE='+EscapeString('PROCEDURE')+
+ ' AND '+Obj.SchemaClauseIS('ROUTINE')
+ );
+ end;
+ end;
+ end;
+
+ end;
+
+end;
+
+
+procedure TDBConnection.PrefetchCreateCode(Objects: TDBObjectList);
+var
+ Queries: TStringList;
+ Obj: TDBObject;
+ UseIt: Boolean;
+begin
+ // Cache some queries used in GetCreateCode for mass operations. See TMainForm.SynCompletionProposalExecute
+ Queries := TStringList.Create;
+ for Obj in Objects do begin
+ case Parameters.NetTypeGroup of
+ ngMySQL: begin
+ UseIt := Obj.NodeType <> lntView;
+ // SHOW CREATE TRIGGER was introduced in MySQL 5.1.21
+ // See #111
+ if Obj.NodeType = lntTrigger then
+ UseIt := UseIt and Has(frShowCreateTrigger);
+ if UseIt then
+ Queries.Add('SHOW CREATE '+UpperCase(Obj.ObjType)+' '+QuoteIdent(Obj.Database)+'.'+QuoteIdent(Obj.Name));
+ end;
+ ngMSSQL: begin
+ if Obj.NodeType in [lntFunction, lntProcedure] then begin
+ if not Obj.Schema.IsEmpty then
+ Queries.Add('EXEC sp_helptext '+EscapeString(Obj.Schema+'.'+Obj.Name))
+ else
+ Queries.Add('EXEC sp_helptext '+EscapeString(Obj.Database+'.'+Obj.Name))
+ end;
+ end;
+ else begin
+ Log(lcDebug, 'No query logic for PrefetchCreateCode');
+ end;
+ end;
+ end;
+ if Queries.Count > 0 then try
+ PrefetchResults(Implode(';', Queries));
+ except
+ on E:EDbError do;
+ end;
+
+end;
+
+
+{**
+ Set "Database" property and select that db if connected
+}
+procedure TDBConnection.SetDatabase(Value: String);
+var
+ s: String;
+ UseQuery: String;
+begin
+ Log(lcDebug, 'SetDatabase('+Value+'), FDatabase: '+FDatabase);
+ if Value <> FDatabase then begin
+ if Value = '' then begin
+ FDatabase := Value;
+ if Assigned(FOnDatabaseChanged) then
+ FOnDatabaseChanged(Self, Value);
+ end else begin
+ if FParameters.NetTypeGroup = ngPgSQL then begin
+ s := EscapeString(Value);
+ // Get schema with the same name as user name in search path
+ // See https://www.heidisql.com/forum.php?t=34558
+ s := s + ', ' + EscapeString('$user');
+ // Always keep public schema in search path, so one can use procedures from it without prefixing
+ // See http://www.heidisql.com/forum.php?t=18581#p18905
+ if Value <> 'public' then
+ s := s + ', ' + EscapeString('public');
+ end else
+ s := QuoteIdent(Value);
+ UseQuery := GetSQLSpecifity(spUSEQuery);
+ if not UseQuery.IsEmpty then begin
+ Query(GetSQLSpecifity(spUSEQuery, [s]), False);
+ end;
+ FDatabase := DeQuoteIdent(Value);
+ if Assigned(FOnDatabaseChanged) then
+ FOnDatabaseChanged(Self, Value);
+ end;
+
+ // Save last used database in session, see #983
+ if not FParameters.SessionPath.Trim.IsEmpty then begin
+ AppSettings.SessionPath := FParameters.SessionPath;
+ AppSettings.WriteString(asLastUsedDB, Value);
+ end;
+
+ // Some session variables are specific to a database, like collation_database, see #1030
+ FreeAndNil(FSessionVariables);
+
+ if Assigned(FOnObjectnamesChanged) then
+ FOnObjectnamesChanged(Self, FDatabase);
+ end;
+end;
+
+
+procedure TDBConnection.DetectUSEQuery(SQL: String);
+var
+ rx: TRegExpr;
+ Quotes: String;
+ NewDb: String;
+begin
+ // Detect query for switching current working database or schema
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ rx.Expression := '^'+GetSQLSpecifity(spUSEQuery);
+ Quotes := QuoteRegExprMetaChars(FQuoteChars+''';');
+ rx.Expression := StringReplace(rx.Expression, ' ', '\s+', [rfReplaceAll]);
+ rx.Expression := StringReplace(rx.Expression, '%s', '['+Quotes+']?([^'+Quotes+']+)['+Quotes+']*', [rfReplaceAll]);
+ if rx.Exec(SQL) then begin
+ NewDb := Trim(rx.Match[1]);
+ NewDb := DeQuoteIdent(NewDb);
+ if (not NewDb.IsEmpty) and (NewDb <> FDatabase) then begin
+ FDatabase := NewDb;
+ Log(lcDebug, f_('Database "%s" selected', [FDatabase]));
+ if Assigned(FOnDatabaseChanged) then
+ FOnDatabaseChanged(Self, Database);
+ end;
+ end;
+ rx.Free;
+end;
+
+
+{**
+ Return current thread id
+ Supports 64bit process numbers on long running servers: https://dev.mysql.com/doc/refman/8.0/en/mysql-thread-id.html
+ ... while ProxySQL does not support CONNECTION_ID()
+}
+function TMySQLConnection.GetThreadId: Int64;
+begin
+ if FThreadId = 0 then begin
+ Ping(False);
+ if FActive then begin
+ if Parameters.IsProxySQLAdmin then
+ FThreadID := FLib.mysql_thread_id(FHandle)
+ else
+ FThreadID := StrToInt64Def(GetVar('SELECT CONNECTION_ID()'), 0);
+ end;
+ end;
+ Result := FThreadID;
+end;
+
+
+{function TAdoDBConnection.GetThreadId: Int64;
+begin
+ if FThreadId = 0 then begin
+ Ping(False);
+ if FActive then
+ FThreadID := StrToInt64Def(GetVar('SELECT @@SPID'), 0);
+ end;
+ Result := FThreadID;
+end;}
+
+
+function TPGConnection.GetThreadId: Int64;
+begin
+ if FThreadId = 0 then begin
+ Ping(False);
+ if FActive then
+ FThreadID := FLib.PQbackendPID(FHandle);
+ end;
+ Result := FThreadID;
+end;
+
+
+function TSQLiteConnection.GetThreadId: Int64;
+begin
+ if FThreadId = 0 then begin
+ Ping(False);
+ if FActive then // We return the application process id, as there is no connection pid in SQLite
+ FThreadID := GetCurrentProcessId;
+ end;
+ Result := FThreadID;
+end;
+
+
+{function TInterbaseConnection.GetThreadId: Int64;
+begin
+ // Todo
+ Result := 0;
+end;}
+
+
+{**
+ Return currently used character set
+}
+function TDBConnection.GetCharacterSet: String;
+begin
+ Result := '';
+end;
+
+
+function TMySQLConnection.GetCharacterSet: String;
+begin
+ Result := DecodeAPIString(FLib.mysql_character_set_name(FHandle));
+end;
+
+
+{**
+ Switch character set
+}
+procedure TDBConnection.SetCharacterSet(CharsetName: String);
+begin
+ // Nothing to do by default
+end;
+
+
+procedure TMySQLConnection.SetCharacterSet(CharsetName: String);
+var
+ Return: Integer;
+begin
+ FStatementNum := 0;
+ Log(lcInfo, 'Changing character set from '+CharacterSet+' to '+CharsetName);
+ Return := FLib.mysql_set_character_set(FHandle, PAnsiChar(Utf8Encode(CharsetName)));
+ if Return <> 0 then
+ raise EDbError.Create(LastErrorMsg)
+ else
+ FIsUnicode := CharsetName.StartsWith('utf', True);
+end;
+
+
+procedure TPGConnection.SetCharacterSet(CharsetName: String);
+begin
+ // See issue #22
+ Query('SET CLIENT_ENCODING TO ' + EscapeString('UTF8'));
+end;
+
+
+function TMySQLConnection.GetLastErrorCode: Cardinal;
+begin
+ Result := FLib.mysql_errno(FHandle);
+end;
+
+
+{function TAdoDBConnection.GetLastErrorCode: Cardinal;
+begin
+ // SELECT @@SPID throws errors without filling the error pool. See issue #2684.
+ if FAdoHandle.Errors.Count > 0 then
+ Result := FAdoHandle.Errors[FAdoHandle.Errors.Count-1].NativeError
+ else
+ Result := 0;
+end; }
+
+
+function TPgConnection.GetLastErrorCode: Cardinal;
+begin
+ Result := Cardinal(FLib.PQstatus(FHandle));
+end;
+
+
+function TSQLiteConnection.GetLastErrorCode: Cardinal;
+begin
+ Result := FLib.sqlite3_errcode(FHandle);
+end;
+
+
+{function TInterbaseConnection.GetLastErrorCode: Cardinal;
+begin
+ // Note: there seem to be negative codes
+ Result := Abs(FLastErrorCode);
+end;}
+
+
+{procedure TInterbaseConnection.OnFdError(ASender: TObject; AInitiator: TObject; var AException: Exception);
+var
+ oExc: EFDDBEngineException;
+begin
+ if AException is EFDDBEngineException then begin
+ oExc := EFDDBEngineException(AException);
+ FLastErrorCode := oExc.ErrorCode;
+ FLastError := oExc.Message;
+ end;
+end;}
+
+
+
+{**
+ Return the last error nicely formatted
+}
+function TMySQLConnection.GetLastErrorMsg: String;
+var
+ Msg, Additional: String;
+ rx: TRegExpr;
+begin
+ Result := '';
+ Additional := '';
+
+ Msg := DecodeAPIString(FLib.mysql_error(FHandle));
+
+ // Find "(errno: 123)" in message and add more meaningful message from perror.exe
+ rx := TRegExpr.Create;
+ rx.Expression := '.+\(errno\:\s+(\d+)\)';
+ if rx.Exec(Msg) then begin
+ Additional := MySQLErrorCodes.Values[rx.Match[1]];
+ end;
+ rx.Free;
+
+ if Additional <> '' then begin
+ Msg := Msg + sLineBreak + sLineBreak + Additional;
+ end;
+
+ case FStatementNum of
+ 0: Result := Msg;
+ 1: Result := f_(MsgSQLError, [LastErrorCode, Msg]);
+ else Result := f_(MsgSQLErrorMultiStatements, [LastErrorCode, FStatementNum, Msg]);
+ end;
+end;
+
+
+{function TAdoDBConnection.GetLastErrorMsg: String;
+var
+ Msg: String;
+ rx: TRegExpr;
+ E: Error;
+begin
+ if FAdoHandle.Errors.Count > 0 then begin
+ E := FAdoHandle.Errors[FAdoHandle.Errors.Count-1];
+ Msg := E.Description;
+ // Remove stuff from driver in message "[DBNETLIB][ConnectionOpen (Connect()).]"
+ rx := TRegExpr.Create;
+ rx.Expression := '^\[DBNETLIB\]\[.*\](.+)$';
+ if rx.Exec(Msg) then
+ Msg := rx.Match[1];
+ rx.Free;
+ end else
+ Msg := _('unknown');
+ if (FLastError <> '') and (Pos(FLastError, Msg) = 0) then
+ Msg := FLastError + CRLF + Msg;
+ Result := f_(MsgSQLError, [LastErrorCode, Msg]);
+end;}
+
+
+function TPgConnection.GetLastErrorMsg: String;
+begin
+ // Todo: use MsgSQLError formatting constant
+ Result := DecodeAPIString(FLib.PQerrorMessage(FHandle));
+ Result := Trim(Result);
+end;
+
+
+function TSQLiteConnection.GetLastErrorMsg: String;
+begin
+ Result := DecodeAPIString(FLib.sqlite3_errmsg(FHandle));
+ Result := f_(MsgSQLError, [LastErrorCode, Result]);
+end;
+
+
+{function TInterbaseConnection.GetLastErrorMsg: String;
+begin
+ Result := f_(MsgSQLError, [LastErrorCode, FLastError]);
+end;}
+
+
+
+{**
+ Get version string as normalized integer
+ "5.1.12-beta-community-123" => 50112
+}
+function TDBConnection.ServerVersionInt: Integer;
+var
+ rx: TRegExpr;
+ v1, v2: String;
+begin
+ Result := 0;
+ rx := TRegExpr.Create;
+ case FParameters.NetTypeGroup of
+ ngMySQL, ngPgSQL, ngSQLite, ngInterbase: begin
+ rx.Expression := '(\d+)\.(\d+)(\.(\d+))?';
+ if rx.Exec(FServerVersionUntouched) then begin
+ Result := StrToIntDef(rx.Match[1], 0) *10000 +
+ StrToIntDef(rx.Match[2], 0) *100 +
+ StrToIntDef(rx.Match[4], 0);
+ end;
+ end;
+ ngMSSQL: begin
+ // See http://support.microsoft.com/kb/321185
+ // "Microsoft SQL Server 7.00 - 7.00.1094 (Intel X86)" ==> 700
+ // "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" ==> 1000
+ // "Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (Intel X86)" ==> 1050
+ rx.ModifierG := False;
+ rx.Expression := '\s(\d+)\.(\d+)\D';
+ if rx.Exec(FServerVersionUntouched) then begin
+ v1 := rx.Match[1];
+ v2 := rx.Match[2];
+ Result := StrToIntDef(v1, 0) *100 +
+ StrToIntDef(v2, 0);
+ end else begin
+ rx.Expression := '(\d+)[,\.](\d+)[,\.](\d+)[,\.](\d+)';
+ if rx.Exec(FServerVersionUntouched) then begin
+ Result := StrToIntDef(rx.Match[1], 0) *100 +
+ StrToIntDef(rx.Match[2], 0);
+ end;
+ end;
+ end;
+ else begin
+ raise EDbError.CreateFmt(_(MsgUnhandledNetType), [Integer(FParameters.NetType)]);
+ end;
+ end;
+ rx.Free;
+end;
+
+
+function TDBConnection.ServerVersionStr: String;
+var
+ v: String;
+ major, minor, build: Integer;
+begin
+ case FParameters.NetTypeGroup of
+ ngMySQL, ngPgSQL, ngSQLite, ngInterbase: begin
+ v := IntToStr(ServerVersionInt);
+ major := StrToIntDef(Copy(v, 1, Length(v)-4), 0);
+ minor := StrToIntDef(Copy(v, Length(v)-3, 2), 0);
+ build := StrToIntDef(Copy(v, Length(v)-1, 2), 0);
+ Result := IntToStr(major) + '.' + IntToStr(minor) + '.' + IntToStr(build);
+ end;
+ ngMSSQL: begin
+ major := ServerVersionInt div 100;
+ minor := ServerVersionInt mod (ServerVersionInt div 100);
+ Result := IntToStr(major) + '.' + IntToStr(minor);
+ end;
+ else begin
+ raise EDbError.CreateFmt(_(MsgUnhandledNetType), [Integer(FParameters.NetType)]);
+ end;
+ end;
+end;
+
+
+function TDBConnection.NdbClusterVersionInt: Integer;
+var
+ rx: TRegExpr;
+begin
+ // 5.6.17-ndb-7.3.5
+ Result := 0;
+ rx := TRegExpr.Create;
+ rx.Expression := '[\d+\.]+-ndb-(\d+)\.(\d+)\.(\d+)';
+ if rx.Exec(FServerVersionUntouched) then begin
+ Result := StrToIntDef(rx.Match[1], 0) *10000 +
+ StrToIntDef(rx.Match[2], 0) *100 +
+ StrToIntDef(rx.Match[3], 0);
+ end;
+ rx.Free;
+end;
+
+
+procedure TDBConnection.ShowWarnings;
+begin
+ // Do nothing by default. SHOW WARNINGS is MySQL only.
+end;
+
+
+procedure TMySQLConnection.ShowWarnings;
+var
+ Warnings: TDBQuery;
+ Info: String;
+begin
+ // Log warnings
+ // SHOW WARNINGS is implemented as of MySQL 4.1.0
+ if (WarningCount > 0) and Has(frShowWarnings) then begin
+ Warnings := GetResults('SHOW WARNINGS');
+ while not Warnings.Eof do begin
+ Log(lcError, _(Warnings.Col('Level')) + ': ('+Warnings.Col('Code')+') ' + Warnings.Col('Message'));
+ Warnings.Next;
+ end;
+ Warnings.Free;
+ end;
+ Info := DecodeAPIString(FLib.mysql_info(FHandle));
+ if not Info.IsEmpty then begin
+ Log(lcInfo, _(SLogPrefixInfo) + ': ' + Info);
+ end;
+end;
+
+
+function TDBConnection.GetAllDatabases: TStringList;
+begin
+ // Get user passed delimited list
+ // Ignore value in case of ntSQLiteEncrypted, when AllDatabasesStr holds encryption parameters
+ if not Assigned(FAllDatabases) then begin
+ if (FParameters.AllDatabasesStr <> '') and (not FParameters.IsAnySQLite) then begin
+ FAllDatabases := FParameters.AllDatabasesList;
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ end;
+ end;
+ Result := FAllDatabases;
+end;
+
+
+function TMySQLConnection.GetAllDatabases: TStringList;
+begin
+ Result := inherited;
+ if not Assigned(Result) then begin
+ try
+ FAllDatabases := GetCol('SHOW DATABASES', IfThen(Parameters.IsProxySQLAdmin, 1, 0));
+ except on E:EDbError do
+ try
+ FAllDatabases := GetCol('SELECT '+QuoteIdent('SCHEMA_NAME')+' FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent('SCHEMATA')+' ORDER BY '+QuoteIdent('SCHEMA_NAME'));
+ except
+ on E:EDbError do begin
+ FAllDatabases := TStringList.Create;
+ Log(lcError, f_('Database names not available due to missing privileges for user %s.', [CurrentUserHostCombination]));
+ end;
+ end;
+ end;
+ Result := FAllDatabases;
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ end;
+end;
+
+
+{function TAdoDBConnection.GetAllDatabases: TStringList;
+begin
+ Result := inherited;
+ if not Assigned(Result) then begin
+ try
+ FAllDatabases := GetCol('SELECT '+QuoteIdent('name')+' FROM '+GetSQLSpecifity(spDatabaseTable)+' ORDER BY '+QuoteIdent('name'));
+ except on E:EDbError do
+ FAllDatabases := TStringList.Create;
+ end;
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ Result := FAllDatabases;
+ end;
+end;}
+
+
+function TPGConnection.GetAllDatabases: TStringList;
+var
+ DbQuery: String;
+begin
+ // In PostgreSQL, we display schemata, not databases.
+ // The AllDatabasesStr is used to set the single database name
+ if not Assigned(FAllDatabases) then begin
+ try
+ // Query is.schemata when using schemata, for databases use pg_database
+ //FAllDatabases := GetCol('SELECT datname FROM pg_database WHERE datistemplate=FALSE');
+ DbQuery := 'SELECT '+QuoteIdent('nspname')+
+ ' FROM '+QuoteIdent('pg_catalog')+'.'+QuoteIdent('pg_namespace');
+ if Parameters.IsRedshift then begin
+ DbQuery := DbQuery + ' WHERE '+QuoteIdent('nspowner')+' != 1'+
+ ' OR '+QuoteIdent('nspname')+' IN ('+EscapeString('pg_catalog')+', '+EscapeString('public')+', '+EscapeString(InfSch)+')';
+ end;
+ DbQuery := DbQuery + ' ORDER BY '+QuoteIdent('nspname');
+ FAllDatabases := GetCol(DbQuery);
+ except on E:EDbError do
+ FAllDatabases := TStringList.Create;
+ end;
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ end;
+ Result := FAllDatabases;
+end;
+
+
+function TSQLiteConnection.GetAllDatabases: TStringList;
+var
+ DbQuery: String;
+begin
+ Result := inherited;
+ if not Assigned(Result) then begin
+ try
+ DbQuery := 'SELECT * FROM pragma_database_list';
+ FAllDatabases := GetCol(DbQuery, 1);
+ except on E:EDbError do
+ FAllDatabases := TStringList.Create;
+ end;
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ Result := FAllDatabases;
+ end;
+end;
+
+
+{function TInterbaseConnection.GetAllDatabases: TStringList;
+begin
+ Result := inherited;
+ if not Assigned(Result) then begin
+ FAllDatabases := TStringList.Create;
+ FFDHandle.GetCatalogNames('', FAllDatabases);
+ ApplyIgnoreDatabasePattern(FAllDatabases);
+ Result := FAllDatabases;
+ end;
+end;}
+
+
+function TDBConnection.RefreshAllDatabases: TStringList;
+begin
+ FreeAndNil(FAllDatabases);
+ Result := AllDatabases;
+end;
+
+
+procedure TDBConnection.ApplyIgnoreDatabasePattern(Dbs: TStringList);
+var
+ i: Integer;
+begin
+ if Parameters.IgnoreDatabasePattern.IsEmpty then
+ Exit;
+
+ try
+ for i:=Dbs.Count-1 downto 0 do begin
+ if ExecRegExpr(Parameters.IgnoreDatabasePattern, Dbs[i]) then
+ Dbs.Delete(i);
+ end;
+ except
+ on E:ERegExpr do
+ Log(lcError, 'Error in ignore database pattern: ' + E.Message);
+ end;
+end;
+
+
+function TDBConnection.GetResults(SQL: String): TDBQuery;
+var
+ Query: TDBQuery;
+begin
+ Result := nil;
+
+ // Look up query result in cache
+ if Assigned(FPrefetchResults) then begin
+ for Query in FPrefetchResults do begin
+ if Query.SQL = SQL then begin
+ Result := Query;
+ Log(lcDebug, 'Using cached result for query: '+StrEllipsis(SQL, 100));
+ Break;
+ end;
+ end;
+ end;
+
+ // Fire query
+ if Result = nil then begin
+ Result := Parameters.CreateQuery(Self);
+ Result.SQL := SQL;
+ try
+ Result.Execute;
+ except
+ FreeAndNil(Result);
+ Raise;
+ end;
+ end;
+end;
+
+
+procedure TDBConnection.PrefetchResults(SQL: String);
+var
+ LastResults: TDBQueryList;
+ Batch: TSQLBatch;
+ i: Integer;
+begin
+ Query(SQL, True);
+ Batch := TSQLBatch.Create;
+ Batch.SQL := SQL;
+ FreeAndNil(FPrefetchResults);
+ FPrefetchResults := TDBQueryList.Create(True);
+ LastResults := GetLastResults;
+ for i:=0 to LastResults.Count-1 do begin
+ FPrefetchResults.Add(LastResults[i]);
+ if Batch.Count > i then
+ FPrefetchResults[i].SQL := Batch[i].SQL;
+ end;
+ Batch.Free;
+end;
+
+
+procedure TDBConnection.FreeResults(Results: TDBQuery);
+begin
+ // Free query result if it is not in prefetch cache
+ if (not Assigned(FPrefetchResults)) or (not FPrefetchResults.Contains(Results)) then
+ FreeAndNil(Results);
+end;
+
+
+{**
+ Call log event if assigned to object
+ If running a thread, log to queue and let the main thread later do logging
+}
+procedure TDBConnection.Log(Category: TDBLogCategory; Msg: String);
+var
+ LogMessage,
+ FilePath: String;
+ DbObj: TDBObject;
+ LogFile: Text;
+
+ function IsDdlQuery: Boolean;
+ begin
+ Result := Msg.StartsWith('CREATE', True)
+ or Msg.StartsWith('ALTER', True)
+ or Msg.StartsWith('DROP', True)
+ or Msg.StartsWith('TRUNCATE', True)
+ or Msg.StartsWith('COMMENT', True)
+ or Msg.StartsWith('RENAME', True)
+ ;
+ end;
+
+ function IsDmlQuery: Boolean;
+ begin
+ Result := Msg.StartsWith('INSERT', True)
+ or Msg.StartsWith('UPDATE', True)
+ or Msg.StartsWith('DELETE', True)
+ or Msg.StartsWith('UPSERT', True)
+ ;
+ end;
+
+begin
+ // If in a thread, synchronize logging with the main thread. Logging within a thread
+ // causes SynEdit to throw exceptions left and right.
+ if IsLockedByThread and (FLockedByThread.ThreadID = GetCurrentThreadID) then begin
+ (FLockedByThread as TQueryThread).LogFromThread(Msg, Category);
+ Exit;
+ end;
+
+ if Assigned(FOnLog) then begin
+ LogMessage := Msg;
+ if FLogPrefix <> '' then
+ LogMessage := '['+FLogPrefix+'] ' + LogMessage;
+ FOnLog(LogMessage, Category, Self);
+ end;
+
+ if Category in [lcSQL, lcUserFiredSQL, lcScript] then begin
+ if (Parameters.LogFileDdl and IsDdlQuery)
+ or (Parameters.LogFileDml and IsDmlQuery)
+ then begin
+ // Log DDL queries to migration file
+ DbObj := TDBObject.Create(Self);
+ DbObj.Database := IfThen(FDatabase.IsEmpty, 'nodb', FDatabase);
+ FilePath := GetOutputFilename(Parameters.LogFilePath, DbObj);
+ DbObj.Free;
+ try
+ ForceDirectories(ExtractFileDir(FilePath));
+ AssignFile(LogFile, FilePath); // TStreamWriter.Create(FilePath, True, UTF8NoBOMEncoding);
+ Append(LogFile);
+ WriteLn(LogFile, Msg + ';');
+ Close(LogFile);
+ except
+ on E:Exception do begin
+ Parameters.LogFileDdl := False;
+ Parameters.LogFileDml := False;
+ Log(lcError, E.Message);
+ Log(lcInfo, _('Logging disabled'));
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+{**
+ Escapes a string for usage in SQL queries
+ - single-backslashes which represent normal parts of the text and not escape-sequences
+ - characters which MySQL doesn't strictly care about, but which might confuse editors etc.
+ - single and double quotes in a text string
+ - joker-chars for LIKE-comparisons
+ Finally, surround the text by single quotes.
+
+ @param string Text to escape
+ @param boolean Escape text so it can be used in a LIKE-comparison
+ @return string
+}
+function TDBConnection.EscapeString(Text: String; ProcessJokerChars: Boolean=false; DoQuote: Boolean=True): String;
+var
+ c1, c2, c3, c4, EscChar: Char;
+begin
+ case FParameters.NetTypeGroup of
+ ngMySQL: begin
+ c1 := '''';
+ c2 := '\';
+ c3 := '%';
+ c4 := '_';
+ EscChar := '\';
+ if not ProcessJokerChars then begin
+ // Do not escape joker-chars which are used in a LIKE-clause
+ c4 := '''';
+ c3 := '''';
+ end;
+ Result := escChars(Text, EscChar, c1, c2, c3, c4);
+
+ // Remove characters that SynEdit chokes on, so that
+ // the SQL file can be non-corruptedly loaded again.
+ c1 := #13;
+ c2 := #10;
+ c3 := #0;
+ c4 := #0;
+ // TODO: SynEdit also chokes on Char($2028) and possibly Char($2029).
+ Result := escChars(Result, EscChar, c1, c2, c3, c4);
+ end;
+
+ ngMSSQL, ngSQLite: begin
+
+ c1 := '''';
+ c2 := '''';
+ c3 := '''';
+ c4 := '''';
+ EscChar := '''';
+ Result := escChars(Text, EscChar, c1, c2, c3, c4);
+
+ // Escape joker chars % and _ in conjunction with a specified escape char after the WHERE clause.
+ // See http://www.heidisql.com/forum.php?t=12747
+ if ProcessJokerChars then begin
+ c1 := '%';
+ c2 := '_';
+ c4 := '_';
+ c3 := '_';
+ EscChar := '\';
+ Result := escChars(Result, EscChar, c1, c2, c3, c4);
+ end;
+ end;
+
+ ngPgSQL: begin
+ if ProcessJokerChars then begin
+ c1 := '%';
+ c2 := '_';
+ c3 := '%';
+ c4 := '%';
+ EscChar := '\';
+ Result := escChars(Text, EscChar, c1, c2, c3, c4);
+ end else begin
+ Result := Text;
+ end;
+ // Escape single quote with a second single quote
+ Result := escChars(Result, '''', '''', '''', '''', '''');
+ end;
+
+ ngInterbase: begin
+ c1 := '''';
+ c2 := '''';
+ c3 := '''';
+ c4 := '''';
+ EscChar := '\';
+ Result := escChars(Text, EscChar, c1, c2, c3, c4);
+ end;
+
+ end;
+
+ if DoQuote then begin
+ // Add surrounding single quotes
+ Result := FStringQuoteChar + Result + FStringQuoteChar;
+ end;
+end;
+
+
+function TDBConnection.EscapeString(Text: String; Datatype: TDBDatatype): String;
+var
+ DoQuote: Boolean;
+const
+ CategoriesNeedQuote = [dtcText, dtcBinary, dtcTemporal, dtcSpatial, dtcOther];
+begin
+ // Quote text based on the passed datatype
+ DoQuote := Datatype.Category in CategoriesNeedQuote;
+ case Datatype.Category of
+ // Some special cases
+ dtcBinary: begin
+ if IsHex(Text) then
+ DoQuote := False;
+ end;
+ dtcInteger, dtcReal: begin
+ if (not IsNumeric(Text)) and (not IsHex(Text)) then
+ DoQuote := True;
+ if Datatype.Index = dbdtBit then
+ DoQuote := True;
+ end;
+ end;
+ Result := EscapeString(Text, False, DoQuote);
+end;
+
+
+{***
+ Attempt to do string replacement faster than StringReplace
+}
+function TDBConnection.escChars(const Text: String; EscChar, Char1, Char2, Char3, Char4: Char): String;
+const
+ // Attempt to match whatever the CPU cache will hold.
+ block: Cardinal = 65536;
+var
+ bstart, bend, matches, i: Cardinal;
+ // These could be bumped to uint64 if necessary.
+ len, respos: Cardinal;
+ next: Char;
+begin
+ len := Length(Text);
+ Result := '';
+ bend := 0;
+ respos := 0;
+ repeat
+ bstart := bend + 1;
+ bend := bstart + block - 1;
+ if bend > len then bend := len;
+ matches := 0;
+ for i := bstart to bend do if
+ (Text[i] = Char1) or
+ (Text[i] = Char2) or
+ (Text[i] = Char3) or
+ (Text[i] = Char4)
+ then Inc(matches);
+ SetLength(Result, bend + 1 - bstart + matches + respos);
+ for i := bstart to bend do begin
+ next := Text[i];
+ if
+ (next = Char1) or
+ (next = Char2) or
+ (next = Char3) or
+ (next = Char4)
+ then begin
+ Inc(respos);
+ Result[respos] := EscChar;
+ // Special values for MySQL escape.
+ if next = #13 then next := 'r';
+ if next = #10 then next := 'n';
+ if next = #0 then next := '0';
+ end;
+ Inc(respos);
+ Result[respos] := next;
+ end;
+ until bend = len;
+end;
+
+
+function TDBConnection.UnescapeString(Text: String): String;
+begin
+ // Return text with MySQL special sequences turned back to normal characters
+ Result := StringReplace(Text, '\\', '\', [rfReplaceAll]);
+ Result := StringReplace(Result, '\0', #0, [rfReplaceAll]);
+ Result := StringReplace(Result, '\b', #8, [rfReplaceAll]);
+ Result := StringReplace(Result, '\t', #9, [rfReplaceAll]);
+ Result := StringReplace(Result, '\n', #10, [rfReplaceAll]);
+ Result := StringReplace(Result, '\r', #13, [rfReplaceAll]);
+ Result := StringReplace(Result, '\Z', #26, [rfReplaceAll]);
+ Result := StringReplace(Result, '''''', '''', [rfReplaceAll]);
+ Result := StringReplace(Result, '\''', '''', [rfReplaceAll]);
+end;
+
+
+function TDBConnection.EscapeBin(BinValue: String): String;
+var
+ BinLen: Integer;
+ Ansi: AnsiString;
+begin
+ // Return a binary value as hex AnsiString
+ Ansi := AnsiString(BinValue);
+ BinLen := Length(Ansi);
+ if BinLen = 0 then begin
+ Result := EscapeString('');
+ end else begin
+ if IsHex(BinValue) then begin
+ Result := BinValue; // Already hex encoded
+ end else begin
+ SetLength(Result, BinLen*2);
+ BinToHex(PAnsiChar(Ansi), PChar(Result), BinLen);
+ Result := '0x' + Result;
+ end;
+ if AppSettings.ReadBool(asLowercaseHex) then
+ Result := Result.ToLowerInvariant;
+ end;
+end;
+
+
+function TDBConnection.EscapeBin(var ByteData: TBytes): String;
+var
+ BinLen: Integer;
+ Ansi: AnsiString;
+begin
+ BinLen := Length(ByteData);
+ SetString(Ansi, PAnsiChar(ByteData), BinLen);
+ if BinLen = 0 then begin
+ Result := EscapeString('');
+ end else begin
+ if IsHex(String(Ansi)) then begin
+ Result := String(Ansi); // Already hex encoded
+ end else begin
+ SetLength(Result, BinLen*2);
+ BinToHex(PAnsiChar(Ansi), PChar(Result), BinLen);
+ Result := '0x' + Result;
+ end;
+ if AppSettings.ReadBool(asLowercaseHex) then
+ Result := Result.ToLowerInvariant;
+ end;
+end;
+
+
+function TDBConnection.ExtractLiteral(var SQL: String; Prefix: String): String;
+var
+ i, LitStart: Integer;
+ InLiteral: Boolean;
+ rx: TRegExpr;
+begin
+ // Return comment from SQL and remove it from the original string
+ // Single quotes are escaped by a second single quote
+ Result := '';
+ rx := TRegExpr.Create;
+ if Prefix.IsEmpty then
+ rx.Expression := '^\s*'''
+ else
+ rx.Expression := '^\s*'+QuoteRegExprMetaChars(Prefix)+'\s+''';
+ rx.ModifierI := True;
+ if rx.Exec(SQL) then begin
+ LitStart := rx.MatchLen[0]+1;
+ InLiteral := True;
+ for i:=LitStart to Length(SQL) do begin
+ if SQL[i] = '''' then
+ InLiteral := not InLiteral
+ else if not InLiteral then
+ break;
+ end;
+ Result := Copy(SQL, LitStart, i-LitStart-1);
+ Result := UnescapeString(Result);
+ Delete(SQL, 1, i);
+ end;
+ rx.Free;
+end;
+
+
+{**
+ Add backticks to identifier
+ Todo: Support ANSI style
+}
+function TDBConnection.QuoteIdent(Identifier: String; AlwaysQuote: Boolean=True; Glue: Char=#0): String;
+var
+ GluePos, i: Integer;
+begin
+ Result := Identifier;
+ GluePos := 0;
+ if Glue <> #0 then begin
+ GluePos := Pos(Glue, Result);
+ if GluePos > 0 then
+ Result := QuoteIdent(Copy(Result, 1, GluePos-1)) + Glue + QuoteIdent(Copy(Result, GluePos+1, MaxInt));
+ end;
+ if GluePos = 0 then begin
+ if not AlwaysQuote then begin
+ if MySQLKeywords.IndexOf(Result) > -1 then
+ AlwaysQuote := True
+ else if SQLFunctions.Names.IndexOf(Result) > -1 then
+ AlwaysQuote := True
+ else for i:=1 to Length(Result) do begin
+ if not CharInSet(Result[i], FIdentCharsNoQuote) then begin
+ AlwaysQuote := True;
+ break;
+ end;
+ end;
+ end;
+ if AlwaysQuote then begin
+ Result := StringReplace(Result, FQuoteChar, FQuoteChar+FQuoteChar, [rfReplaceAll]);
+ Result := FQuoteChar + Result + FQuoteChar;
+ end;
+ end;
+end;
+
+
+function TDBConnection.DeQuoteIdent(Identifier: String; Glue: Char=#0): String;
+var
+ Quote: Char;
+begin
+ Result := Identifier;
+ if (Length(Identifier)>0) and (Result[1] = FQuoteChar) and (Result[Length(Identifier)] = FQuoteChar) then
+ Result := Copy(Result, 2, Length(Result)-2);
+ if Glue <> #0 then
+ Result := StringReplace(Result, FQuoteChar+Glue+FQuoteChar, Glue, [rfReplaceAll]);
+ Result := StringReplace(Result, FQuoteChar+FQuoteChar, FQuoteChar, [rfReplaceAll]);
+ // Remove all probable quote characters, to fix various problems
+ for Quote in FQuoteChars do begin
+ Result := StringReplace(Result, Quote, '', [rfReplaceAll]);
+ end;
+end;
+
+
+function TDBConnection.CleanIdent(Identifier: string): string;
+begin
+ Result := Trim(Identifier);
+ // See issue #1947:
+ //Result := LowerCase(Result);
+ Result := ReplaceRegExpr('[^A-Za-z0-9]', Result, '_');
+ Result := ReplaceRegExpr('_+', Result, '_');
+end;
+
+
+function TDBConnection.QuotedDbAndTableName(DB, Obj: String): String;
+var
+ o: TDBObject;
+begin
+ // Call TDBObject.QuotedDbAndTableName for db and table string.
+ // Return fully qualified db and tablename, quoted, and including schema if required
+ o := FindObject(DB, Obj);
+ if o <> nil then
+ Result := o.QuotedDbAndTableName()
+ else begin
+ // Fallback for target tables which do not yet exist. For example in copytable dialog.
+ Result := QuoteIdent(DB) + '.';
+ if Parameters.IsAnyMSSQL then
+ Result := Result + '.';
+ Result := Result + QuoteIdent(Obj);
+ end;
+end;
+
+
+function TDBConnection.FindObject(DB, Obj: String): TDBObject;
+var
+ Objects: TDBObjectList;
+ o: TDBObject;
+begin
+ // Find TDBObject by db and table string
+ Objects := GetDBObjects(DB);
+ Result := nil;
+ for o in Objects do begin
+ if o.Name = Obj then begin
+ Result := o;
+ Break;
+ end;
+ end;
+ if not Assigned(Result) then begin
+ Log(lcDebug, Format('Could not find object "%s" in database "%s"', [Obj, DB]));
+ end;
+end;
+
+
+function TDBConnection.GetCol(SQL: String; Column: Integer=0): TStringList;
+var
+ Results: TDBQuery;
+begin
+ Results := GetResults(SQL);
+ Result := TStringList.Create;
+ if Results.RecordCount > 0 then while not Results.Eof do begin
+ Result.Add(Results.Col(Column));
+ Results.Next;
+ end;
+ FreeResults(Results);
+end;
+
+
+{**
+ Get single cell value via SQL query, identified by column number
+}
+function TDBConnection.GetVar(SQL: String; Column: Integer=0): String;
+var
+ Results: TDBQuery;
+begin
+ Results := GetResults(SQL);
+ if Results.RecordCount > 0 then
+ Result := Results.Col(Column)
+ else
+ Result := '';
+ FreeResults(Results);
+end;
+
+
+{**
+ Get single cell value via SQL query, identified by column name
+}
+function TDBConnection.GetVar(SQL: String; Column: String): String;
+var
+ Results: TDBQuery;
+begin
+ Results := GetResults(SQL);
+ if Results.RecordCount > 0 then
+ Result := Results.Col(Column)
+ else
+ Result := '';
+ FreeResults(Results);
+end;
+
+
+function TDBConnection.GetTableEngines: TStringList;
+begin
+ if not Assigned(FTableEngines) then
+ FTableEngines := TStringList.Create;
+ Result := FTableEngines;
+end;
+
+
+function TMySQLConnection.GetTableEngines: TStringList;
+var
+ Results: TDBQuery;
+ engineName, engineSupport: String;
+ rx: TRegExpr;
+begin
+ // After a disconnect Ping triggers the cached engines to be reset
+ Log(lcDebug, 'Fetching list of table engines ...');
+ Ping(True);
+ if not Assigned(FTableEngines) then begin
+ FTableEngines := TStringList.Create;
+ try
+ Results := GetResults('SHOW ENGINES');
+ while not Results.Eof do begin
+ engineName := Results.Col('Engine');
+ engineSupport := LowerCase(Results.Col('Support'));
+ // Add to dropdown if supported
+ if (engineSupport = 'yes') or (engineSupport = 'default') then
+ FTableEngines.Add(engineName);
+ // Check if this is the default engine
+ if engineSupport = 'default' then
+ FTableEngineDefault := engineName;
+ Results.Next;
+ end;
+ Results.Free;
+ except
+ // Ignore errors on old servers and try a fallback:
+ // Manually fetch available engine types by analysing have_* options
+ // This is for servers below 4.1 or when the SHOW ENGINES statement has
+ // failed for some other reason
+ Results := GetSessionVariables(False);
+ // Add default engines which will not show in a have_* variable:
+ FTableEngines.CommaText := 'MyISAM,MRG_MyISAM,HEAP';
+ FTableEngineDefault := 'MyISAM';
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ rx.Expression := '^have_(ARCHIVE|BDB|BLACKHOLE|CSV|EXAMPLE|FEDERATED|INNODB|ISAM)(_engine)?$';
+ while not Results.Eof do begin
+ if rx.Exec(Results.Col(0)) and (LowerCase(Results.Col(1)) = 'yes') then
+ FTableEngines.Add(UpperCase(rx.Match[1]));
+ Results.Next;
+ end;
+ rx.Free;
+ end;
+ end;
+ Result := FTableEngines;
+end;
+
+
+function TDBConnection.GetCollationTable: TDBQuery;
+begin
+ Log(lcDebug, 'Fetching list of collations ...');
+ Ping(True);
+ Result := FCollationTable;
+end;
+
+
+function TMySQLConnection.GetCollationTable: TDBQuery;
+begin
+ inherited;
+ if (not Assigned(FCollationTable)) and Has(frShowCollation) then begin
+ if Has(frShowCollationExtended) then try
+ // Issue #1917: MariaDB 10.10.1+ versions have additional collations in IS.COLLATION_CHARACTER_SET_APPLICABILITY
+ FCollationTable := GetResults('SELECT'+
+ ' FULL_COLLATION_NAME AS '+QuoteIdent('Collation')+
+ ', CHARACTER_SET_NAME AS '+QuoteIdent('Charset')+
+ ', ID AS '+QuoteIdent('Id')+
+ ', IS_DEFAULT AS '+QuoteIdent('Default')+
+ ', 0 AS '+QuoteIdent('Sortlen')+
+ ' FROM '+QuoteIdent(InfSch)+'.COLLATION_CHARACTER_SET_APPLICABILITY'+
+ ' ORDER BY '+QuoteIdent('Collation')
+ );
+ except
+ on E:EDbError do;
+ end;
+ if not Assigned(FCollationTable) then
+ FCollationTable := GetResults('SHOW COLLATION');
+ end;
+ if Assigned(FCollationTable) then
+ FCollationTable.First;
+ Result := FCollationTable;
+end;
+
+
+{function TAdoDBConnection.GetCollationTable: TDBQuery;
+begin
+ inherited;
+ if (not Assigned(FCollationTable)) then
+ FCollationTable := GetResults('SELECT '+EscapeString('')+' AS '+QuoteIdent('Collation')+', '+
+ EscapeString('')+' AS '+QuoteIdent('Charset')+', 0 AS '+QuoteIdent('Id')+', '+
+ EscapeString('')+' AS '+QuoteIdent('Default')+', '+EscapeString('')+' AS '+QuoteIdent('Compiled')+', '+
+ '1 AS '+QuoteIdent('Sortlen'));
+ if Assigned(FCollationTable) then
+ FCollationTable.First;
+ Result := FCollationTable;
+end;}
+
+
+{function TInterbaseConnection.GetCollationTable: TDBQuery;
+begin
+ inherited;
+ if not Assigned(FCollationTable) then begin
+ FCollationTable := GetResults('SELECT RDB$COLLATION_NAME AS '+QuoteIdent('Collation')+', RDB$COLLATION_ID AS '+QuoteIdent('Id')+', RDB$CHARACTER_SET_ID FROM RDB$COLLATIONS');
+ end;
+ if Assigned(FCollationTable) then
+ FCollationTable.First;
+ Result := FCollationTable;
+end;}
+
+
+function TDBConnection.GetCollationList: TStringList;
+var
+ c: TDBQuery;
+begin
+ c := CollationTable;
+ Result := TStringList.Create;
+ if Assigned(c) then while not c.Eof do begin
+ Result.Add(c.Col('Collation'));
+ c.Next;
+ end;
+end;
+
+
+function TSQLiteConnection.GetCollationList: TStringList;
+begin
+ // See https://www.sqlite.org/datatype3.html#collation_sequence_examples
+ Result := TStringList.Create;
+ Result.CommaText := 'nocase,binary,rtrim';
+end;
+
+
+function TDBConnection.GetCharsetTable: TDBQuery;
+begin
+ Log(lcDebug, 'Fetching charset list ...');
+ Ping(True);
+ Result := nil;
+end;
+
+
+function TMySQLConnection.GetCharsetTable: TDBQuery;
+begin
+ inherited;
+ if (not Assigned(FCharsetTable)) and Has(frShowCharset) then
+ FCharsetTable := GetResults('SHOW CHARSET');
+ Result := FCharsetTable;
+end;
+
+
+{function TAdoDBConnection.GetCharsetTable: TDBQuery;
+begin
+ inherited;
+ if not Assigned(FCharsetTable) then
+ FCharsetTable := GetResults('SELECT '+QuoteIdent('name')+' AS '+QuoteIdent('Charset')+', '+QuoteIdent('description')+' AS '+QuoteIdent('Description')+
+ ' FROM '+QuotedDbAndTableName('master', 'syscharsets')
+ );
+ Result := FCharsetTable;
+end;}
+
+
+function TPgConnection.GetCharsetTable: TDBQuery;
+begin
+ inherited;
+ if not Assigned(FCharsetTable) then
+ FCharsetTable := GetResults('SELECT PG_ENCODING_TO_CHAR('+QuoteIdent('encid')+') AS '+QuoteIdent('Charset')+', '+EscapeString('')+' AS '+QuoteIdent('Description')+' FROM ('+
+ 'SELECT '+QuoteIdent('conforencoding')+' AS '+QuoteIdent('encid')+' FROM '+QuoteIdent('pg_conversion')+', '+QuoteIdent('pg_database')+' '+
+ 'WHERE '+QuoteIdent('contoencoding')+'='+QuoteIdent('encoding')+' AND '+QuoteIdent('datname')+'=CURRENT_DATABASE()) AS '+QuoteIdent('e')
+ );
+ Result := FCharsetTable;
+end;
+
+
+function TSQLiteConnection.GetCharsetTable;
+begin
+ inherited;
+ if not Assigned(FCharsetTable) then begin
+ //FCharsetTable := // Todo!
+ end;
+ Result := FCharsetTable;
+end;
+
+
+{function TInterbaseConnection.GetCharsetTable: TDBQuery;
+begin
+ inherited;
+ if not Assigned(FCharsetTable) then
+ FCharsetTable := GetResults('SELECT RDB$CHARACTER_SET_NAME AS '+QuoteIdent('Charset')+', RDB$CHARACTER_SET_NAME AS '+QuoteIdent('Description')+' FROM RDB$CHARACTER_SETS');
+ Result := FCharsetTable;
+end;}
+
+
+function TDBConnection.GetCharsetList: TStringList;
+var
+ c: TDBQuery;
+begin
+ c := CharsetTable;
+ Result := TStringList.Create;
+ if Assigned(c) then begin
+ c.First;
+ while not c.Eof do begin
+ Result.Add(c.Col('Charset') + ': ' + c.Col('Description'));
+ c.Next;
+ end;
+ Result.Sort;
+ end;
+end;
+
+
+function TDBConnection.GetSessionVariables(Refresh: Boolean): TDBQuery;
+begin
+ // Return server variables
+ if (not Assigned(FSessionVariables)) or Refresh then begin
+ if Assigned(FSessionVariables) then
+ FreeAndNil(FSessionVariables);
+ FSessionVariables := GetResults(GetSQLSpecifity(spSessionVariables));
+ end;
+ FSessionVariables.First;
+ Result := FSessionVariables;
+end;
+
+
+function TDBConnection.GetSessionVariable(VarName: String; DefaultValue: String=''; Refresh: Boolean=False): String;
+var
+ Vars: TDBQuery;
+ VarExists: Boolean;
+begin
+ // Return the value of a specific server variable
+ Vars := GetSessionVariables(Refresh);
+ Result := DefaultValue;
+ VarExists := False;
+ while not Vars.Eof do begin
+ if Vars.Col(0) = VarName then begin
+ Result := Vars.Col(1);
+ VarExists := True;
+ Break;
+ end;
+ Vars.Next;
+ end;
+ if not VarExists then begin
+ Log(lcDebug, 'Variable "'+VarName+'" does not exist');
+ end;
+end;
+
+
+function TDBConnection.MaxAllowedPacket: Int64;
+begin
+ // Default
+ Result := SIZE_MB;
+end;
+
+
+function TMySQLConnection.MaxAllowedPacket: Int64;
+begin
+ Result := MakeInt(GetSessionVariable('max_allowed_packet'));
+ if Result < SIZE_KB*10 then begin
+ Result := SIZE_MB;
+ Log(lcError, f_('The server did not return a non-zero value for the %s variable. Assuming %s now.', ['max_allowed_packet', FormatByteNumber(Result)]));
+ end;
+
+end;
+
+
+function TDBConnection.GetLockedTableCount(db: String): Integer;
+var
+ sql: String;
+ LockedTables: TStringList;
+begin
+ // Find tables which are currently locked.
+ // Used to prevent waiting time in GetDBObjects.
+ sql := GetSQLSpecifity(spLockedTables);
+ Result := 0;
+ if not sql.IsEmpty then try
+ LockedTables := GetCol(Format(sql, [QuoteIdent(db,False)]));
+ Result := LockedTables.Count;
+ LockedTables.Free;
+ except // Suppress errors, due to not working on all servers: https://www.heidisql.com/forum.php?t=34984
+ on E:EDbError do;
+ end;
+end;
+
+
+function TDBConnection.IdentifierEquals(Ident1, Ident2: String): Boolean;
+begin
+ // Compare only name of identifier, in the case fashion the server tells us
+ case FCaseSensitivity of
+ 0: Result := Ident1 = Ident2;
+ else Result := CompareText(Ident1, Ident2) = 0;
+ end;
+end;
+
+
+function TDBConnection.IsTextDefault(Value: String; Tp: TDBDatatype): Boolean;
+begin
+ // Helper for GetTableColumns
+ if FParameters.IsMariaDB then begin
+ // Only MariaDB 10.2.27+ wraps default text in single quotes
+ // see https://mariadb.com/kb/en/information-schema-columns-table/
+ Result := (ServerVersionInt >= 100207) and Value.StartsWith('''');
+ // Prior to 10.2.1, only CURRENT_TIMESTAMP allowed
+ // see https://mariadb.com/kb/en/create-table/#default-column-option
+ Result := Result or ((ServerVersionInt < 100201) and (not Value.StartsWith('CURRENT_TIMESTAMP', True)));
+ // Inexact fallback detection, wrong if MariaDB allows "0+1" as expression at some point
+ Result := Result or Value.IsEmpty or IsInt(Value[1]);
+ end else if FParameters.IsAnyMySQL then begin
+ // Only MySQL case with expression in default value is as follows:
+ if (Tp.Category = dtcTemporal) and Value.StartsWith('CURRENT_TIMESTAMP', True) then begin
+ Result := False;
+ end
+ else if Tp.Index = dbdtBit then
+ Result := False
+ else case ServerVersionInt of
+ 0..80013: Result := True;
+ else begin
+ // https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html#data-type-defaults-explicit
+ // MySQL 8.0.13+ expect expressions to be wrapped in (..) when you create a table.
+ // But checking if first char is an opening parenthesis does not work here, as we get the expression
+ // from IS.COLUMNS, not from SHOW CREATE TABLE. So here's a workaround for distinguishing text
+ // from an expression:
+ Result := not Value.Contains('(');
+ end;
+ end;
+ end else if FParameters.IsAnyPostgreSQL then begin
+ // text only if starting with '
+ Result := Value.StartsWith('''');
+ end else begin
+ // MS SQL, PG and SQLite:
+ Result := True;
+ end;
+end;
+
+
+function TDBConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ TableIdx: Integer;
+ ColQuery: TDBQuery;
+ Col: TTableColumn;
+ dt, DefText, ExtraText, MaxLen: String;
+begin
+ // Generic: query table columns from IS.COLUMNS
+ Log(lcDebug, 'Getting fresh columns for '+Table.QuotedDbAndTableName);
+ Result := TTableColumnList.Create(True);
+ TableIdx := InformationSchemaObjects.IndexOf('columns');
+ if TableIdx = -1 then begin
+ // No is.columns table available
+ Exit;
+ end;
+ ColQuery := GetResults('SELECT * FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent(InformationSchemaObjects[TableIdx])+
+ ' WHERE '+Table.SchemaClauseIS('TABLE')+' AND TABLE_NAME='+EscapeString(Table.Name)+
+ ' ORDER BY ORDINAL_POSITION');
+ while not ColQuery.Eof do begin
+ Col := TTableColumn.Create(Self);
+ Result.Add(Col);
+ Col.Name := ColQuery.Col('COLUMN_NAME');
+ Col.OldName := Col.Name;
+ // MySQL and most commonly used field:
+ if ColQuery.ColExists('COLUMN_TYPE') then
+ dt := 'COLUMN_TYPE'
+ // PostgreSQL:
+ else if ColQuery.ColExists('DATA_TYPE') then begin
+ // user defined types, like CITEXT:
+ if (ColQuery.Col('DATA_TYPE').ToLower = 'user-defined') and ColQuery.ColExists('UDT_NAME') then
+ dt := 'UDT_NAME'
+ else
+ dt := 'DATA_TYPE';
+ end;
+ Col.ParseDatatype(ColQuery.Col(dt));
+ // PG/MSSQL don't include length in data type
+ if Col.LengthSet.IsEmpty and Col.DataType.HasLength then begin
+ MaxLen := '';
+ case Col.DataType.Category of
+ dtcText, dtcBinary: begin
+ if not ColQuery.IsNull('CHARACTER_MAXIMUM_LENGTH') then begin
+ MaxLen := ColQuery.Col('CHARACTER_MAXIMUM_LENGTH');
+ if MaxLen = '-1' then
+ MaxLen := 'max';
+ end;
+ end;
+ dtcInteger: begin
+ if (not ColQuery.IsNull('NUMERIC_PRECISION')) and Has(frIntegerDisplayWidth) then begin
+ // Integer display width is deprecated as of MySQL 8.0.17
+ MaxLen := ColQuery.Col('NUMERIC_PRECISION');
+ end;
+ end;
+ dtcReal: begin
+ // See #953
+ if (not ColQuery.IsNull('NUMERIC_PRECISION')) and (not ColQuery.IsNull('NUMERIC_SCALE')) then begin
+ MaxLen := ColQuery.Col('NUMERIC_PRECISION')
+ + ',' + StrToIntDef(ColQuery.Col('NUMERIC_SCALE'), 0).ToString;
+ end;
+ end;
+ dtcTemporal: begin
+ if not ColQuery.IsNull('DATETIME_PRECISION') then begin
+ MaxLen := ColQuery.Col('DATETIME_PRECISION');
+ // Remove meaningless length of "0"
+ if StrToIntDef(MaxLen, -1) < 1 then
+ MaxLen := '';
+ end;
+ end;
+ end;
+ if (not MaxLen.IsEmpty) and ((MaxLen <> Col.DataType.DefaultSize.ToString) or Col.DataType.RequiresLength) then
+ Col.LengthSet := MaxLen;
+ end;
+ Col.Charset := ColQuery.Col('CHARACTER_SET_NAME');
+ Col.Collation := ColQuery.Col('COLLATION_NAME');
+ // MSSQL has no expression
+ Col.GenerationExpression := ColQuery.Col('GENERATION_EXPRESSION', True);
+ // PG has no extra:
+ ExtraText := ColQuery.Col('EXTRA', True);
+
+ Col.Virtuality := RegExprGetMatch('\b(\w+)\s+generated\b', ExtraText.ToLowerInvariant, 1);
+ Col.Invisible := ExecRegExprI('\binvisible\b', ExtraText);
+ Col.AllowNull := ColQuery.Col('IS_NULLABLE').ToLowerInvariant = 'yes';
+ Col.SRID := StrToUIntDef(ColQuery.Col('SRS_ID', True), 0);
+
+ DefText := ColQuery.Col('COLUMN_DEFAULT');
+ Col.OnUpdateType := cdtNothing;
+ if DefText.StartsWith('nextval(', True) then begin
+ // PG auto increment
+ Col.DefaultType := cdtAutoInc;
+ Col.DefaultText := DefText;
+ end
+ else if ExecRegExpr('\bauto_increment\b', ExtraText.ToLowerInvariant) then begin
+ // MySQL auto increment
+ Col.DefaultType := cdtAutoInc;
+ Col.DefaultText := Col.AutoIncName;
+ end
+ else if DefText.ToLowerInvariant = 'null' then begin
+ Col.DefaultType := cdtNull;
+ end
+ else if ColQuery.IsNull('COLUMN_DEFAULT') then begin
+ if Col.AllowNull then
+ Col.DefaultType := cdtNull
+ else
+ Col.DefaultType := cdtNothing;
+ end
+ else if IsTextDefault(DefText, Col.DataType) then begin
+ Col.DefaultType := cdtText;
+ Col.DefaultText := IfThen(DefText.StartsWith(''''), ExtractLiteral(DefText, ''), DefText);
+ end
+ else begin
+ Col.DefaultType := cdtExpression;
+ Col.DefaultText := DefText;
+ end;
+ Col.OnUpdateText := RegExprGetMatch('\bon update (.*)$', ExtraText, 1, False, True);
+ if not Col.OnUpdateText.IsEmpty then begin
+ Col.OnUpdateType := cdtExpression;
+ end;
+
+ // PG has no column_comment:
+ Col.Comment := ColQuery.Col('COLUMN_COMMENT', True);
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+end;
+
+
+function TMySQLConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ TableIdx: Integer;
+ ColQuery: TDBQuery;
+ Col: TTableColumn;
+ DefText, ExtraText: String;
+begin
+ TableIdx := InformationSchemaObjects.IndexOf('columns');
+ if TableIdx > -1 then begin
+ Result := inherited;
+ Exit;
+ end;
+
+ // !!Fallback!! for old MySQL pre-5.0 servers and ProxySQL
+ Result := TTableColumnList.Create(True);
+
+ if Parameters.IsProxySQLAdmin then begin
+
+ // ProxySQL has no IS.COLUMNS
+ Result := TTableColumnList.Create(True);
+ ColQuery := GetResults('SELECT * FROM pragma_table_info('+EscapeString(Table.Name)+')');
+ while not ColQuery.Eof do begin
+ Col := TTableColumn.Create(Self);
+ Result.Add(Col);
+ Col.Name := ColQuery.Col('name');
+ Col.OldName := Col.Name;
+ Col.ParseDatatype(ColQuery.Col('type'));
+ Col.AllowNull := ColQuery.Col('notnull') <> '1';
+ Col.DefaultType := cdtNothing;
+ Col.DefaultText := '';
+ Col.OnUpdateType := cdtNothing;
+ Col.OnUpdateText := '';
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+
+ end else begin
+
+ // MySQL pre-5.0 has no IS.COLUMNS table
+ ColQuery := GetResults('SHOW FULL COLUMNS FROM '+QuoteIdent(Table.Database)+'.'+QuoteIdent(Table.Name));
+ while not ColQuery.Eof do begin
+ Col := TTableColumn.Create(Self);
+ Result.Add(Col);
+ Col.Name := ColQuery.Col(0);
+ Col.OldName := Col.Name;
+ Col.ParseDatatype(ColQuery.Col('Type'));
+ Col.Collation := ColQuery.Col('Collation', True);
+ if Col.Collation.ToLowerInvariant = 'null' then
+ Col.Collation := '';
+ Col.AllowNull := ColQuery.Col('Null').ToLowerInvariant = 'yes';
+
+ DefText := ColQuery.Col('Default');
+ ExtraText := ColQuery.Col('Extra');
+ Col.OnUpdateType := cdtNothing;
+ if ExecRegExpr('^auto_increment$', ExtraText.ToLowerInvariant) then begin
+ Col.DefaultType := cdtAutoInc;
+ Col.DefaultText := Col.AutoIncName;
+ end else if ColQuery.IsNull('Default') then begin
+ Col.DefaultType := cdtNothing;
+ end else if IsTextDefault(DefText, Col.DataType) then begin
+ Col.DefaultType := cdtText;
+ Col.DefaultText := IfThen(DefText.StartsWith(''''), ExtractLiteral(DefText, ''), DefText);
+ end else begin
+ Col.DefaultType := cdtExpression;
+ Col.DefaultText := DefText;
+ end;
+ Col.OnUpdateText := RegExprGetMatch('^on update (.*)$', ExtraText, 1);
+ if not Col.OnUpdateText.IsEmpty then begin
+ Col.OnUpdateType := cdtExpression;
+ end;
+
+ Col.Comment := ColQuery.Col('Comment', True);
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+ end;
+end;
+
+
+{function TAdoDBConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ Comments: TDBQuery;
+ TableCol: TTableColumn;
+begin
+ // Parent method is sufficient for most things
+ Result := inherited;
+
+ // Remove surrounding parentheses from default value. See #721
+ for TableCol in Result do begin
+ if not TableCol.DefaultText.IsEmpty then
+ TableCol.DefaultText := RegExprGetMatch('^\((.*)\)$', TableCol.DefaultText, 1);
+ end;
+
+ // Column comments in MSSQL. See http://www.heidisql.com/forum.php?t=19576
+ try
+ Comments := GetResults('SELECT c.name AS '+QuoteIdent('column')+', prop.value AS '+QuoteIdent('comment')+' '+
+ 'FROM sys.extended_properties AS prop '+
+ 'INNER JOIN sys.all_objects o ON prop.major_id = o.object_id '+
+ 'INNER JOIN sys.schemas s ON o.schema_id = s.schema_id '+
+ 'INNER JOIN sys.columns AS c ON prop.major_id = c.object_id AND prop.minor_id = c.column_id '+
+ 'WHERE '+
+ ' prop.name='+EscapeString('MS_Description')+
+ ' AND s.name='+EscapeString(Table.Schema)+
+ ' AND o.name='+EscapeString(Table.Name)
+ );
+ while not Comments.Eof do begin
+ for TableCol in Result do begin
+ if TableCol.Name = Comments.Col('column') then begin
+ TableCol.Comment := Comments.Col('comment');
+ Break;
+ end;
+ end;
+ Comments.Next;
+ end;
+ except // Fails on old servers
+ on E:EDbError do;
+ end;
+
+end;}
+
+function TPgConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ Comments: TDBQuery;
+ TableCol: TTableColumn;
+begin
+ Result := inherited;
+ // Column comments in Postgre. See issue #859
+ // Todo: add current schema to WHERE clause?
+ Comments := GetResults('SELECT a.attname AS column, des.description AS comment'+
+ ' FROM pg_attribute AS a, pg_description AS des, pg_class AS pgc'+
+ ' WHERE'+
+ ' pgc.oid = a.attrelid'+
+ ' AND des.objoid = pgc.oid'+
+ ' AND pg_table_is_visible(pgc.oid)'+
+ ' AND pgc.relname = '+EscapeString(Table.Name)+
+ ' AND a.attnum = des.objsubid'
+ );
+ while not Comments.Eof do begin
+ for TableCol in Result do begin
+ if TableCol.Name = Comments.Col('column') then begin
+ TableCol.Comment := Comments.Col('comment');
+ Break;
+ end;
+ end;
+ Comments.Next;
+ end;
+end;
+
+function TSQLiteConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ ColQuery: TDBQuery;
+ Col: TTableColumn;
+begin
+ // SQLite has no IS.COLUMNS
+ // Todo: include database name
+ // Todo: default values
+ Result := TTableColumnList.Create(True);
+ ColQuery := GetResults('SELECT * FROM '+QuoteIdent(Table.Database)+'.pragma_table_info('+EscapeString(Table.Name)+')');
+ while not ColQuery.Eof do begin
+ Col := TTableColumn.Create(Self);
+ Result.Add(Col);
+ Col.Name := ColQuery.Col('name');
+ Col.OldName := Col.Name;
+ Col.ParseDatatype(ColQuery.Col('type'));
+ Col.AllowNull := ColQuery.Col('notnull') <> '1';
+ Col.DefaultType := cdtNothing;
+ Col.DefaultText := '';
+ Col.OnUpdateType := cdtNothing;
+ Col.OnUpdateText := '';
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+end;
+
+
+{function TInterbaseConnection.GetTableColumns(Table: TDBObject): TTableColumnList;
+var
+ ColQuery: TDBQuery;
+ Col: TTableColumn;
+begin
+ // Todo
+ Result := TTableColumnList.Create(True);
+ ColQuery := GetResults('SELECT r.RDB$FIELD_NAME AS field_name,'+
+ ' r.RDB$DESCRIPTION AS field_description,'+
+ ' r.RDB$DEFAULT_VALUE AS field_default_value,'+
+ ' r.RDB$NULL_FLAG AS null_flag,'+
+ ' f.RDB$FIELD_LENGTH AS field_length,'+
+ ' f.RDB$FIELD_PRECISION AS field_precision,'+
+ ' f.RDB$FIELD_SCALE AS field_scale,'+
+ ' f.RDB$FIELD_TYPE AS field_type,'+
+ ' f.RDB$FIELD_SUB_TYPE AS field_subtype,'+
+ ' coll.RDB$COLLATION_NAME AS field_collation,'+
+ ' cset.RDB$CHARACTER_SET_NAME AS field_charset'+
+ ' FROM RDB$RELATION_FIELDS r'+
+ ' LEFT JOIN RDB$FIELDS f ON r.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME'+
+ ' LEFT JOIN RDB$CHARACTER_SETS cset ON f.RDB$CHARACTER_SET_ID = cset.RDB$CHARACTER_SET_ID'+
+ ' LEFT JOIN RDB$COLLATIONS coll ON f.RDB$COLLATION_ID = coll.RDB$COLLATION_ID'+
+ ' AND F.RDB$CHARACTER_SET_ID = COLL.RDB$CHARACTER_SET_ID'+
+ ' WHERE r.RDB$RELATION_NAME='+EscapeString(Table.Name)+
+ ' ORDER BY r.RDB$FIELD_POSITION');
+ while not ColQuery.Eof do begin
+ Col := TTableColumn.Create(Self);
+ Result.Add(Col);
+ Col.Name := ColQuery.Col('FIELD_NAME');
+ Col.OldName := Col.Name;
+ //Col.ParseDatatype(ColQuery.Col('type'));
+ Col.DataType := GetDatatypeByNativeType(MakeInt(ColQuery.Col('FIELD_TYPE')));
+ Col.AllowNull := ColQuery.IsNull('NULL_FLAG');
+ Col.DefaultType := cdtNothing;
+ Col.DefaultText := '';
+ Col.OnUpdateType := cdtNothing;
+ Col.OnUpdateText := '';
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+end;}
+
+
+function TDBConnection.GetTableKeys(Table: TDBObject): TTableKeyList;
+var
+ ColTableIdx, ConTableIdx: Integer;
+ KeyQuery: TDBQuery;
+ NewKey: TTableKey;
+begin
+ // Generic: query table keys from IS.KEY_COLUMN_USAGE
+ Result := TTableKeyList.Create(True);
+ ColTableIdx := InformationSchemaObjects.IndexOf('KEY_COLUMN_USAGE');
+ ConTableIdx := InformationSchemaObjects.IndexOf('TABLE_CONSTRAINTS');
+ KeyQuery := GetResults('SELECT * FROM '+
+ QuoteIdent(InfSch)+'.'+QuoteIdent(InformationSchemaObjects[ColTableIdx])+' AS col'+
+ ', '+QuoteIdent(InfSch)+'.'+QuoteIdent(InformationSchemaObjects[ConTableIdx])+' AS con'+
+ ' WHERE col.TABLE_SCHEMA='+EscapeString(IfThen(Parameters.IsAnyMSSQL, Table.Schema, Table.Database))+
+ ' AND col.TABLE_NAME='+EscapeString(Table.Name)+
+ ' AND col.TABLE_SCHEMA=con.TABLE_SCHEMA'+
+ ' AND col.TABLE_NAME=con.TABLE_NAME'+
+ ' AND col.CONSTRAINT_NAME=con.CONSTRAINT_NAME'
+ );
+ NewKey := nil;
+ while not KeyQuery.Eof do begin
+ if (not KeyQuery.ColExists('REFERENCED_TABLE_NAME'))
+ or KeyQuery.Col('REFERENCED_TABLE_NAME').IsEmpty then begin
+ if (not Assigned(NewKey)) or (NewKey.Name <> KeyQuery.Col('CONSTRAINT_NAME')) then begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := KeyQuery.Col('CONSTRAINT_NAME');
+ NewKey.OldName := NewKey.Name;
+ if KeyQuery.Col('CONSTRAINT_TYPE').StartsWith(TTableKey.PRIMARY, True) then
+ NewKey.IndexType := TTableKey.PRIMARY
+ else
+ NewKey.IndexType := KeyQuery.Col('CONSTRAINT_TYPE');
+ NewKey.OldIndexType := NewKey.IndexType;
+ end;
+ NewKey.Columns.Add(KeyQuery.Col('COLUMN_NAME'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ end;
+ KeyQuery.Next;
+ end;
+ KeyQuery.Free;
+end;
+
+
+function TMySQLConnection.GetTableKeys(Table: TDBObject): TTableKeyList;
+var
+ KeyQuery, ColQuery: TDBQuery;
+ NewKey: TTableKey;
+begin
+ Result := TTableKeyList.Create(True);
+
+ if Parameters.IsProxySQLAdmin then begin
+
+ ColQuery := GetResults('SELECT * '+
+ 'FROM pragma_table_info('+EscapeString(Table.Name)+') '+
+ 'WHERE pk!=0 ORDER BY pk');
+ NewKey := nil;
+ while not ColQuery.Eof do begin
+ if not Assigned(NewKey) then begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := TTableKey.PRIMARY;
+ NewKey.OldName := NewKey.Name;
+ NewKey.IndexType := TTableKey.PRIMARY;
+ NewKey.OldIndexType := NewKey.IndexType;
+ end;
+ NewKey.Columns.Add(ColQuery.Col('name'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+
+ KeyQuery := GetResults('SELECT * '+
+ 'FROM pragma_index_list('+EscapeString(Table.Name)+') '+
+ 'WHERE origin!='+EscapeString('pk'));
+ while not KeyQuery.Eof do begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := KeyQuery.Col('name');
+ NewKey.OldName := NewKey.Name;
+ NewKey.IndexType := IfThen(KeyQuery.Col('unique')='0', TTableKey.KEY, TTableKey.UNIQUE);
+ NewKey.OldIndexType := NewKey.IndexType;
+ ColQuery := GetResults('SELECT * '+
+ 'FROM pragma_index_info('+EscapeString(NewKey.Name)+')');
+ while not ColQuery.Eof do begin
+ NewKey.Columns.Add(ColQuery.Col('name'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+ KeyQuery.Next;
+ end;
+ KeyQuery.Free;
+
+ end else begin
+
+ KeyQuery := GetResults('SHOW INDEXES FROM '+QuoteIdent(Table.Name)+' FROM '+QuoteIdent(Table.Database));
+ NewKey := nil;
+ while not KeyQuery.Eof do begin
+ if (not Assigned(NewKey)) or (NewKey.Name <> KeyQuery.Col('Key_name')) then begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := KeyQuery.Col('Key_name');
+ NewKey.OldName := NewKey.Name;
+ if CompareText(NewKey.Name, TTableKey.PRIMARY) = 0 then
+ NewKey.IndexType := TTableKey.PRIMARY
+ else if KeyQuery.Col('Non_unique') = '0' then
+ NewKey.IndexType := TTableKey.UNIQUE
+ else if CompareText(KeyQuery.Col('Index_type'), TTableKey.FULLTEXT) = 0 then
+ NewKey.IndexType := TTableKey.FULLTEXT
+ else if CompareText(KeyQuery.Col('Index_type'), TTableKey.SPATIAL) = 0 then
+ NewKey.IndexType := TTableKey.SPATIAL
+ else
+ NewKey.IndexType := TTableKey.KEY;
+ NewKey.OldIndexType := NewKey.IndexType;
+ if ExecRegExpr('(BTREE|HASH)', KeyQuery.Col('Index_type')) then
+ NewKey.Algorithm := KeyQuery.Col('Index_type');
+ NewKey.Comment := KeyQuery.Col('Index_comment', True);
+ end;
+ if KeyQuery.ColumnExists('Expression') and (not KeyQuery.IsNull('Expression')) then begin
+ // Functional key part: enclose expression within parentheses to distinguish them from columns (issue #1777)
+ NewKey.Columns.Add('('+KeyQuery.Col('Expression')+')');
+ end
+ else begin
+ // Normal column
+ NewKey.Columns.Add(KeyQuery.Col('Column_name'));
+ end;
+ NewKey.Collations.Add(KeyQuery.Col('Collation', True));
+ if NewKey.IsSpatial then
+ NewKey.SubParts.Add('') // Keep in sync, prevent "Incorrect prefix key"
+ else
+ NewKey.SubParts.Add(KeyQuery.Col('Sub_part'));
+ KeyQuery.Next;
+ end;
+ KeyQuery.Free;
+
+ end;
+end;
+
+
+function TPGConnection.GetTableKeys(Table: TDBObject): TTableKeyList;
+var
+ KeyQuery: TDBQuery;
+ NewKey: TTableKey;
+begin
+ Result := TTableKeyList.Create(True);
+ // For PostgreSQL there seem to be privilege problems in IS.
+ // See http://www.heidisql.com/forum.php?t=16213
+ if ServerVersionInt >= 90000 then begin
+ KeyQuery := GetResults('WITH ndx_list AS ('+
+ ' SELECT pg_index.indexrelid, pg_class.oid'+
+ ' FROM pg_index, pg_class'+
+ ' WHERE pg_class.relname = '+EscapeString(Table.Name)+
+ ' AND pg_class.oid = pg_index.indrelid'+
+ ' ),'+
+ ' ndx_cols AS ('+
+ ' SELECT pg_class.relname, UNNEST(i.indkey) AS col_ndx,'+
+ ' CASE i.indisprimary WHEN true THEN '+EscapeString(TTableKey.PRIMARY)+' ELSE CASE i.indisunique WHEN true THEN '+EscapeString(TTableKey.UNIQUE)+' ELSE '+EscapeString(TTableKey.KEY)+' END END AS CONSTRAINT_TYPE,'+
+ ' pg_class.oid'+
+ ' FROM pg_class'+
+ ' JOIN pg_index i ON (pg_class.oid = i.indexrelid)'+
+ ' JOIN ndx_list ON (pg_class.oid = ndx_list.indexrelid)'+
+ ' WHERE pg_table_is_visible(pg_class.oid)'+
+ ' )'+
+ 'SELECT ndx_cols.relname AS CONSTRAINT_NAME, ndx_cols.CONSTRAINT_TYPE, a.attname AS COLUMN_NAME '+
+ 'FROM pg_attribute a '+
+ 'JOIN ndx_cols ON (a.attnum = ndx_cols.col_ndx) '+
+ 'JOIN ndx_list ON (ndx_list.oid = a.attrelid AND ndx_list.indexrelid = ndx_cols.oid)'
+ );
+ end else begin
+ KeyQuery := GetResults('SELECT '+QuoteIdent('c')+'.'+QuoteIdent('conname')+' AS '+QuoteIdent('CONSTRAINT_NAME')+', '+
+ 'CASE '+QuoteIdent('c')+'.'+QuoteIdent('contype')+' '+
+ 'WHEN '+EscapeString('c')+' THEN '+EscapeString('CHECK')+' '+
+ 'WHEN '+EscapeString('f')+' THEN '+EscapeString('FOREIGN KEY')+' '+
+ 'WHEN '+EscapeString('p')+' THEN '+EscapeString('PRIMARY KEY')+' '+
+ 'WHEN '+EscapeString('u')+' THEN '+EscapeString('UNIQUE')+' '+
+ 'END AS '+QuoteIdent('CONSTRAINT_TYPE')+', '+
+ QuoteIdent('a')+'.'+QuoteIdent('attname')+' AS '+QuoteIdent('COLUMN_NAME')+' '+
+ 'FROM '+QuoteIdent('pg_constraint')+' AS '+QuoteIdent('c')+' '+
+ 'LEFT JOIN '+QuoteIdent('pg_class')+' '+QuoteIdent('t')+' ON '+QuoteIdent('c')+'.'+QuoteIdent('conrelid')+'='+QuoteIdent('t')+'.'+QuoteIdent('oid')+' '+
+ 'LEFT JOIN '+QuoteIdent('pg_attribute')+' '+QuoteIdent('a')+' ON '+QuoteIdent('t')+'.'+QuoteIdent('oid')+'='+QuoteIdent('a')+'.'+QuoteIdent('attrelid')+' '+
+ 'LEFT JOIN '+QuoteIdent('pg_namespace')+' '+QuoteIdent('n')+' ON '+QuoteIdent('t')+'.'+QuoteIdent('relnamespace')+'='+QuoteIdent('n')+'.'+QuoteIdent('oid')+' '+
+ 'WHERE c.contype IN ('+EscapeString('p')+', '+EscapeString('u')+') '+
+ 'AND '+QuoteIdent('a')+'.'+QuoteIdent('attnum')+'=ANY('+QuoteIdent('c')+'.'+QuoteIdent('conkey')+') '+
+ 'AND '+QuoteIdent('n')+'.'+QuoteIdent('nspname')+'='+EscapeString(Table.Schema)+' '+
+ 'AND '+QuoteIdent('t')+'.'+QuoteIdent('relname')+'='+EscapeString(Table.Name)+' '+
+ 'ORDER BY '+QuoteIdent('a')+'.'+QuoteIdent('attnum')
+ );
+ end;
+ NewKey := nil;
+ while not KeyQuery.Eof do begin
+ if (not Assigned(NewKey)) or (NewKey.Name <> KeyQuery.Col('CONSTRAINT_NAME')) then begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := KeyQuery.Col('CONSTRAINT_NAME');
+ NewKey.OldName := NewKey.Name;
+ NewKey.IndexType := KeyQuery.Col('CONSTRAINT_TYPE');
+ if NewKey.IndexType.ToLowerInvariant.EndsWith(' key') then
+ Delete(NewKey.IndexType, Length(NewKey.IndexType)-4, 4);
+ NewKey.OldIndexType := NewKey.IndexType;
+ end;
+ NewKey.Columns.Add(KeyQuery.Col('COLUMN_NAME'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ KeyQuery.Next;
+ end;
+ KeyQuery.Free;
+end;
+
+
+function TSQLiteConnection.GetTableKeys(Table: TDBObject): TTableKeyList;
+var
+ ColQuery, KeyQuery: TDBQuery;
+ NewKey: TTableKey;
+begin
+ Result := TTableKeyList.Create(True);
+ ColQuery := GetResults('SELECT * '+
+ 'FROM '+QuoteIdent(Table.Database)+'.pragma_table_info('+EscapeString(Table.Name)+') '+
+ 'WHERE pk!=0 ORDER BY pk');
+ NewKey := nil;
+ while not ColQuery.Eof do begin
+ if not Assigned(NewKey) then begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := TTableKey.PRIMARY;
+ NewKey.OldName := NewKey.Name;
+ NewKey.IndexType := TTableKey.PRIMARY;
+ NewKey.OldIndexType := NewKey.IndexType;
+ end;
+ NewKey.Columns.Add(ColQuery.Col('name'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+
+ KeyQuery := GetResults('SELECT * '+
+ 'FROM '+QuoteIdent(Table.Database)+'.pragma_index_list('+EscapeString(Table.Name)+') '+
+ 'WHERE origin!='+EscapeString('pk'));
+ while not KeyQuery.Eof do begin
+ NewKey := TTableKey.Create(Self);
+ Result.Add(NewKey);
+ NewKey.Name := KeyQuery.Col('name');
+ NewKey.OldName := NewKey.Name;
+ NewKey.IndexType := IfThen(KeyQuery.Col('unique')='0', TTableKey.KEY, TTableKey.UNIQUE);
+ NewKey.OldIndexType := NewKey.IndexType;
+ ColQuery := GetResults('SELECT * '+
+ 'FROM '+QuoteIdent(Table.Database)+'.pragma_index_info('+EscapeString(NewKey.Name)+')');
+ while not ColQuery.Eof do begin
+ NewKey.Columns.Add(ColQuery.Col('name'));
+ NewKey.SubParts.Add('');
+ NewKey.Collations.Add('');
+ ColQuery.Next;
+ end;
+ ColQuery.Free;
+ KeyQuery.Next;
+ end;
+ KeyQuery.Free;
+end;
+
+
+{function TInterbaseConnection.GetTableKeys(Table: TDBObject): TTableKeyList;
+begin
+ Result := TTableKeyList.Create(True);
+end;}
+
+
+function TDBConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
+var
+ ForeignQuery, ColQuery: TDBQuery;
+ ForeignKey: TForeignKey;
+begin
+ // Generic: query table foreign keys from IS.?
+ Result := TForeignKeyList.Create(True);
+ if FForeignKeyQueriesFailed then begin
+ Log(lcDebug, 'Avoid foreign key retrieval with queries which failed before');
+ Exit;
+ end;
+ try
+ // Combine two IS tables by hand, not by JOIN, as this is too slow. See #852
+ ForeignQuery := GetResults('SELECT *'+
+ ' FROM '+InfSch+'.REFERENTIAL_CONSTRAINTS'+
+ ' WHERE'+
+ ' CONSTRAINT_SCHEMA='+EscapeString(Table.Database)+
+ ' AND TABLE_NAME='+EscapeString(Table.Name)+
+ ' AND REFERENCED_TABLE_NAME IS NOT NULL'
+ );
+ ColQuery := GetResults('SELECT *'+
+ ' FROM '+InfSch+'.KEY_COLUMN_USAGE'+
+ ' WHERE'+
+ ' TABLE_SCHEMA='+EscapeString(Table.Database)+
+ ' AND TABLE_NAME='+EscapeString(Table.Name)+
+ ' AND REFERENCED_TABLE_NAME IS NOT NULL'
+ );
+ try
+ while not ForeignQuery.Eof do begin
+ ForeignKey := TForeignKey.Create(Self);
+ Result.Add(ForeignKey);
+ ForeignKey.KeyName := ForeignQuery.Col('CONSTRAINT_NAME');
+ ForeignKey.OldKeyName := ForeignKey.KeyName;
+ ForeignKey.Db := Table.Database;
+ ForeignKey.ReferenceDb := ForeignQuery.Col('UNIQUE_CONSTRAINT_SCHEMA');
+ ForeignKey.ReferenceTable := ForeignQuery.Col('UNIQUE_CONSTRAINT_SCHEMA') +
+ '.' + ForeignQuery.Col('REFERENCED_TABLE_NAME');
+ ForeignKey.OnUpdate := ForeignQuery.Col('UPDATE_RULE');
+ ForeignKey.OnDelete := ForeignQuery.Col('DELETE_RULE');
+ while not ColQuery.Eof do begin
+ if ColQuery.Col('CONSTRAINT_NAME') = ForeignQuery.Col('CONSTRAINT_NAME') then begin
+ ForeignKey.Columns.Add(ColQuery.Col('COLUMN_NAME'));
+ ForeignKey.ForeignColumns.Add(ColQuery.Col('REFERENCED_COLUMN_NAME'));
+ end;
+ ColQuery.Next;
+ end;
+ ColQuery.First;
+ ForeignQuery.Next;
+ end;
+ ForeignQuery.Free;
+ ColQuery.Free;
+ except
+ // Don't silence errors here:
+ on E:EDbError do
+ Log(lcError, E.Message);
+ end;
+ except
+ // Silently ignore non existent IS tables and/or columns
+ // And remember to not fire such queries again here
+ on E:EDbError do begin
+ FForeignKeyQueriesFailed := True;
+ end;
+ end;
+end;
+
+
+{function TAdoDbConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
+var
+ ForeignQuery: TDBQuery;
+ ForeignKey: TForeignKey;
+begin
+ // MS SQL: see #150
+ Result := TForeignKeyList.Create(True);
+ ForeignQuery := GetResults('SELECT'+
+ ' f.name AS foreign_key_name,'+
+ ' COL_NAME(fc.parent_object_id, fc.parent_column_id) AS constraint_column_name,'+
+ ' OBJECT_NAME (f.referenced_object_id) AS referenced_object,'+
+ ' COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS referenced_column_name,'+
+ ' update_referential_action_desc,'+
+ ' delete_referential_action_desc'+
+ ' FROM sys.foreign_keys AS f'+
+ ' INNER JOIN sys.foreign_key_columns AS fc'+
+ ' ON f.object_id = fc.constraint_object_id'+
+ ' WHERE f.parent_object_id = OBJECT_ID('+EscapeString(Table.Name)+')'
+ );
+ ForeignKey := nil;
+ while not ForeignQuery.Eof do begin
+ if (not Assigned(ForeignKey)) or (ForeignKey.KeyName <> ForeignQuery.Col('foreign_key_name')) then begin
+ ForeignKey := TForeignKey.Create(Self);
+ Result.Add(ForeignKey);
+ ForeignKey.KeyName := ForeignQuery.Col('foreign_key_name');
+ ForeignKey.OldKeyName := ForeignKey.KeyName;
+ ForeignKey.ReferenceTable := ForeignQuery.Col('referenced_object');
+ ForeignKey.OnUpdate := ForeignQuery.Col('update_referential_action_desc');
+ ForeignKey.OnDelete := ForeignQuery.Col('delete_referential_action_desc');
+ end;
+ ForeignKey.Columns.Add(ForeignQuery.Col('constraint_column_name'));
+ ForeignKey.ForeignColumns.Add(ForeignQuery.Col('referenced_column_name'));
+ ForeignQuery.Next;
+ end;
+ ForeignQuery.Free;
+end;}
+
+
+function TPgConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
+var
+ ForeignQuery: TDBQuery;
+ ForeignKey: TForeignKey;
+begin
+ // see #158
+ Result := TForeignKeyList.Create(True);
+ try
+ ForeignQuery := GetResults('SELECT'+
+ ' refc.constraint_name,'+
+ ' refc.update_rule,'+
+ ' refc.delete_rule,'+
+ ' kcu.table_name,'+
+ ' STRING_AGG(distinct kcu.column_name, '','') AS columns,'+
+ ' ccu.table_schema AS ref_schema,'+
+ ' ccu.table_name AS ref_table,'+
+ ' STRING_AGG(distinct ccu.column_name, '','') AS ref_columns,'+
+ ' STRING_AGG(distinct kcu.ordinal_position::text, '','') AS ord_position'+
+ ' FROM'+
+ ' '+InfSch+'.referential_constraints AS refc,'+
+ ' '+InfSch+'.key_column_usage AS kcu,'+
+ ' '+InfSch+'.constraint_column_usage AS ccu'+
+ ' WHERE'+
+ ' refc.constraint_schema = '+EscapeString(Table.Schema)+
+ ' AND kcu.table_name = '+EscapeString(Table.Name)+
+ ' AND kcu.constraint_name = refc.constraint_name'+
+ ' AND kcu.table_schema = refc.constraint_schema'+
+ ' AND ccu.constraint_name = refc.constraint_name'+
+ ' AND ccu.constraint_schema = refc.constraint_schema'+
+ ' GROUP BY'+
+ ' refc.constraint_name,'+
+ ' refc.update_rule,'+
+ ' refc.delete_rule,'+
+ ' kcu.table_name,'+
+ ' ccu.table_schema,'+
+ ' ccu.table_name'+
+ ' ORDER BY'+
+ ' ord_position'
+ );
+ while not ForeignQuery.Eof do begin
+ ForeignKey := TForeignKey.Create(Self);
+ Result.Add(ForeignKey);
+ ForeignKey.KeyName := ForeignQuery.Col('constraint_name');
+ ForeignKey.OldKeyName := ForeignKey.KeyName;
+ ForeignKey.Db := Table.Schema;
+ ForeignKey.ReferenceDb := ForeignQuery.Col('ref_schema');
+ ForeignKey.ReferenceTable := ForeignQuery.Col('ref_schema')+'.'+ForeignQuery.Col('ref_table');
+ ForeignKey.OnUpdate := ForeignQuery.Col('update_rule');
+ ForeignKey.OnDelete := ForeignQuery.Col('delete_rule');
+ ForeignKey.Columns.CommaText := ForeignQuery.Col('columns');
+ ForeignKey.ForeignColumns.CommaText := ForeignQuery.Col('ref_columns');
+ ForeignQuery.Next;
+ end;
+ ForeignQuery.Free;
+ except
+ // STRING_AGG() fails on pre-v9 servers,
+ // and the alternative ARRAY_TO_STRING(ARRAY_AGG(x), ',') fails on v9+ servers
+ // See https://www.heidisql.com/forum.php?t=36149
+ on E:EDbError do
+ Log(lcError, 'Foreign key detection failed: '+E.Message);
+ end;
+end;
+
+
+function TSQLiteConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
+var
+ ForeignQuery: TDBQuery;
+ ForeignKey: TForeignKey;
+begin
+ // SQLite: query PRAGMA foreign_key_list
+ Result := TForeignKeyList.Create(True);
+ ForeignQuery := GetResults('SELECT * '+
+ 'FROM '+QuoteIdent(Table.Database)+'.pragma_foreign_key_list('+EscapeString(Table.Name)+')');
+ ForeignKey := nil;
+ while not ForeignQuery.Eof do begin
+ if (not Assigned(ForeignKey)) or (ForeignKey.KeyName <> ForeignQuery.Col('id')) then begin
+ ForeignKey := TForeignKey.Create(Self);
+ Result.Add(ForeignKey);
+ ForeignKey.KeyName := ForeignQuery.Col('id');
+ ForeignKey.OldKeyName := ForeignKey.KeyName;
+ ForeignKey.ReferenceTable := ForeignQuery.Col('table');
+ ForeignKey.OnUpdate := ForeignQuery.Col('on_update');
+ ForeignKey.OnDelete := ForeignQuery.Col('on_delete');
+ end;
+ ForeignKey.Columns.Add(ForeignQuery.Col('from'));
+ ForeignKey.ForeignColumns.Add(ForeignQuery.Col('to'));
+ ForeignQuery.Next;
+ end;
+ ForeignQuery.Free;
+end;
+
+
+{function TInterbaseConnection.GetTableForeignKeys(Table: TDBObject): TForeignKeyList;
+var
+ ForeignQuery: TDBQuery;
+ ForeignKey: TForeignKey;
+begin
+ // SQLite: query PRAGMA foreign_key_list
+ Result := TForeignKeyList.Create(True);
+ ForeignQuery := GetResults(
+ 'select strc.rdb$relation_name' +#13#10+
+ ' , strc.rdb$constraint_name' +#13#10+
+ ' , fkrc.rdb$relation_name as "ReferenceTable"' +#13#10+
+ ' , stis.rdb$field_name as "from"' +#13#10+
+ ' , fkis.rdb$field_name as "to"' +#13#10+
+ ' , rdb$ref_constraints.rdb$update_rule' +#13#10+
+ ' , rdb$ref_constraints.rdb$delete_rule' +#13#10+
+ ' from rdb$relation_constraints strc' +#13#10+
+ ' join rdb$ref_constraints on RDB$REF_CONSTRAINTS.rdb$constraint_name = strc.rdb$constraint_name' +#13#10+
+ ' join rdb$relation_constraints fkrc on fkrc.rdb$constraint_name = rdb$ref_constraints.rdb$const_name_uq' +#13#10+
+ ' join rdb$index_segments stis on stis.rdb$index_name = strc.rdb$index_name' +#13#10+
+ ' join rdb$index_segments fkis on fkis.rdb$index_name = fkrc.rdb$index_name' +#13#10+
+ ' where strc.rdb$relation_name = ' +QuotedStr(Table.Name)+#13#10+
+ ' and strc.rdb$constraint_type = ''FOREIGN KEY''');
+
+ ForeignKey := nil;
+ while not ForeignQuery.Eof do begin
+ if (not Assigned(ForeignKey)) or (ForeignKey.KeyName <> ForeignQuery.Col('rdb$constraint_name')) then begin
+ ForeignKey := TForeignKey.Create(Self);
+ Result.Add(ForeignKey);
+ ForeignKey.KeyName := ForeignQuery.Col('rdb$constraint_name');
+ ForeignKey.OldKeyName := ForeignKey.KeyName;
+ ForeignKey.ReferenceTable := ForeignQuery.Col('ReferenceTable');
+ ForeignKey.OnUpdate := ForeignQuery.Col('rdb$update_rule');
+ ForeignKey.OnDelete := ForeignQuery.Col('rdb$delete_rule');
+ end;
+ ForeignKey.Columns.Add(ForeignQuery.Col('from'));
+ ForeignKey.ForeignColumns.Add(ForeignQuery.Col('to'));
+ ForeignQuery.Next;
+ end;
+ ForeignQuery.Free;
+end;}
+
+
+function TDBConnection.GetTableCheckConstraints(Table: TDBObject): TCheckConstraintList;
+var
+ CheckQuery: TDBQuery;
+ CheckConstraint: TCheckConstraint;
+ ConTableIdx, TconTableIdx: Integer;
+begin
+ Result := TCheckConstraintList.Create(True);
+ ConTableIdx := FInformationSchemaObjects.IndexOf('CHECK_CONSTRAINTS');
+ TconTableIdx := FInformationSchemaObjects.IndexOf('TABLE_CONSTRAINTS');
+ if (ConTableIdx = -1) or (TconTableIdx = -1) then
+ Exit;
+
+ try
+ if FParameters.IsMariaDB then begin
+ CheckQuery := GetResults('SELECT CONSTRAINT_NAME, CHECK_CLAUSE'+
+ ' FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent(FInformationSchemaObjects[ConTableIdx])+
+ ' WHERE'+
+ ' '+Table.SchemaClauseIS('CONSTRAINT')+
+ ' AND TABLE_NAME='+EscapeString(Table.Name)
+ );
+ end
+ else begin
+ CheckQuery := GetResults('SELECT tc.CONSTRAINT_NAME, cc.CHECK_CLAUSE'+
+ ' FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent(FInformationSchemaObjects[ConTableIdx])+' AS cc, '+
+ QuoteIdent(InfSch)+'.'+QuoteIdent(FInformationSchemaObjects[TconTableIdx])+' AS tc'+
+ ' WHERE'+
+ ' '+Table.SchemaClauseIS('tc.CONSTRAINT')+
+ ' AND tc.TABLE_NAME='+EscapeString(Table.Name)+
+ ' AND tc.CONSTRAINT_TYPE='+EscapeString('CHECK')+
+ ' AND tc.CONSTRAINT_SCHEMA=cc.CONSTRAINT_SCHEMA'+
+ ' AND tc.CONSTRAINT_NAME=cc.CONSTRAINT_NAME'+
+ IfThen(FParameters.IsAnyPostgreSQL, ' AND cc.CONSTRAINT_NAME NOT LIKE '+EscapeString('%\_not\_null'), '')
+ );
+ end;
+ while not CheckQuery.Eof do begin
+ CheckConstraint := TCheckConstraint.Create(Self);
+ Result.Add(CheckConstraint);
+ CheckConstraint.Name := CheckQuery.Col('CONSTRAINT_NAME');
+ CheckConstraint.CheckClause := CheckQuery.Col('CHECK_CLAUSE');
+ CheckQuery.Next;
+ end;
+ CheckQuery.Free;
+ except
+ on E:EDbError do begin
+ Log(lcError, 'Detection of check constraints disabled due to error in query');
+ // Table is likely not there or does not have expected columns - prevent further queries with the same error:
+ FInformationSchemaObjects.Delete(ConTableIdx);
+ end;
+ end;
+end;
+
+
+function TDBConnection.IsNumeric(Text: String): Boolean;
+begin
+ // Check if value is an integer or float number
+ Result := ExecRegExpr('^[+-]?\d+(\.\d+)?$', Text);
+end;
+
+
+function TDBConnection.IsHex(Text: String): Boolean;
+var
+ i, Len: Integer;
+const
+ HexChars: TSysCharSet = ['0'..'9','a'..'f', 'A'..'F'];
+begin
+ // Check first kilobyte of passed text whether it's a hex encoded string. Hopefully faster than a regex.
+ Result := False;
+ Len := Length(Text);
+ if Len >= 3 then begin
+ Result := (Text[1] = '0') and (Text[2] = 'x');
+ if Result then begin
+ for i:=3 to SIZE_KB do begin
+ if not CharInSet(Text[i], HexChars) then begin
+ Result := False;
+ Break;
+ end;
+ if i >= Len then
+ Break;
+ end;
+ end;
+ end;
+end;
+
+function TDBConnection.Has(Item: TFeatureOrRequirement): Boolean;
+begin
+ case FParameters.NetTypeGroup of
+ ngMySQL:
+ case Item of
+ frSrid: Result := FParameters.IsMySQL(True) and (ServerVersionInt >= 80000);
+ frTimezoneVar: Result := ServerVersionInt >= 40103;
+ frTemporalTypesFraction: Result := (FParameters.IsMariaDB and (ServerVersionInt >= 50300)) or
+ (FParameters.IsMySQL(True) and (ServerVersionInt >= 50604));
+ frKillQuery: Result := (not FParameters.IsMySQLonRDS) and (ServerVersionInt >= 50000);
+ frLockedTables: Result := (not FParameters.IsProxySQLAdmin) and (ServerVersionInt >= 50124);
+ frShowCreateTrigger: Result := ServerVersionInt >= 50121;
+ frShowWarnings: Result := ServerVersionInt >= 40100;
+ frShowCollation: Result := ServerVersionInt >= 40100;
+ frShowCollationExtended: Result := FParameters.IsMariaDB and (ServerVersionInt >= 101001);
+ frShowCharset: Result := ServerVersionInt >= 40100;
+ frIntegerDisplayWidth: Result := (FParameters.IsMySQL(True) and (ServerVersionInt < 80017)) or
+ (not FParameters.IsMySQL(True));
+ frShowFunctionStatus: Result := (not Parameters.IsProxySQLAdmin) and (ServerVersionInt >= 50000);
+ frShowProcedureStatus: Result := (not FParameters.IsProxySQLAdmin) and (ServerVersionInt >= 50000);
+ frShowTriggers: Result := (not FParameters.IsProxySQLAdmin) and (ServerVersionInt >= 50010);
+ frShowEvents: Result := (not Parameters.IsProxySQLAdmin) and (ServerVersionInt >= 50100);
+ frColumnDefaultParentheses: Result := FParameters.IsMySQL(True) and (ServerVersionInt >= 80013);
+ frForeignKeyChecksVar: Result := ServerVersionInt >= 40014;
+ frHelpKeyword: Result := (not FParameters.IsProxySQLAdmin) and (ServerVersionInt >= 40100);
+ frEditVariables: Result := ServerVersionInt >= 40003;
+ frCreateView: Result := ServerVersionInt >= 50001;
+ frCreateProcedure: Result := ServerVersionInt >= 50003;
+ frCreateFunction: Result := ServerVersionInt >= 50003;
+ frCreateTrigger: Result := ServerVersionInt >= 50002;
+ frCreateEvent: Result := ServerVersionInt >= 50100;
+ frInvisibleColumns: Result := (FParameters.IsMariaDB and (ServerVersionInt >= 100303)) or
+ (FParameters.IsMySQL(True) and (ServerVersionInt >= 80023));
+ end;
+ else Result := False;
+ end;
+end;
+
+
+function TDBConnection.GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64;
+var
+ Rows: String;
+begin
+ // Get row number from a table
+ Rows := GetVar('SELECT COUNT(*) FROM '+QuoteIdent(Obj.Database)+'.'+QuoteIdent(Obj.Name), 0);
+ Result := MakeInt(Rows);
+end;
+
+
+function TMySQLConnection.GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64;
+var
+ Rows: String;
+begin
+ // Get row number from a mysql table
+ if Parameters.IsProxySQLAdmin or ForceExact then begin
+ Result := inherited
+ end
+ else begin
+ Rows := GetVar('SHOW TABLE STATUS LIKE '+EscapeString(Obj.Name), 'Rows');
+ Result := MakeInt(Rows);
+ end;
+end;
+
+
+{function TAdoDBConnection.GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64;
+var
+ Rows: String;
+begin
+ // Get row number from a mssql table
+ if ServerVersionInt >= 900 then begin
+ Rows := GetVar('SELECT SUM('+QuoteIdent('rows')+') FROM '+QuoteIdent('sys')+'.'+QuoteIdent('partitions')+
+ ' WHERE '+QuoteIdent('index_id')+' IN (0, 1)'+
+ ' AND '+QuoteIdent('object_id')+' = object_id('+EscapeString(Obj.Database+'.'+Obj.Schema+'.'+Obj.Name)+')'
+ );
+ end else begin
+ Rows := GetVar('SELECT COUNT(*) FROM '+Obj.QuotedDbAndTableName);
+ end;
+ Result := MakeInt(Rows);
+end;}
+
+
+function TPgConnection.GetRowCount(Obj: TDBObject; ForceExact: Boolean=False): Int64;
+var
+ Rows: String;
+begin
+ // Get row number from a postgres table
+ Rows := GetVar('SELECT '+QuoteIdent('reltuples')+'::bigint FROM '+QuoteIdent('pg_class')+
+ ' LEFT JOIN '+QuoteIdent('pg_namespace')+
+ ' ON ('+QuoteIdent('pg_namespace')+'.'+QuoteIdent('oid')+' = '+QuoteIdent('pg_class')+'.'+QuoteIdent('relnamespace')+')'+
+ ' WHERE '+QuoteIdent('pg_class')+'.'+QuoteIdent('relkind')+'='+EscapeString('r')+
+ ' AND '+QuoteIdent('pg_namespace')+'.'+QuoteIdent('nspname')+'='+EscapeString(Obj.Database)+
+ ' AND '+QuoteIdent('pg_class')+'.'+QuoteIdent('relname')+'='+EscapeString(Obj.Name)
+ );
+ Result := MakeInt(Rows);
+end;
+
+
+procedure TDBConnection.Drop(Obj: TDBObject);
+begin
+ Query('DROP '+UpperCase(Obj.ObjType)+' '+Obj.QuotedName);
+end;
+
+
+procedure TPgConnection.Drop(Obj: TDBObject);
+var
+ sql: String;
+ i: Integer;
+ Params: TRoutineParamList;
+begin
+ case Obj.NodeType of
+ lntFunction, lntProcedure: begin
+ sql := 'DROP '+UpperCase(Obj.ObjType)+' '+Obj.QuotedName+'(';
+ Params := TRoutineParamList.Create;
+ ParseRoutineStructure(Obj, Params);
+ for i:=0 to Params.Count-1 do begin
+ if Obj.NodeType = lntProcedure then
+ sql := sql + Params[i].Context + ' ';
+ sql := sql + QuoteIdent(Params[i].Name) + ' ' + Params[i].Datatype;
+ if i < Params.Count-1 then
+ sql := sql + ', ';
+ end;
+ sql := sql + ')';
+ Query(sql);
+ end;
+ else
+ inherited;
+ end;
+end;
+
+
+function TDBConnection.GetSQLSpecifity(Specifity: TSQLSpecifityId): String;
+begin
+ // Return some version specific SQL clause or snippet
+ Result := FSQLSpecifities[Specifity];
+end;
+
+
+function TDBConnection.GetSQLSpecifity(Specifity: TSQLSpecifityId; const Args: array of const): String;
+begin
+ Result := GetSQLSpecifity(Specifity);
+ Result := Format(Result, Args);
+end;
+
+
+function TDBConnection.ResultCount;
+begin
+ case Parameters.NetTypeGroup of
+ ngMySQL:
+ Result := Length(TMySQLConnection(Self).LastRawResults);
+ //ngMSSQL:
+ // Result := Length(TAdoDBConnection(Self).LastRawResults);
+ ngPgSQL:
+ Result := Length(TPGConnection(Self).LastRawResults);
+ ngSQLite:
+ Result := Length(TSQLiteConnection(Self).LastRawResults);
+ //ngInterbase:
+ // Result := Length(TInterbaseConnection(Self).LastRawResults);
+ else
+ raise Exception.CreateFmt(_(MsgUnhandledNetType), [Integer(Parameters.NetType)]);
+ end;
+end;
+
+
+function TDBConnection.GetConnectionUptime: Integer;
+begin
+ // Return seconds since last connect
+ if not FActive then
+ Result := 0
+ else
+ Result := (GetTickCount div 1000) - FConnectionStarted;
+end;
+
+
+function TDBConnection.GetServerUptime: Integer;
+begin
+ // Return server uptime in seconds. Return -1 if unknown.
+ if FServerUptime > 0 then
+ Result := Cardinal(FServerUptime) + ((GetTickCount div 1000) - FConnectionStarted)
+ else
+ Result := -1;
+end;
+
+
+function TDBConnection.GetServerNow: TDateTime;
+var
+ d: TDateTime;
+begin
+ // Return server datetime. Return -1 if unknown.
+ if not FServerDateTimeOnStartup.IsEmpty then begin
+ d := StrToDateTimeDef(FServerDateTimeOnStartup, 0);
+ Result := IncSecond(d, (GetTickCount div 1000) - FConnectionStarted);
+ end else
+ Result := -1;
+end;
+
+
+function TDBConnection.GetCurrentUserHostCombination: String;
+begin
+ // Return current user@host combination, used by various object editors for DEFINER clauses
+ Log(lcDebug, 'Fetching user@host ...');
+ Ping(True);
+ if FCurrentUserHostCombination.IsEmpty and (not GetSQLSpecifity(spCurrentUserHost).IsEmpty) then
+ FCurrentUserHostCombination := GetVar(GetSQLSpecifity(spCurrentUserHost))
+ else
+ FCurrentUserHostCombination := '';
+ Result := FCurrentUserHostCombination;
+end;
+
+
+function TDBConnection.GetAllUserHostCombinations: TStringList;
+begin
+ // For populating combobox items
+ if not Assigned(FAllUserHostCombinations) then begin
+ try
+ FAllUserHostCombinations := GetCol('SELECT CONCAT('+QuoteIdent('User')+', '+EscapeString('@')+', '+QuoteIdent('Host')+') '+
+ 'FROM '+QuoteIdent('mysql')+'.'+QuoteIdent('user')+' '+
+ 'WHERE '+QuoteIdent('User')+'!='+EscapeString('')+' '+
+ 'ORDER BY '+QuoteIdent('User')+', '+QuoteIdent('Host'));
+ except on E:EDbError do
+ FAllUserHostCombinations := TStringList.Create;
+ end;
+ end;
+ Result := FAllUserHostCombinations;
+end;
+
+
+function TDBConnection.GetDateTimeValue(Input: String; Datatype: TDBDatatypeIndex): String;
+var
+ rx: TRegExpr;
+begin
+ // Return date/time string value as expected by server
+ case Parameters.NetTypeGroup of
+ ngMSSQL: begin
+ rx := TRegExpr.Create;
+ rx.Expression := '^(\d+\-\d+\-\d+)\s(\d+\:.+)$';
+ Result := Input;
+ if rx.Exec(Input) then begin
+ // Inject "T" between date and time, for MSSQL. See http://www.heidisql.com/forum.php?t=18441
+ Result := rx.Match[1] + 'T' + rx.Match[2];
+ end;
+ rx.Free;
+ end;
+ else
+ Result := Input;
+ end;
+end;
+
+
+
+procedure TDBConnection.ClearCache(IncludeDBObjects: Boolean);
+begin
+ // Free cached lists and results. Called when the connection was closed and/or destroyed
+ PurgePrefetchResults;
+ FreeAndNil(FCollationTable);
+ FreeAndNil(FCharsetTable);
+ FreeAndNil(FSessionVariables);
+ FreeAndNil(FTableEngines);
+ if IncludeDBObjects then begin
+ ClearAllDbObjects;
+ FColumnCache.Clear;
+ FKeyCache.Clear;
+ FForeignKeyCache.Clear;
+ FCheckConstraintCache.Clear;
+ end;
+ FTableEngineDefault := '';
+ FCurrentUserHostCombination := '';
+ FThreadID := 0;
+end;
+
+
+procedure TDBConnection.ClearDbObjects(db: String);
+var
+ i: Integer;
+begin
+ // Free cached database object list
+ for i:=FDatabaseCache.Count-1 downto 0 do begin
+ if FDatabaseCache[i].Database = db then begin
+ FDatabaseCache.Delete(i);
+ end;
+ end;
+end;
+
+
+procedure TDBConnection.ClearAllDbObjects;
+var
+ i: Integer;
+begin
+ for i:=FDatabaseCache.Count-1 downto 0 do begin
+ if FDatabaseCache.Count > i then
+ ClearDbObjects(FDatabaseCache[i].Database);
+ end;
+end;
+
+
+function TDBConnection.DbObjectsCached(db: String): Boolean;
+var
+ i: Integer;
+begin
+ // Check if a table list is stored in cache
+ Result := False;
+ for i:=0 to FDatabaseCache.Count-1 do begin
+ if FDatabaseCache[i].Database = db then begin
+ Result := True;
+ break;
+ end;
+ end;
+end;
+
+
+function TDBConnection.ParseDateTime(Str: String): TDateTime;
+var
+ rx: TRegExpr;
+begin
+ // Parse SQL date/time string value into a TDateTime
+ Result := 0;
+ rx := TRegExpr.Create;
+ rx.Expression := '^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})\:(\d{2})\:(\d{2})';
+ if rx.Exec(Str) then try
+ Result := EncodeDateTime(
+ StrToIntDef(rx.Match[1], 0),
+ StrToIntDef(rx.Match[2], 1),
+ StrToIntDef(rx.Match[3], 1),
+ StrToIntDef(rx.Match[4], 0),
+ StrToIntDef(rx.Match[5], 0),
+ StrToIntDef(rx.Match[6], 0),
+ 0 // milliseconds, unused
+ );
+ except
+ Result := 0;
+ end;
+end;
+
+
+function TDBConnection.GetDbObjects(db: String; Refresh: Boolean=False; OnlyNodeType: TListNodeType=lntNone): TDBObjectList;
+var
+ CacheAllTypes, TempList: TDBObjectList;
+ i, j, ObjIndex: Integer;
+ DbObjectCopy: TDBObject;
+begin
+ // Cache and return a db's table list
+
+ // Find all-types list in cache
+ CacheAllTypes := nil;
+ for i:=0 to FDatabaseCache.Count-1 do begin
+ if (FDatabaseCache[i].Database = db) and (FDatabaseCache[i].OnlyNodeType=lntNone) then begin
+ CacheAllTypes := FDatabaseCache[i];
+ Break;
+ end;
+ end;
+
+ // First time creation of all-types list
+ if CacheAllTypes = nil then begin
+ CacheAllTypes := TDBObjectList.Create(TDBObjectComparer.Create, True);
+ CacheAllTypes.FOnlyNodeType := lntNone;
+ CacheAllTypes.FDatabase := db;
+ CacheAllTypes.FObjectsLoaded := False;
+ FDatabaseCache.Add(CacheAllTypes);
+ end;
+ // Fill all-types list if not yet fetched
+ if (not CacheAllTypes.FObjectsLoaded) or Refresh then begin
+ TempList := TDBObjectList.Create(TDBObjectComparer.Create, False);
+ FetchDbObjects(db, TempList);
+ // Find youngest last update
+ {for i:=0 to TempList.Count-1 do begin
+ TempList.FLastUpdate := Max(TempList.FLastUpdate, max(TempList[i].Updated, TempList[i].Created));
+ end;}
+ // Sort list like it get sorted in AnyGridCompareNodes
+ TempList.Sort;
+
+ CacheAllTypes.FLargestObjectSize := TempList.FLargestObjectSize;
+ CacheAllTypes.FLastUpdate := TempList.FLastUpdate;
+ CacheAllTypes.FDataSize := TempList.FDataSize;
+ CacheAllTypes.FObjectsLoaded := True;
+ // Assign templist properties to existing objects and add non existing
+ for i:=0 to TempList.Count-1 do begin
+ ObjIndex := -1;
+ for j:=0 to CacheAllTypes.Count-1 do begin
+ if CacheAllTypes[j].IsSameAs(TempList[i]) then begin
+ ObjIndex := j;
+ Break;
+ end;
+ end;
+ if ObjIndex > -1 then
+ CacheAllTypes[ObjIndex].Assign(TempList[i])
+ else
+ CacheAllTypes.Add(TempList[i]);
+ end;
+ // Delete no longer existing
+ for i:=0 to CacheAllTypes.Count-1 do begin
+ ObjIndex := -1;
+ for j:=0 to TempList.Count-1 do begin
+ if TempList[j].IsSameAs(CacheAllTypes[i]) then begin
+ ObjIndex := j;
+ Break;
+ end;
+ end;
+ if ObjIndex = -1 then
+ CacheAllTypes.Delete(i);
+ end;
+ // Free list, clear detail caches and call change event
+ TempList.Free;
+ FColumnCache.Clear;
+ FKeyCache.Clear;
+ FForeignKeyCache.Clear;
+ FCheckConstraintCache.Clear;
+ if Assigned(FOnObjectnamesChanged) then
+ FOnObjectnamesChanged(Self, db);
+ end;
+
+ // Now we can see if we already have a result with the right type.
+ // All-types list is already there, so this first loop should find it.
+ Result := nil;
+ for i:=0 to FDatabaseCache.Count-1 do begin
+ if (FDatabaseCache[i].Database = db) and (FDatabaseCache[i].OnlyNodeType=OnlyNodeType) then begin
+ Result := FDatabaseCache[i];
+ break;
+ end;
+ end;
+ // Certain-types list not yet in cache. Create and cache it
+ if Result = nil then begin
+ Result := TDBObjectList.Create(TDBObjectComparer.Create, True);
+ Result.FOnlyNodeType := OnlyNodeType;
+ Result.FLastUpdate := CacheAllTypes.FLastUpdate;
+ Result.FDataSize := CacheAllTypes.FDataSize;
+ Result.FObjectsLoaded := True;
+ Result.FDatabase := CacheAllTypes.FDatabase;
+ Result.FCollation := CacheAllTypes.FCollation;
+ for i:=0 to CacheAllTypes.Count-1 do begin
+ if CacheAllTypes[i].NodeType = OnlyNodeType then begin
+ DbObjectCopy := TDBObject.Create(Self);
+ DbObjectCopy.Assign(CacheAllTypes[i]);
+ Result.Add(DbObjectCopy);
+ end;
+ end;
+ FDatabaseCache.Add(Result);
+ end;
+end;
+
+
+procedure TMySQLConnection.FetchDbObjects(db: String; var Cache: TDBObjectList);
+var
+ obj: TDBObject;
+ Results: TDBQuery;
+ rx: TRegExpr;
+ SchemaBug41907Exists, DbNameMatches: Boolean;
+begin
+ // Return a db's table list
+ try
+ Cache.FCollation := GetVar('SELECT '+QuoteIdent('DEFAULT_COLLATION_NAME')+
+ ' FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent('SCHEMATA')+
+ ' WHERE '+QuoteIdent('SCHEMA_NAME')+'='+EscapeString(db));
+ except
+ Cache.FCollation := '';
+ end;
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+
+ // Tables and views
+ Results := nil;
+ try
+ if Parameters.IsProxySQLAdmin then begin
+ Results := GetResults('SHOW TABLES FROM '+QuoteIdent(db));
+ end else if Parameters.FullTableStatus or (UpperCase(db) = UpperCase(InfSch)) then begin
+ Results := GetResults('SHOW TABLE STATUS FROM '+QuoteIdent(db));
+ end else begin
+ Results := GetResults('SELECT '+
+ QuoteIdent('TABLE_NAME')+' AS '+QuoteIdent('Name')+', '+
+ QuoteIdent('ENGINE')+' AS '+QuoteIdent('Engine')+', '+
+ QuoteIdent('VERSION')+' AS '+QuoteIdent('Version')+', '+
+ QuoteIdent('TABLE_COLLATION')+' AS '+QuoteIdent('Collation')+', '+
+ QuoteIdent('TABLE_COMMENT')+' AS '+QuoteIdent('Comment')+', '+
+ 'NULL AS '+QuoteIdent('Create_time')+', '+
+ 'NULL AS '+QuoteIdent('Update_time')+', '+
+ 'NULL AS '+QuoteIdent('Data_length')+', '+
+ 'NULL AS '+QuoteIdent('Index_length')+', '+
+ 'NULL AS '+QuoteIdent('Rows')+', '+
+ 'NULL AS '+QuoteIdent('Auto_increment')+', '+
+ 'NULL AS '+QuoteIdent('Row_format')+', '+
+ 'NULL AS '+QuoteIdent('Avg_row_length')+', '+
+ 'NULL AS '+QuoteIdent('Max_data_length')+', '+
+ 'NULL AS '+QuoteIdent('Data_free')+', '+
+ 'NULL AS '+QuoteIdent('Check_time')+', '+
+ 'NULL AS '+QuoteIdent('Checksum')+', '+
+ 'NULL AS '+QuoteIdent('Create_options')+
+ ' FROM '+InfSch+'.TABLES'+
+ ' WHERE TABLE_SCHEMA='+EscapeString(db)+' AND TABLE_TYPE IN('+EscapeString('BASE TABLE')+', '+EscapeString('VIEW')+')'
+ );
+ end;
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col(0);
+ obj.Database := db;
+ obj.Rows := StrToInt64Def(Results.Col('Rows', True), -1);
+ if (not Results.IsNull('Data_length')) and (not Results.IsNull('Index_length')) then begin
+ Obj.Size := StrToInt64Def(Results.Col('Data_length', True), 0) + StrToInt64Def(Results.Col('Index_length', True), 0);
+ Inc(Cache.FDataSize, Obj.Size);
+ Cache.FLargestObjectSize := Max(Cache.FLargestObjectSize, Obj.Size);
+ end;
+ Obj.NodeType := lntTable;
+ if Results.IsNull(1) and Results.IsNull(2) then // Engine column is NULL for views
+ Obj.NodeType := lntView;
+ Obj.Created := ParseDateTime(Results.Col('Create_time', True));
+ Obj.Updated := ParseDateTime(Results.Col('Update_time', True));
+ if Results.ColExists('Type') then
+ Obj.Engine := Results.Col('Type', True)
+ else
+ Obj.Engine := Results.Col('Engine', True);
+ Obj.Comment := Results.Col('Comment', True);
+ // Sanitize comment from automatically appendage
+ rx.Expression := '(;\s*)?InnoDB\s*free\:.*$';
+ Obj.Comment := rx.Replace(Obj.Comment, '', False);
+ Obj.Version := StrToInt64Def(Results.Col('Version', True), Obj.Version);
+ Obj.AutoInc := StrToInt64Def(Results.Col('Auto_increment', True), Obj.AutoInc);
+ Obj.RowFormat := Results.Col('Row_format', True);
+ Obj.AvgRowLen := StrToInt64Def(Results.Col('Avg_row_length', True), Obj.AvgRowLen);
+ Obj.MaxDataLen := StrToInt64Def(Results.Col('Max_data_length', True), Obj.MaxDataLen);
+ Obj.IndexLen := StrToInt64Def(Results.Col('Index_length', True), Obj.IndexLen);
+ Obj.DataLen := StrToInt64Def(Results.Col('Data_length', True), Obj.DataLen);
+ Obj.DataFree := StrToInt64Def(Results.Col('Data_free', True), Obj.DataFree);
+ Obj.LastChecked := ParseDateTime(Results.Col('Check_time', True));
+ Obj.Collation := Results.Col('Collation', True);
+ Obj.CheckSum := StrToInt64Def(Results.Col('Checksum', True), Obj.CheckSum);
+ Obj.CreateOptions := Results.Col('Create_options', True);
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+ // Stored functions
+ if Has(frShowFunctionStatus) then try
+ Results := GetResults('SHOW FUNCTION STATUS WHERE '+QuoteIdent('Db')+'='+EscapeString(db));
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('Name');
+ obj.Database := db;
+ Obj.NodeType := lntFunction;
+ Obj.Created := ParseDateTime(Results.Col('Created'));
+ Obj.Updated := ParseDateTime(Results.Col('Modified'));
+ Obj.Comment := Results.Col('Comment');
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+ // Stored procedures
+ if Has(frShowProcedureStatus) then try
+ Results := GetResults('SHOW PROCEDURE STATUS WHERE '+QuoteIdent('Db')+'='+EscapeString(db));
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('Name');
+ obj.Database := db;
+ Obj.NodeType := lntProcedure;
+ Obj.Created := ParseDateTime(Results.Col('Created'));
+ Obj.Updated := ParseDateTime(Results.Col('Modified'));
+ Obj.Comment := Results.Col('Comment');
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+ // Triggers
+ if Has(frShowTriggers) then try
+ Results := GetResults('SHOW TRIGGERS FROM '+QuoteIdent(db));
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('Trigger');
+ obj.Database := db;
+ Obj.NodeType := lntTrigger;
+ Obj.Created := ParseDateTime(Results.Col('Created'));
+ Obj.Comment := Results.Col('Timing')+' '+Results.Col('Event')+' in table '+QuoteIdent(Results.Col('Table'));
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+ // Events
+ if Has(frShowEvents) then try
+ Results := GetResults('SELECT *, EVENT_SCHEMA AS '+QuoteIdent('Db')+', EVENT_NAME AS '+QuoteIdent('Name')+
+ ' FROM '+InfSch+'.'+QuoteIdent('EVENTS')+' WHERE '+QuoteIdent('EVENT_SCHEMA')+'='+EscapeString(db))
+ except
+ on E:EDbError do begin
+ try
+ Results := GetResults('SHOW EVENTS FROM '+QuoteIdent(db));
+ except
+ on EDbError do;
+ end;
+ end;
+ end;
+ if Assigned(Results) then begin
+ // Work around old MySQL bug: https://bugs.mysql.com/bug.php?id=41907#c360194
+ // "Noted [fixed] in 5.1.57, 5.5.12, 5.6.3 changelogs."
+ SchemaBug41907Exists := (ServerVersionInt < 50157) or
+ ((ServerVersionInt >= 50500) and (ServerVersionInt < 50512)) or
+ ((ServerVersionInt >= 50600) and (ServerVersionInt < 50603));
+ while not Results.Eof do begin
+ DbNameMatches := CompareText(Results.Col('Db'), db) = 0;
+ if (SchemaBug41907Exists and DbNameMatches) or (not SchemaBug41907Exists) then begin
+ Obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ Obj.Name := Results.Col('Name');
+ Obj.Created := ParseDateTime(Results.Col('CREATED', True));
+ Obj.Updated := ParseDateTime(Results.Col('LAST_ALTERED', True));
+ Obj.LastChecked := ParseDateTime(Results.Col('STARTS', True));
+ Obj.Comment := Results.Col('EVENT_COMMENT', True);
+ Obj.Size := Length(Results.Col('EVENT_DEFINITION', True));
+ Obj.Database := db;
+ Obj.NodeType := lntEvent;
+ end;
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+end;
+
+
+{procedure TAdoDBConnection.FetchDbObjects(db: String; var Cache: TDBObjectList);
+var
+ obj: TDBObject;
+ Results: TDBQuery;
+ tp, SchemaSelect: String;
+begin
+ // Tables, views and procedures
+ Results := nil;
+ // Schema support introduced in MSSQL 2005 (9.0). See issue #3212.
+ SchemaSelect := EscapeString('');
+ if ServerVersionInt >= 900 then
+ SchemaSelect := 'SCHEMA_NAME('+QuoteIdent('schema_id')+')';
+ try
+ Results := GetResults('SELECT *, '+SchemaSelect+' AS '+EscapeString('schema')+
+ ' FROM '+QuoteIdent(db)+GetSQLSpecifity(spDbObjectsTable)+
+ ' WHERE '+QuoteIdent('type')+' IN ('+EscapeString('P')+', '+EscapeString('U')+', '+EscapeString('V')+', '+EscapeString('TR')+', '+EscapeString('FN')+', '+EscapeString('TF')+', '+EscapeString('IF')+')');
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('name');
+ obj.Created := ParseDateTime(Results.Col(GetSQLSpecifity(spDbObjectsCreateCol), True));
+ obj.Updated := ParseDateTime(Results.Col(GetSQLSpecifity(spDbObjectsUpdateCol), True));
+ obj.Schema := Results.Col('schema');
+ obj.Database := db;
+ tp := Trim(Results.Col(GetSQLSpecifity(spDbObjectsTypeCol), True));
+ if tp = 'U' then
+ obj.NodeType := lntTable
+ else if tp = 'P' then
+ obj.NodeType := lntProcedure
+ else if tp = 'V' then
+ obj.NodeType := lntView
+ else if tp = 'TR' then
+ obj.NodeType := lntTrigger
+ else if (tp = 'FN') or (tp = 'TF') or (tp = 'IF') then
+ obj.NodeType := lntFunction;
+ // Set reasonable default value for calculation of export chunks. See #343
+ // OFFSET..FETCH supported from v11.0/2012
+ // Disabled, leave at -1 and prefer a generic calculation in TfrmTableTools.DoExport
+ //if ServerVersionInt >= 1100 then
+ // obj.AvgRowLen := 10*SIZE_KB;
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+end; }
+
+
+procedure TPGConnection.FetchDbObjects(db: String; var Cache: TDBObjectList);
+var
+ obj: TDBObject;
+ Results: TDBQuery;
+ tp, SchemaTable: String;
+ DataLenClause, IndexLenClause: String;
+begin
+ // Tables, views and procedures
+ Results := nil;
+ try
+ // See http://www.heidisql.com/forum.php?t=16429
+ if ServerVersionInt >= 70300 then
+ SchemaTable := 'QUOTE_IDENT(t.TABLE_SCHEMA) || '+EscapeString('.')+' || QUOTE_IDENT(t.TABLE_NAME)'
+ else
+ SchemaTable := EscapeString(FQuoteChar)+' || t.TABLE_SCHEMA || '+EscapeString(FQuoteChar+'.'+FQuoteChar)+' || t.TABLE_NAME || '+EscapeString(FQuoteChar);
+ // See http://www.heidisql.com/forum.php?t=16996
+ if Parameters.FullTableStatus and (ServerVersionInt >= 90000) then
+ DataLenClause := 'pg_table_size('+SchemaTable+')::bigint'
+ else
+ DataLenClause := 'NULL';
+ // See https://www.heidisql.com/forum.php?t=34635
+ if Parameters.FullTableStatus and (ServerVersionInt >= 80100) then
+ IndexLenClause := 'pg_relation_size('+SchemaTable+')::bigint'
+ else
+ IndexLenClause := 'relpages::bigint * '+SIZE_KB.ToString;
+ Results := GetResults('SELECT *,'+
+ ' '+DataLenClause+' AS data_length,'+
+ ' '+IndexLenClause+' AS index_length,'+
+ ' c.reltuples, obj_description(c.oid) AS comment'+
+ ' FROM '+QuoteIdent(InfSch)+'.'+QuoteIdent('tables')+' AS t'+
+ ' LEFT JOIN '+QuoteIdent('pg_namespace')+' n ON t.table_schema = n.nspname'+
+ ' LEFT JOIN '+QuoteIdent('pg_class')+' c ON n.oid = c.relnamespace AND c.relname=t.table_name'+
+ ' WHERE t.'+QuoteIdent('table_schema')+'='+EscapeString(db) // Use table_schema when using schemata
+ );
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('table_name');
+ obj.Created := 0;
+ obj.Updated := 0;
+ obj.Database := db;
+ obj.Schema := Results.Col('table_schema'); // Remove when using schemata
+ obj.Comment := Results.Col('comment');
+ obj.Rows := StrToInt64Def(Results.Col('reltuples'), obj.Rows);
+ obj.DataLen := StrToInt64Def(Results.Col('data_length'), obj.DataLen);
+ obj.IndexLen := StrToInt64Def(Results.Col('index_length'), obj.IndexLen);
+ obj.Size := obj.DataLen + obj.IndexLen;
+ Inc(Cache.FDataSize, Obj.Size);
+ Cache.FLargestObjectSize := Max(Cache.FLargestObjectSize, Obj.Size);
+ tp := Results.Col('table_type', True);
+ if tp = 'VIEW' then
+ obj.NodeType := lntView
+ else
+ obj.NodeType := lntTable;
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+ // Stored functions. No procedures in PostgreSQL.
+ // See http://dba.stackexchange.com/questions/2357/what-are-the-differences-between-stored-procedures-and-stored-functions
+ try
+ Results := GetResults('SELECT '+QuoteIdent('p')+'.'+QuoteIdent('proname')+', '+QuoteIdent('p')+'.'+QuoteIdent('proargtypes')+' '+
+ 'FROM '+QuoteIdent('pg_catalog')+'.'+QuoteIdent('pg_namespace')+' AS '+QuoteIdent('n')+' '+
+ 'JOIN '+QuoteIdent('pg_catalog')+'.'+QuoteIdent('pg_proc')+' AS '+QuoteIdent('p')+' ON '+QuoteIdent('p')+'.'+QuoteIdent('pronamespace')+' = '+QuoteIdent('n')+'.'+QuoteIdent('oid')+' '+
+ 'WHERE '+QuoteIdent('n')+'.'+QuoteIdent('nspname')+'='+EscapeString(db)
+ );
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('proname');
+ obj.ArgTypes := Results.Col('proargtypes');
+ obj.Database := db;
+ obj.NodeType := lntFunction;
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+
+end;
+
+
+procedure TSQLiteConnection.FetchDbObjects(db: String; var Cache: TDBObjectList);
+var
+ obj: TDBObject;
+ Results: TDBQuery;
+ TypeS: String;
+begin
+ // Tables, views and procedures
+ Results := nil;
+ try
+ Results := GetResults('SELECT * FROM '+QuoteIdent(db)+'.sqlite_master '+
+ 'WHERE type IN('+EscapeString('table')+', '+EscapeString('view')+', '+EscapeString('trigger')+') '+
+ 'AND name NOT LIKE '+EscapeString('sqlite_%'));
+ except
+ on E:EDbError do;
+ end;
+ if Assigned(Results) then begin
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('name');
+ obj.Created := Now;
+ obj.Updated := Now;
+ obj.Database := db;
+ TypeS := Results.Col('type').ToLowerInvariant;
+ if TypeS = 'view' then begin
+ obj.NodeType := lntView;
+ obj.FCreateCode := Results.Col('sql');
+ end else if TypeS = 'trigger' then begin
+ obj.NodeType := lntTrigger;
+ obj.FCreateCode := Results.Col('sql');
+ end else
+ obj.NodeType := lntTable;
+ Results.Next;
+ end;
+ FreeAndNil(Results);
+ end;
+end;
+
+
+{procedure TInterbaseConnection.FetchDbObjects(db: String; var Cache: TDBObjectList);
+var
+ obj: TDBObject;
+ Results: TDBQuery;
+begin
+ // Tables and views
+ Results := nil;
+ try
+ Results := GetResults('SELECT RDB$RELATION_NAME, RDB$DESCRIPTION, RDB$RELATION_TYPE AS '+QuoteIdent('ViewContext') +
+ ' FROM RDB$RELATIONS WHERE RDB$RELATIONS.RDB$SYSTEM_FLAG = 0');
+ try
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col(0);
+ obj.Created := Now;
+ obj.Updated := Now;
+ obj.Database := db;
+ obj.Comment := Results.Col('RDB$DESCRIPTION');
+
+ if Parameters.IsInterbase then
+ begin
+ if Results.Col('ViewContext') = 'PERSISTENT' then
+ obj.NodeType := lntTable
+ else
+ obj.NodeType := lntView;
+ end
+ else
+ begin
+ if Results.Col('ViewContext') = '0' then
+ obj.NodeType := lntTable
+ else
+ obj.NodeType := lntView;
+ end;
+ Results.Next;
+ end;
+ finally
+ FreeAndNil(Results);
+ end;
+ except
+ on E:EDbError do;
+ end;
+
+ // Procedures
+ try
+ Results := GetResults('SELECT RDB$PROCEDURE_NAME, RDB$DESCRIPTION FROM RDB$PROCEDURES WHERE RDB$SYSTEM_FLAG = 0');
+ try
+
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('RDB$PROCEDURE_NAME');
+ obj.Database := db;
+ Obj.NodeType := lntProcedure;
+ obj.Created := Now;
+ obj.Updated := Now;
+ Obj.Comment := Results.Col('RDB$DESCRIPTION');
+ Results.Next;
+ end;
+ finally
+ FreeAndNil(Results);
+ end;
+ except
+ on E:EDbError do;
+ end;
+
+ // Triggers
+ try
+ Results := GetResults('SELECT RDB$TRIGGER_NAME, RDB$DESCRIPTION FROM RDB$TRIGGERS WHERE RDB$SYSTEM_FLAG = 0');
+ try
+
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('RDB$TRIGGER_NAME');
+ obj.Database := db;
+ Obj.NodeType := lntTrigger;
+ obj.Created := Now;
+ obj.Updated := Now;
+ Obj.Comment := Results.Col('RDB$DESCRIPTION');
+ Results.Next;
+ end;
+ finally
+ FreeAndNil(Results);
+ end;
+ except
+ on E:EDbError do;
+ end;
+
+ // Functions
+ try
+ Results := GetResults('SELECT rdb$function_name, RDB$DESCRIPTION FROM rdb$functions WHERE RDB$SYSTEM_FLAG = 0');
+ try
+
+ while not Results.Eof do begin
+ obj := TDBObject.Create(Self);
+ Cache.Add(obj);
+ obj.Name := Results.Col('RDB$function_name');
+ obj.Database := db;
+ Obj.NodeType := lntFunction;
+ obj.Created := Now;
+ obj.Updated := Now;
+ Obj.Comment := Results.Col('RDB$DESCRIPTION');
+ Results.Next;
+ end;
+ finally
+ FreeAndNil(Results);
+ end;
+ except
+ on E:EDbError do;
+ end;
+
+end;}
+
+
+function TDBConnection.GetKeyColumns(Columns: TTableColumnList; Keys: TTableKeyList): TTableColumnList;
+var
+ AllowsNull: Boolean;
+ Key: TTableKey;
+ Col: TTableColumn;
+ ColName: String;
+begin
+ Result := TTableColumnList.Create;
+ // Find best key for updates
+ // 1. round: find a primary key
+ for Key in Keys do begin
+ if Key.IsPrimary then
+ begin
+ for ColName in Key.Columns do begin
+ Col := Columns.FindByName(ColName);
+ if Assigned(Col) then
+ Result.Add(Col);
+ end;
+ end;
+ end;
+ if Result.Count = 0 then begin
+ // no primary key available -> 2. round: find a unique key
+ for Key in Keys do begin
+ if Key.IsUnique then begin
+ // We found a UNIQUE key - better than nothing. Check if one of the key
+ // columns allows NULLs which makes it dangerous to use in UPDATES + DELETES.
+ AllowsNull := False;
+ for ColName in Key.Columns do begin
+ Col := Columns.FindByName(ColName);
+ AllowsNull := Assigned(Col) and Col.AllowNull;
+ if AllowsNull then
+ break; // Unusable, don't use this key
+ end;
+ if not AllowsNull then begin
+ for ColName in Key.Columns do begin
+ Col := Columns.FindByName(ColName);
+ if Assigned(Col) then
+ Result.Add(Col);
+ end;
+ break;
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+function TDBConnection.DecodeAPIString(a: AnsiString): String;
+begin
+ if IsUnicode then
+ Result := Utf8ToString(a)
+ else
+ Result := String(a);
+end;
+
+
+function TDBConnection.ConnectionInfo: TStringList;
+
+ function EvalBool(B: Boolean): String;
+ begin
+ if B then Result := _('Yes') else Result := _('No');
+ end;
+
+begin
+ Log(lcDebug, 'Get connection details ...');
+ Result := TStringList.Create;
+ if Assigned(Parameters) then begin
+ Result.Values[_('Host')] := Parameters.Hostname;
+ Result.Values[_('Network type')] := Parameters.NetTypeName(True);
+ end;
+ Ping(False);
+ Result.Values[_('Connected')] := EvalBool(FActive);
+ if FActive then begin
+ Result.Values[_('Real Hostname')] := FRealHostname;
+ Result.Values[_('Server OS')] := ServerOS;
+ Result.Values[_('Server version')] := FServerVersionUntouched;
+ Result.Values[_('Connection port')] := IntToStr(Parameters.Port);
+ Result.Values[_('Compressed protocol')] := EvalBool(Parameters.Compressed);
+ Result.Values[_('Unicode enabled')] := EvalBool(IsUnicode);
+ Result.Values[_('SSL enabled')] := EvalBool(IsSSL);
+ if Assigned(FSessionVariables) then
+ Result.Values['max_allowed_packet'] := FormatByteNumber(MaxAllowedPacket);
+ end;
+end;
+
+
+function TMySQLConnection.ConnectionInfo: TStringList;
+var
+ Infos, Val: String;
+ rx: TRegExpr;
+begin
+ Result := Inherited;
+ Result.Values[f_('Client version (%s)', [FLib.DllFile])] := DecodeApiString(FLib.mysql_get_client_info);
+ if FActive then begin
+ Infos := DecodeApiString(FLib.mysql_stat(FHandle));
+ rx := TRegExpr.Create;
+ rx.ModifierG := False;
+ rx.Expression := '(\S.*)\:\s+(\S*)(\s+|$)';
+ if rx.Exec(Infos) then while True do begin
+ Val := rx.Match[2];
+ if LowerCase(rx.Match[1]) = 'uptime' then
+ Val := FormatTimeNumber(StrToFloatDef(Val, 0), True)
+ else
+ Val := FormatNumber(Val);
+ Result.Values[_(rx.Match[1])] := Val;
+ if not rx.ExecNext then
+ break;
+ end;
+ rx.Free;
+ end;
+end;
+
+
+{function TAdoDBConnection.ConnectionInfo: TStringList;
+var
+ ConnectionString: String;
+ rx: TRegExpr;
+begin
+ Result := Inherited;
+ if FActive then begin
+ // clear out password
+ ConnectionString := FAdoHandle.ConnectionString;
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ rx.Expression := '(\Wpassword=)([^;]*)';}
+ //ConnectionString := rx.Replace(ConnectionString, '${1}******', True);
+ {rx.Free;
+ Result.Values[_('Connection string')] := ConnectionString;
+ end;
+end; }
+
+
+function TPgConnection.ConnectionInfo: TStringList;
+var
+ v: String;
+ major, minor, build: Integer;
+begin
+ Result := Inherited;
+ v := IntToStr(FLib.PQlibVersion);
+ major := StrToIntDef(Copy(v, 1, Length(v)-4), 0);
+ minor := StrToIntDef(Copy(v, Length(v)-3, 2), 0);
+ build := StrToIntDef(Copy(v, Length(v)-1, 2), 0);
+ Result.Values[f_('Client version (%s)', [FLib.DllFile])] := IntToStr(major) + '.' + IntToStr(minor) + '.' + IntToStr(build);
+end;
+
+
+procedure TDBConnection.ParseViewStructure(CreateCode: String; DBObj: TDBObject;
+ var Algorithm, Definer, SQLSecurity, CheckOption, SelectCode: String);
+var
+ rx: TRegExpr;
+ EscQuote: String;
+begin
+ if CreateCode <> '' then begin
+ // CREATE
+ // [OR REPLACE]
+ // [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
+ // [DEFINER = { user | CURRENT_USER }]
+ // [SQL SECURITY { DEFINER | INVOKER }]
+ // VIEW view_name [(column_list)]
+ // AS select_statement
+ // [WITH [CASCADED | LOCAL] CHECK OPTION]
+ rx := TRegExpr.Create;
+ rx.ModifierG := False;
+ rx.ModifierI := True;
+ EscQuote := QuoteRegExprMetaChars(FQuoteChar);
+ rx.Expression := 'CREATE\s+(OR\s+REPLACE\s+)?'+
+ '(ALGORITHM\s*=\s*(\w*)\s*)?'+
+ '(DEFINER\s*=\s*(\S+|'+EscQuote+'[^@'+EscQuote+']+'+EscQuote+'@'+EscQuote+'[^'+EscQuote+']+'+EscQuote+')\s+)?'+
+ '(SQL\s+SECURITY\s+(\S+)\s+)?'+
+ 'VIEW\s+[^\(]+\s+'+
+ '(\([^\)]+\)\s+)?'+
+ 'AS\s+(.+)(\s+WITH\s+(\w+\s+)?CHECK\s+OPTION\s*)?$';
+ if rx.Exec(CreateCode) then begin
+ Algorithm := rx.Match[3];
+ Definer := DeQuoteIdent(rx.Match[5], '@');
+ SQLSecurity := rx.Match[7];
+ if SQLSecurity.IsEmpty then
+ SQLSecurity := 'DEFINER';
+ CheckOption := Trim(rx.Match[11]);
+ SelectCode := rx.Match[9];
+ end else
+ raise Exception.CreateFmt(_('Regular expression did not match the VIEW code in %s: %s'), ['ParseViewStructure()', CRLF+CRLF+CreateCode]);
+ rx.Free;
+ end;
+
+end;
+
+
+procedure TDBConnection.ParseRoutineStructure(Obj: TDBObject; Parameters: TRoutineParamList);
+var
+ CreateCode, Params, Body, Match: String;
+ ParenthesesCount: Integer;
+ rx: TRegExpr;
+ i: Integer;
+ Param: TRoutineParam;
+ InLiteral: Boolean;
+begin
+ // Parse CREATE code of stored function or procedure to detect parameters
+ rx := TRegExpr.Create;
+ rx.ModifierI := True;
+ rx.ModifierG := True;
+ // CREATE DEFINER=`root`@`localhost` PROCEDURE `bla2`(IN p1 INT, p2 VARCHAR(20))
+ // CREATE DEFINER=`root`@`localhost` FUNCTION `test3`(`?b` varchar(20)) RETURNS tinyint(4)
+ // CREATE DEFINER=`root`@`localhost` PROCEDURE `test3`(IN `Param1` int(1) unsigned)
+ // MSSQL: CREATE FUNCTION dbo.ConvertToInt(@string nvarchar(255), @maxValue int, @defValue int) RETURNS int
+
+ CreateCode := Obj.CreateCode;
+
+ rx.Expression := '\bDEFINER\s*=\s*(\S+)\s';
+ if rx.Exec(CreateCode) then
+ Obj.Definer := DequoteIdent(rx.Match[1], '@')
+ else
+ Obj.Definer := '';
+
+ // Parse parameter list
+ ParenthesesCount := 0;
+ Params := '';
+ InLiteral := False;
+ for i:=1 to Length(CreateCode) do begin
+ if (CreateCode[i] = ')') and (not InLiteral) then begin
+ Dec(ParenthesesCount);
+ if ParenthesesCount = 0 then
+ break;
+ end;
+ if Pos(CreateCode[i], FQuoteChars) > 0 then
+ InLiteral := not InLiteral;
+ if ParenthesesCount >= 1 then
+ Params := Params + CreateCode[i];
+ if (CreateCode[i] = '(') and (not InLiteral) then
+ Inc(ParenthesesCount);
+ end;
+ Params := TSQLBatch.GetSQLWithoutComments(Params);
+
+ // Extract parameters from left part
+ rx.Expression := '(^|,)\s*((IN|OUT|INOUT)\s+)?(\S+)\s+([^\s,\(]+(\([^\)]*\))?[^,]*)';
+ if rx.Exec(Params) then while true do begin
+ Param := TRoutineParam.Create;
+ Param.Context := UpperCase(rx.Match[3]);
+ if Param.Context = '' then
+ Param.Context := 'IN';
+ Param.Name := DeQuoteIdent(rx.Match[4]);
+ Param.Datatype := Trim(rx.Match[5]);
+ Parameters.Add(Param);
+ if not rx.ExecNext then
+ break;
+ end;
+
+ // Right part contains routine body
+ Body := Copy(CreateCode, i+1, Length(CreateCode));
+ // Remove "RETURNS x" and routine characteristics from body
+ // LANGUAGE SQL
+ // | [NOT] DETERMINISTIC
+ // | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
+ // | SQL SECURITY { DEFINER | INVOKER }
+ // | COMMENT 'string'
+ rx.Expression := '^\s*('+
+ 'RETURNS\s+(\S+(\s+UNSIGNED)?(\s+CHARSET\s+\S+)?(\s+COLLATE\s\S+)?)|'+
+ // MySQL function characteristics - see http://dev.mysql.com/doc/refman/5.1/de/create-procedure.html
+ 'LANGUAGE\s+SQL|'+
+ '(NOT\s+)?DETERMINISTIC|'+
+ 'CONTAINS\s+SQL|'+
+ 'NO\s+SQL|'+
+ 'READS\s+SQL\s+DATA|'+
+ 'MODIFIES\s+SQL\s+DATA|'+
+ 'SQL\s+SECURITY\s+(DEFINER|INVOKER)|'+
+ // MS SQL function options - see http://msdn.microsoft.com/en-us/library/ms186755.aspx
+ 'AS|'+
+ 'WITH\s+ENCRYPTION|'+
+ 'WITH\s+SCHEMABINDING|'+
+ 'WITH\s+RETURNS\s+NULL\s+ON\s+NULL\s+INPUT|'+
+ 'WITH\s+CALLED\s+ON\s+NULL\s+INPUT|'+
+ 'WITH\s+EXECUTE_AS_Clause'+
+ ')\s';
+ if rx.Exec(Body) then while true do begin
+ Match := UpperCase(rx.Match[1]);
+ if Pos('RETURNS', Match) = 1 then
+ Obj.Returns := rx.Match[2]
+ else if Pos('DETERMINISTIC', Match) = 1 then
+ Obj.Deterministic := True
+ else if Pos('NOT DETERMINISTIC', Match) = 1 then
+ Obj.Deterministic := False
+ else if (Pos('CONTAINS SQL', Match) = 1) or (Pos('NO SQL', Match) = 1) or (Pos('READS SQL DATA', Match) = 1) or (Pos('MODIFIES SQL DATA', Match) = 1) then
+ Obj.DataAccess := rx.Match[1]
+ else if Pos('SQL SECURITY', Match) = 1 then
+ Obj.Security := rx.Match[7];
+
+
+ Delete(Body, 1, rx.MatchLen[0]);
+ if not rx.Exec(Body) then
+ break;
+ end;
+ Obj.Comment := ExtractLiteral(Body, 'COMMENT');
+ Obj.Body := TrimLeft(Body);
+ rx.Free;
+end;
+
+
+procedure TDBConnection.PurgePrefetchResults;
+begin
+ // Remove cached results
+ if Assigned(FPrefetchResults) then
+ FreeAndNil(FPrefetchResults);
+end;
+
+
+function TDBConnection.ApplyLimitClause(QueryType, QueryBody: String; Limit, Offset: Int64): String;
+begin
+ QueryType := UpperCase(QueryType);
+ Result := QueryType + ' ';
+ case FParameters.NetTypeGroup of
+ ngMSSQL: begin
+ if (QueryType = 'UPDATE') or (QueryType = 'DELETE') then begin
+ // TOP(x) clause for UPDATES + DELETES introduced in MSSQL 2005
+ if ServerVersionInt >= 900 then
+ Result := Result + 'TOP('+IntToStr(Limit)+') ';
+ Result := Result + QueryBody;
+ end else if QueryType = 'SELECT' then begin
+ if ServerVersionInt >= 1100 then begin
+ Result := Result + QueryBody;
+ if not ContainsText(Result, ' ORDER BY ') then
+ Result := Result + ' ORDER BY 1'; // mandatory for using with OFFSET/FETCH
+ Result := Result + ' OFFSET '+Offset.ToString+' ROWS FETCH NEXT '+Limit.ToString+' ROWS ONLY';
+ end else begin
+ // OFFSET not supported in < 2012
+ Result := Result + 'TOP ' + IntToStr(Limit) + ' ' + QueryBody;
+ end;
+ end else
+ Result := Result + QueryBody;
+ end;
+ ngMySQL: begin
+ Result := Result + QueryBody + ' LIMIT ';
+ if Offset > 0 then
+ Result := Result + IntToStr(Offset) + ', ';
+ Result := Result + IntToStr(Limit);
+ end;
+ ngPgSQL: begin
+ if QueryType = 'SELECT' then begin
+ Result := Result + QueryBody + ' LIMIT ' + IntToStr(Limit);
+ if Offset > 0 then
+ Result := Result + ' OFFSET ' + IntToStr(Offset);
+ end else
+ Result := Result + QueryBody;
+ end;
+ ngSQLite: begin
+ // LIMIT supported only in SELECT queries
+ // For UPDATEs and DELETEs only if we would compile sqlite library with SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile flag
+ Result := Result + QueryBody;
+ if Result.StartsWith('SELECT') then begin
+ Result := Result + ' LIMIT ';
+ if Offset > 0 then
+ Result := Result + IntToStr(Offset) + ', ';
+ Result := Result + IntToStr(Limit);
+ end;
+ end;
+ ngInterbase: begin
+ // No support for limit nor offset
+ Result := Result + QueryBody;
+ end;
+ end;
+end;
+
+
+function TDBConnection.LikeClauseTail: String;
+begin
+ case FParameters.NetTypeGroup of
+ ngMSSQL: Result := ' ESCAPE ' + EscapeString('\');
+ else Result := '';
+ end;
+end;
+
+
+
+{ TMySQLQuery }
+
+constructor TDBQuery.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FConnection := AOwner as TDbConnection;
+ FRecNo := -1;
+ FRecordCount := 0;
+ FColumnNames := TStringList.Create;
+ FColumnNames.CaseSensitive := False;
+ FColumnOrgNames := TStringList.Create;
+ FColumnOrgNames.CaseSensitive := False;
+ FStoreResult := True;
+ FDBObject := nil;
+ //FFormatSettings := TFormatSettings.Create('en-US');
+end;
+
+
+constructor TMySQLQuery.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ // suspicous state here - what type has FConnection now?
+ FConnection := AOwner as TMySQLConnection;
+end;
+
+
+constructor TPgQuery.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FConnection := AOwner as TPgConnection;
+end;
+
+
+constructor TSQLiteQuery.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FConnection := AOwner as TSQLiteConnection;
+end;
+
+
+{constructor TInterbaseQuery.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FConnection := AOwner as TInterbaseConnection;
+end;}
+
+
+destructor TDBQuery.Destroy;
+begin
+ FreeAndNil(FColumnNames);
+ FreeAndNil(FColumnOrgNames);
+ FreeAndNil(FColumns);
+ FreeAndNil(FKeys);
+ FreeAndNil(FUpdateData);
+ if FDBObject <> nil then
+ FDBObject.Free;
+ SetLength(FColumnFlags, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnTypes, 0);
+ FSQL := '';
+ FRecordCount := 0;
+ inherited;
+end;
+
+
+destructor TMySQLQuery.Destroy;
+var
+ i: Integer;
+begin
+ if HasResult and (FConnection <> nil) and (FConnection.Active) then begin
+ for i:=Low(FResultList) to High(FResultList) do
+ FConnection.Lib.mysql_free_result(FResultList[i]);
+ end;
+ SetLength(FResultList, 0);
+ inherited;
+end;
+
+
+{destructor TAdoDBQuery.Destroy;
+var
+ i: Integer;
+begin
+ if HasResult and (FConnection <> nil) and (FConnection.Active) then begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FResultList[i].Close;
+ FResultList[i].Free;
+ end;
+ end;
+ SetLength(FResultList, 0);
+ inherited;
+end;}
+
+
+destructor TPGQuery.Destroy;
+var
+ i: Integer;
+begin
+ if HasResult and (FConnection <> nil) and (FConnection.Active) then begin
+ for i:=Low(FResultList) to High(FResultList) do
+ FConnection.Lib.PQclear(FResultList[i]);
+ end;
+ SetLength(FResultList, 0);
+ inherited;
+end;
+
+
+destructor TSQLiteQuery.Destroy;
+var
+ i: Integer;
+begin
+ if HasResult and (FConnection <> nil) and (FConnection.Active) then begin
+ for i:=Low(FResultList) to High(FResultList) do
+ FResultList[i].Free;
+ end;
+ SetLength(FResultList, 0);
+ inherited;
+end;
+
+
+{destructor TInterbaseQuery.Destroy;
+var
+ i: Integer;
+begin
+ if HasResult and (FConnection <> nil) and (FConnection.Active) then begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FResultList[i].Close;
+ FResultList[i].Free;
+ end;
+ end;
+ SetLength(FResultList, 0);
+ inherited;
+end;}
+
+
+procedure TDBQuery.LogMetaInfo(NumResult: Integer);
+var
+ MetaInfo: String;
+begin
+ // Debug log output after DBQuery.Execute with result
+ MetaInfo := 'Result #'+IntToStr(NumResult)+' fetched in ';
+ if Connection.LastQueryDuration < 60*1000 then
+ MetaInfo := MetaInfo + FormatNumber(Connection.LastQueryDuration/1000, 3) +' ' + _('sec.')
+ else
+ MetaInfo := MetaInfo + FormatTimeNumber(Connection.LastQueryDuration/1000, True);
+ if Connection.LastQueryNetworkDuration > 0 then
+ MetaInfo := MetaInfo + ' (+ '+FormatNumber(Connection.LastQueryNetworkDuration/1000, 3) +' ' + _('sec.') + ' ' + _('network') + ')';
+ Connection.Log(lcDebug, MetaInfo);
+end;
+
+procedure TMySQLQuery.Execute(AddResult: Boolean=False; UseRawResult: Integer=-1);
+var
+ i, j, NumFields, NumResults: Integer;
+ Field: PMYSQL_FIELD;
+ IsBinary: Boolean;
+ LastResult: PMYSQL_RES;
+begin
+ // Execute a query, or just take over one of the last result pointers
+ if UseRawResult = -1 then begin
+ Connection.Query(FSQL, FStoreResult);
+ UseRawResult := 0;
+ end;
+ if Connection.ResultCount > UseRawResult then begin
+ LastResult := TMySQLConnection(Connection).LastRawResults[UseRawResult]
+ end else begin
+ LastResult := nil;
+ end;
+ if AddResult and (Length(FResultList) = 0) then
+ AddResult := False;
+ if AddResult then
+ NumResults := Length(FResultList)+1
+ else begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FConnection.Lib.mysql_free_result(FResultList[i]);
+ end;
+ SetLength(FResultList, 0);
+ NumResults := 1;
+ FRecordCount := 0;
+ FAutoIncrementColumn := -1;
+ FEditingPrepared := False;
+ end;
+ if LastResult <> nil then begin
+ LogMetaInfo(NumResults);
+ SetLength(FResultList, NumResults);
+ FResultList[NumResults-1] := LastResult;
+ FRecordCount := FRecordCount + LastResult.row_count;
+ end;
+ if not AddResult then begin
+ if HasResult then begin
+ // FCurrentResults is normally done in SetRecNo, but never if result has no rows
+ FCurrentResults := LastResult;
+ NumFields := FConnection.Lib.mysql_num_fields(LastResult);
+ SetLength(FColumnTypes, NumFields);
+ SetLength(FColumnLengths, NumFields);
+ SetLength(FColumnFlags, NumFields);
+ FColumnNames.Clear;
+ FColumnOrgNames.Clear;
+ for i:=0 to NumFields-1 do begin
+ Field := FConnection.Lib.mysql_fetch_field_direct(LastResult, i);
+ FColumnNames.Add(Connection.DecodeAPIString(Field.name));
+ if Connection.ServerVersionInt >= 40100 then
+ FColumnOrgNames.Add(Connection.DecodeAPIString(Field.org_name))
+ else
+ FColumnOrgNames.Add(Connection.DecodeAPIString(Field.name));
+ FColumnFlags[i] := Field.flags;
+ FColumnTypes[i] := FConnection.Datatypes[0];
+ if (Field.flags and AUTO_INCREMENT_FLAG) = AUTO_INCREMENT_FLAG then
+ FAutoIncrementColumn := i;
+ for j:=0 to High(FConnection.Datatypes) do begin
+ if (Field.flags and ENUM_FLAG) = ENUM_FLAG then begin
+ if FConnection.Datatypes[j].Index = dbdtEnum then
+ FColumnTypes[i] := FConnection.Datatypes[j];
+ end else if (Field.flags and SET_FLAG) = SET_FLAG then begin
+ if FConnection.Datatypes[j].Index = dbdtSet then
+ FColumnTypes[i] := FConnection.Datatypes[j];
+ end else if Field._type = Cardinal(FConnection.Datatypes[j].NativeType) then begin
+ // Text and Blob types share the same constants (see FIELD_TYPEs)
+ // See http://dev.mysql.com/doc/refman/5.7/en/c-api-data-structures.html
+ if Connection.IsUnicode then
+ IsBinary := Field.charsetnr = COLLATION_BINARY
+ else
+ IsBinary := (Field.flags and BINARY_FLAG) = BINARY_FLAG;
+ if IsBinary and (FConnection.Datatypes[j].Category = dtcText) then
+ continue;
+ FColumnTypes[i] := FConnection.Datatypes[j];
+ Break;
+ end;
+ end;
+ FConnection.Log(lcDebug, 'Detected column type for '+FColumnNames[i]+' ('+IntToStr(Field._type)+'): '+FColumnTypes[i].Name);
+ end;
+ FRecNo := -1;
+ First;
+ end else begin
+ SetLength(FColumnTypes, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnFlags, 0);
+ end;
+ end;
+end;
+
+
+{procedure TAdoDBQuery.Execute(AddResult: Boolean=False; UseRawResult: Integer=-1);
+var
+ i, j, NumFields, NumResults: Integer;
+ TypeIndex: TDBDatatypeIndex;
+ LastResult: TAdoQuery;
+begin
+ // TODO: Handle multiple results
+ if UseRawResult = -1 then begin
+ Connection.Query(FSQL, FStoreResult);
+ UseRawResult := 0;
+ end;
+ if Connection.ResultCount > UseRawResult then begin
+ LastResult := TAdoQuery.Create(Self);
+ LastResult.Recordset := TAdoDBConnection(Connection).LastRawResults[UseRawResult];
+ LastResult.Open;
+ end else begin
+ LastResult := nil;
+ end;
+ if AddResult and (Length(FResultList) = 0) then
+ AddResult := False;
+ if AddResult then
+ NumResults := Length(FResultList)+1
+ else begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FResultList[i].Close;
+ FResultList[i].Free;
+ end;
+ NumResults := 1;
+ FRecordCount := 0;
+ FAutoIncrementColumn := -1;
+ FEditingPrepared := False;
+ end;
+ if LastResult <> nil then begin
+ LogMetaInfo(NumResults);
+ SetLength(FResultList, NumResults);
+ FResultList[NumResults-1] := LastResult;
+ FRecordCount := FRecordCount + LastResult.RecordCount;
+ end;
+
+ // Set up columns and data types
+ if not AddResult then begin
+ if HasResult then begin
+ FCurrentResults := LastResult;
+ NumFields := LastResult.FieldCount;
+ SetLength(FColumnTypes, NumFields);
+ SetLength(FColumnLengths, NumFields);
+ SetLength(FColumnFlags, NumFields);
+ FColumnNames.Clear;
+ FColumnOrgNames.Clear;
+ for i:=0 to NumFields-1 do begin
+ FColumnNames.Add(LastResult.Fields[i].FieldName);
+ FColumnOrgNames.Add(FColumnNames[i]); }
+ { ftUnknown, ftString, ftSmallint, ftInteger, ftWord, // 0..4
+ ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, // 5..11
+ ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic, ftFmtMemo, // 12..18
+ ftParadoxOle, ftDBaseOle, ftTypedBinary, ftCursor, ftFixedChar, ftWideString, // 19..24
+ ftLargeint, ftADT, ftArray, ftReference, ftDataSet, ftOraBlob, ftOraClob, // 25..31
+ ftVariant, ftInterface, ftIDispatch, ftGuid, ftTimeStamp, ftFMTBcd, // 32..37
+ ftFixedWideChar, ftWideMemo, ftOraTimeStamp, ftOraInterval, // 38..41
+ ftLongWord, ftShortint, ftByte, ftExtended, ftConnection, ftParams, ftStream, //42..48
+ ftTimeStampOffset, ftObject, ftSingle //49..51 }
+ {case LastResult.Fields[i].DataType of
+ ftSmallint, ftWord:
+ TypeIndex := dbdtMediumInt;
+ ftInteger:
+ TypeIndex := dbdtInt;
+ ftAutoInc: begin
+ TypeIndex := dbdtInt;
+ FAutoIncrementColumn := i;
+ end;
+ ftLargeint:
+ TypeIndex := dbdtBigInt;
+ ftBCD, ftFMTBcd:
+ TypeIndex := dbdtDecimal;
+ ftFixedChar, ftFixedWideChar:
+ TypeIndex := dbdtChar;
+ ftString, ftWideString, ftBoolean, ftGuid:
+ TypeIndex := dbdtVarchar;
+ ftMemo, ftWideMemo:
+ TypeIndex := dbdtText;
+ ftBlob, ftVariant:
+ TypeIndex := dbdtMediumBlob;
+ ftBytes:
+ TypeIndex := dbdtBinary;
+ ftVarBytes:
+ TypeIndex := dbdtVarbinary;
+ ftFloat:
+ TypeIndex := dbdtFloat;
+ ftDate:
+ TypeIndex := dbdtDate;
+ ftTime:
+ TypeIndex := dbdtTime;
+ ftDateTime:
+ TypeIndex := dbdtDateTime;
+ //ftTimeStampOffset: // this is NOT data type DATETIMEOFFSET
+ // TypeIndex := dbdtDatetime;
+ else
+ raise EDbError.CreateFmt(_('Unknown data type for column #%d - %s: %d'), [i, FColumnNames[i], Integer(LastResult.Fields[i].DataType)]);
+ end;
+ for j:=0 to High(FConnection.DataTypes) do begin
+ if TypeIndex = FConnection.DataTypes[j].Index then
+ FColumnTypes[i] := FConnection.DataTypes[j];
+ end;
+
+ end;
+ FRecNo := -1;
+ First;
+ end else begin
+ SetLength(FColumnTypes, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnFlags, 0);
+ end;
+ end;
+end;}
+
+
+procedure TPGQuery.Execute(AddResult: Boolean=False; UseRawResult: Integer=-1);
+var
+ i, NumFields, NumResults: Integer;
+ FieldTypeOID: POid;
+ LastResult: PPGresult;
+begin
+ if UseRawResult = -1 then begin
+ Connection.Query(FSQL, FStoreResult);
+ UseRawResult := 0;
+ end;
+ if Connection.ResultCount > UseRawResult then begin
+ LastResult := TPGConnection(Connection).LastRawResults[UseRawResult]
+ end else begin
+ LastResult := nil;
+ end;
+ if AddResult and (Length(FResultList) = 0) then
+ AddResult := False;
+ if AddResult then
+ NumResults := Length(FResultList)+1
+ else begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FConnection.Lib.PQclear(FResultList[i]);
+ end;
+ NumResults := 1;
+ FRecordCount := 0;
+ FAutoIncrementColumn := -1;
+ FEditingPrepared := False;
+ end;
+ if LastResult <> nil then begin
+ LogMetaInfo(NumResults);
+ SetLength(FResultList, NumResults);
+ FResultList[NumResults-1] := LastResult;
+ FRecordCount := FRecordCount + FConnection.Lib.PQntuples(LastResult);
+ end;
+ if not AddResult then begin
+ if HasResult then begin
+ // FCurrentResults is normally done in SetRecNo, but never if result has no rows
+ FCurrentResults := LastResult;
+ NumFields := FConnection.Lib.PQnfields(LastResult);
+ SetLength(FColumnTypes, NumFields);
+ SetLength(FColumnLengths, NumFields);
+ SetLength(FColumnFlags, NumFields);
+ FColumnNames.Clear;
+ FColumnOrgNames.Clear;
+ for i:=0 to NumFields-1 do begin
+ FColumnNames.Add(Connection.DecodeAPIString(FConnection.Lib.PQfname(LastResult, i)));
+ FColumnOrgNames.Add(FColumnNames[FColumnNames.Count-1]);
+ FieldTypeOID := FConnection.Lib.PQftype(LastResult, i);
+ FColumnTypes[i] := FConnection.GetDatatypeByNativeType(FieldTypeOID, FColumnNames[FColumnNames.Count-1]);
+ end;
+ FRecNo := -1;
+ First;
+ end else begin
+ SetLength(FColumnTypes, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnFlags, 0);
+ end;
+ end;
+end;
+
+
+procedure TSQLiteQuery.Execute(AddResult: Boolean=False; UseRawResult: Integer=-1);
+var
+ i, NumFields, NumResults: Integer;
+ LastResult: TSQLiteGridRows;
+ ColName, ColOrgName, DataTypeStr: String;
+ StepResult: Integer;
+begin
+ if UseRawResult = -1 then begin
+ Connection.Query(FSQL, FStoreResult);
+ UseRawResult := 0;
+ end;
+ if Connection.ResultCount > UseRawResult then begin
+ LastResult := TSQLiteConnection(Connection).LastRawResults[UseRawResult];
+ end else begin
+ LastResult := nil;
+ end;
+ if AddResult and (Length(FResultList) = 0) then
+ AddResult := False;
+ if AddResult then
+ NumResults := Length(FResultList)+1
+ else begin
+ for i:=Length(FResultList)-1 downto 0 do begin
+ FResultList[i].Free;
+ end;
+ NumResults := 1;
+ FRecordCount := 0;
+ FAutoIncrementColumn := -1;
+ FEditingPrepared := False;
+ end;
+ if LastResult <> nil then begin
+ LogMetaInfo(NumResults);
+ SetLength(FResultList, NumResults);
+ FResultList[NumResults-1] := LastResult;
+ FRecordCount := FRecordCount + LastResult.Count;
+ end;
+ if not AddResult then begin
+ if HasResult then begin
+ // FCurrentResults is normally done in SetRecNo, but never if result has no rows
+ FCurrentResults := LastResult;
+ NumFields := FConnection.Lib.sqlite3_column_count(LastResult.Statement);
+ SetLength(FColumnTypes, NumFields);
+ SetLength(FColumnLengths, NumFields);
+ SetLength(FColumnFlags, NumFields);
+ FColumnNames.Clear;
+ FColumnOrgNames.Clear;
+ StepResult := -1;
+ for i:=0 to NumFields-1 do begin
+ ColName := FConnection.DecodeAPIString(FConnection.Lib.sqlite3_column_name(LastResult.Statement, i));
+ FColumnNames.Add(ColName);
+ ColOrgName := FConnection.DecodeAPIString(FConnection.Lib.sqlite3_column_origin_name(LastResult.Statement, i));
+ FColumnOrgNames.Add(ColOrgName);
+ DataTypeStr := FConnection.DecodeAPIString(FConnection.Lib.sqlite3_column_decltype(LastResult.Statement, i));
+ if DataTypeStr.IsEmpty then begin
+ if StepResult = -1 then
+ StepResult := FConnection.Lib.sqlite3_step(LastResult.Statement);
+ if StepResult = SQLITE_ROW then begin
+ case FConnection.Lib.sqlite3_column_type(LastResult.Statement, i) of
+ SQLITE_INTEGER: DataTypeStr := 'INTEGER';
+ SQLITE_FLOAT: DataTypeStr := 'FLOAT';
+ SQLITE_BLOB: DataTypeStr := 'BLOB';
+ SQLITE3_TEXT: DataTypeStr := 'TEXT';
+ // SQLITE_NULL gets "unknown"
+ end;
+ end else begin
+ // No row available, fall back to TEXT
+ DataTypeStr := 'TEXT';
+ end;
+ end;
+ FColumnTypes[i] := FConnection.GetDatatypeByName(DataTypeStr, False);
+ end;
+ if StepResult <> -1 then begin
+ FConnection.Lib.sqlite3_reset(LastResult.Statement);
+ end;
+ FRecNo := -1;
+ First;
+ end else begin
+ SetLength(FColumnTypes, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnFlags, 0);
+ end;
+ end;
+end;
+
+
+{procedure TInterbaseQuery.Execute(AddResult: Boolean; UseRawResult: Integer);
+var
+ i, j, NumFields, NumResults: Integer;
+ TypeIndex: TDBDatatypeIndex;
+ LastResult: TFDQuery;
+begin
+ if UseRawResult = -1 then begin
+ Connection.Query(FSQL, FStoreResult);
+ UseRawResult := 0;
+ end;
+ if Connection.ResultCount > UseRawResult then begin
+ LastResult := TInterbaseConnection(Connection).LastRawResults[UseRawResult]
+ end else begin
+ LastResult := nil;
+ end;
+ if AddResult and (Length(FResultList) = 0) then
+ AddResult := False;
+ if AddResult then
+ NumResults := Length(FResultList)+1
+ else begin
+ for i:=Low(FResultList) to High(FResultList) do begin
+ FResultList[i].Free;
+ end;
+ NumResults := 1;
+ FRecordCount := 0;
+ FAutoIncrementColumn := -1;
+ FEditingPrepared := False;
+ end;
+ if LastResult <> nil then begin
+ LogMetaInfo(NumResults);
+ SetLength(FResultList, NumResults);
+ FResultList[NumResults-1] := LastResult;
+ FRecordCount := FRecordCount + LastResult.RecordCount;
+ end;
+ if not AddResult then begin
+ if HasResult then begin
+ // FCurrentResults is normally done in SetRecNo, but never if result has no rows
+ FCurrentResults := LastResult;
+ NumFields := LastResult.FieldCount;
+ SetLength(FColumnTypes, NumFields);
+ SetLength(FColumnLengths, NumFields);
+ SetLength(FColumnFlags, NumFields);
+ FColumnNames.Clear;
+ FColumnOrgNames.Clear;
+ for i:=0 to NumFields-1 do begin
+ FColumnNames.Add(LastResult.Fields[i].FieldName);
+ FColumnOrgNames.Add(FColumnNames[FColumnNames.Count-1]);
+ case LastResult.Fields[i].DataType of
+ ftSmallint, ftWord:
+ TypeIndex := dbdtMediumInt;
+ ftInteger:
+ TypeIndex := dbdtInt;
+ ftAutoInc: begin
+ TypeIndex := dbdtInt;
+ FAutoIncrementColumn := i;
+ end;
+ ftLargeint:
+ TypeIndex := dbdtBigInt;
+ ftBCD, ftFMTBcd:
+ TypeIndex := dbdtDecimal;
+ ftFixedChar, ftFixedWideChar:
+ TypeIndex := dbdtChar;
+ ftString, ftWideString, ftBoolean, ftGuid:
+ TypeIndex := dbdtVarchar;
+ ftMemo, ftWideMemo:
+ TypeIndex := dbdtText;
+ ftBlob, ftVariant:
+ TypeIndex := dbdtMediumBlob;
+ ftBytes:
+ TypeIndex := dbdtBinary;
+ ftVarBytes:
+ TypeIndex := dbdtVarbinary;
+ ftFloat, ftSingle:
+ TypeIndex := dbdtFloat;
+ ftDate:
+ TypeIndex := dbdtDate;
+ ftTime:
+ TypeIndex := dbdtTime;
+ ftDateTime, ftTimeStamp:
+ TypeIndex := dbdtDateTime;
+ else
+ raise EDbError.CreateFmt(_('Unknown data type for column #%d - %s: %d'), [i, FColumnNames[i], Integer(LastResult.Fields[i].DataType)]);
+ end;
+ for j:=0 to High(FConnection.DataTypes) do begin
+ if TypeIndex = FConnection.DataTypes[j].Index then
+ FColumnTypes[i] := FConnection.DataTypes[j];
+ end;
+ end;
+ FRecNo := -1;
+ First;
+ end else begin
+ SetLength(FColumnTypes, 0);
+ SetLength(FColumnLengths, 0);
+ SetLength(FColumnFlags, 0);
+ end;
+ end;
+end;}
+
+
+procedure TDBQuery.SetColumnOrgNames(Value: TStringList);
+begin
+ // Retrieve original column names from caller
+ FColumnOrgNames.Text := Value.Text;
+end;
+
+
+procedure TDBQuery.SetDBObject(Value: TDBObject);
+begin
+ // Assign values from outside to a new tdbobject
+ FDBObject := TDBObject.Create(FConnection);
+ FDBObject.Assign(Value);
+end;
+
+
+procedure TDBQuery.First;
+begin
+ RecNo := 0;
+end;
+
+
+procedure TDBQuery.Next;
+begin
+ RecNo := RecNo + 1;
+end;
+
+
+procedure TMySQLQuery.SetRecNo(Value: Int64);
+var
+ LengthsPointer: PMYSQL_LENGTHS;
+ i, j: Integer;
+ NumRows, WantedLocalRecNo: Int64;
+ Row: TGridRow;
+ RowFound: Boolean;
+begin
+ if Value = FRecNo then
+ Exit;
+ if (not FEditingPrepared) and (Value >= RecordCount) then begin
+ FRecNo := RecordCount;
+ FEof := True;
+ end else begin
+
+ // Find row in edited data
+ RowFound := False;
+ if FEditingPrepared then begin
+ for Row in FUpdateData do begin
+ if Row.RecNo = Value then begin
+ FCurrentRow := nil;
+ FCurrentUpdateRow := Row;
+ for i:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ RowFound := True;
+ break;
+ end;
+ end;
+ end;
+
+ // Row not edited data - find it in normal result
+ if not RowFound then begin
+ NumRows := 0;
+ for i:=Low(FResultList) to High(FResultList) do begin
+ Inc(NumRows, FResultList[i].row_count);
+ if NumRows > Value then begin
+ FCurrentResults := FResultList[i];
+ // Do not seek if FCurrentRow points to the previous row of the wanted row
+ WantedLocalRecNo := FCurrentResults.row_count-(NumRows-Value);
+ if (WantedLocalRecNo = 0) or (FRecNo+1 <> Value) or (FCurrentRow = nil) then
+ FConnection.Lib.mysql_data_seek(FCurrentResults, WantedLocalRecNo);
+ FCurrentRow := FConnection.Lib.mysql_fetch_row(FCurrentResults);
+ FCurrentUpdateRow := nil;
+ // Remember length of column contents. Important for Col() so contents of cells with #0 chars are not cut off
+ LengthsPointer := FConnection.Lib.mysql_fetch_lengths(FCurrentResults);
+ for j:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[j] := LengthsPointer^[j];
+ break;
+ end;
+ end;
+ end;
+
+ FRecNo := Value;
+ FEof := False;
+ end;
+end;
+
+
+{procedure TAdoDBQuery.SetRecNo(Value: Int64);
+var
+ i, j: Integer;
+ RowFound: Boolean;
+ Row: TGridRow;
+ NumRows, WantedLocalRecNo: Int64;
+begin
+ if Value = FRecNo then
+ Exit;
+ if (not FEditingPrepared) and (Value >= RecordCount) then begin
+ FRecNo := RecordCount;
+ FEof := True;
+ FCurrentResults.Last;
+ end else begin
+
+ // Find row in edited data
+ RowFound := False;
+ if FEditingPrepared then begin
+ for Row in FUpdateData do begin
+ if Row.RecNo = Value then begin
+ FCurrentUpdateRow := Row;
+ for i:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ RowFound := True;
+ break;
+ end;
+ end;
+ end;
+
+ // Row not edited data - find it in normal result
+ if not RowFound then begin
+ NumRows := 0;
+ try
+ for i:=Low(FResultList) to High(FResultList) do begin
+ Inc(NumRows, FResultList[i].RecordCount);
+ if NumRows > Value then begin
+ FCurrentResults := FResultList[i];
+ WantedLocalRecNo := FCurrentResults.RecordCount-(NumRows-Value);
+ FCurrentResults.RecNo := WantedLocalRecNo+1;
+ FCurrentUpdateRow := nil;
+ for j:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[j] := FCurrentResults.Fields[j].DataSize;
+ break;
+ end;
+ end;
+ except
+ // Catch broken connection
+ on E:EOleException do begin
+ FConnection.Active := False;
+ FConnection.Log(lcError, E.Message);
+ end;
+ end;
+ end;
+
+ FRecNo := Value;
+ FEof := False;
+ end;
+end;}
+
+
+procedure TPGQuery.SetRecNo(Value: Int64);
+var
+ i, j: Integer;
+ RowFound: Boolean;
+ Row: TGridRow;
+ NumRows: Int64;
+begin
+ if Value = FRecNo then
+ Exit;
+ if (not FEditingPrepared) and (Value >= RecordCount) then begin
+ FRecNo := RecordCount;
+ FEof := True;
+ end else begin
+
+ // Find row in edited data
+ RowFound := False;
+ if FEditingPrepared then begin
+ for Row in FUpdateData do begin
+ if Row.RecNo = Value then begin
+ FCurrentUpdateRow := Row;
+ for i:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ RowFound := True;
+ break;
+ end;
+ end;
+ end;
+
+ // Row not edited data - find it in normal result
+ if not RowFound then begin
+ NumRows := 0;
+ for i:=Low(FResultList) to High(FResultList) do begin
+ Inc(NumRows, FConnection.Lib.PQntuples(FResultList[i]));
+ if NumRows > Value then begin
+ FCurrentResults := FResultList[i];
+ FRecNoLocal := FConnection.Lib.PQntuples(FCurrentResults)-(NumRows-Value);
+ FCurrentUpdateRow := nil;
+ for j:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[j] := FConnection.Lib.PQgetlength(FCurrentResults, FRecNoLocal, j);
+ break;
+ end;
+ end;
+ end;
+
+ FRecNo := Value;
+ FEof := False;
+ end;
+end;
+
+
+procedure TSQLiteQuery.SetRecNo(Value: Int64);
+var
+ i: Integer;
+ RowFound: Boolean;
+ Row: TGridRow;
+ NumRows: Int64;
+begin
+ if Value = FRecNo then
+ Exit;
+ if (not FEditingPrepared) and (Value >= RecordCount) then begin
+ FRecNo := RecordCount;
+ FEof := True;
+ end else begin
+
+ // Find row in edited data
+ RowFound := False;
+ if FEditingPrepared then begin
+ for Row in FUpdateData do begin
+ if Row.RecNo = Value then begin
+ FCurrentUpdateRow := Row;
+ for i:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ RowFound := True;
+ break;
+ end;
+ end;
+ end;
+
+ // Row not edited data - find it in normal result
+ if not RowFound then begin
+ NumRows := 0;
+ for i:=Low(FResultList) to High(FResultList) do begin
+ Inc(NumRows, FResultList[i].Count);
+ if NumRows > Value then begin
+ FCurrentResults := FResultList[i];
+ FRecNoLocal := FResultList[i].Count-(NumRows-Value);
+ FCurrentUpdateRow := nil;
+ //for j:=Low(FColumnLengths) to High(FColumnLengths) do
+ // FColumnLengths[j] := FConnection.Lib.PQgetlength(FCurrentResults, FRecNoLocal, j);
+ break;
+ end;
+ end;
+ end;
+
+ FRecNo := Value;
+ FEof := False;
+ end;
+end;
+
+
+{procedure TInterbaseQuery.SetRecNo(Value: Int64);
+var
+ i, j: Integer;
+ RowFound: Boolean;
+ Row: TGridRow;
+ NumRows, WantedLocalRecNo: Int64;
+begin
+ if Value = FRecNo then
+ Exit;
+ if (not FEditingPrepared) and (Value >= RecordCount) then begin
+ FRecNo := RecordCount;
+ FEof := True;
+ FCurrentResults.Last;
+ end else begin
+
+ // Find row in edited data
+ RowFound := False;
+ if FEditingPrepared then begin
+ for Row in FUpdateData do begin
+ if Row.RecNo = Value then begin
+ FCurrentUpdateRow := Row;
+ for i:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ RowFound := True;
+ break;
+ end;
+ end;
+ end;
+
+ // Row not edited data - find it in normal result
+ if not RowFound then begin
+ NumRows := 0;
+ try
+ for i:=Low(FResultList) to High(FResultList) do begin
+ Inc(NumRows, FResultList[i].RecordCount);
+ if NumRows > Value then begin
+ FCurrentResults := FResultList[i];
+ WantedLocalRecNo := FCurrentResults.RecordCount-(NumRows-Value);
+ FCurrentResults.RecNo := WantedLocalRecNo+1;
+ FCurrentUpdateRow := nil;
+ for j:=Low(FColumnLengths) to High(FColumnLengths) do
+ FColumnLengths[j] := FCurrentResults.Fields[j].DataSize;
+ break;
+ end;
+ end;
+ except
+ // Catch broken connection
+ raise;
+ end;
+ end;
+
+ FRecNo := Value;
+ FEof := False;
+ end;
+end;}
+
+
+function TDBQuery.ColumnCount: Integer;
+begin
+ if Assigned(FColumnNames) then
+ Result := FColumnNames.Count
+ else
+ Result := -1;
+end;
+
+
+function TDBQuery.ColumnExists(Column: Integer): Boolean;
+begin
+ // Check if given column exists in current row
+ // Prevents crash when cancelling new row insertion
+ Result := FConnection.Active and (Column > -1) and (Column < ColumnCount);
+ if Result and FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ Result := FCurrentUpdateRow.Count > Column;
+ end;
+end;
+
+
+function TDBQuery.ColumnExists(ColumnName: String): Boolean;
+begin
+ Result := FConnection.Active and (ColumnNames.IndexOf(ColumnName) > -1);
+end;
+
+
+function TDBQuery.GetColBinData(Column: Integer; var baData: TBytes): Boolean;
+begin
+ Raise EDbError.Create(SNotImplemented);
+end;
+
+
+function TMySQLQuery.GetColBinData(Column: Integer; var baData: TBytes): Boolean;
+var
+ AnsiStr: AnsiString;
+begin
+ Result := False;
+
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ // Row was edited and only valid in a TGridRow
+ AnsiStr := AnsiString(FCurrentUpdateRow[Column].NewText);
+ end else begin
+ // The normal case: Fetch cell from mysql result
+ SetString(AnsiStr, FCurrentRow[Column], FColumnLengths[Column]);
+ end;
+
+ if Datatype(Column).Category in [dtcBinary, dtcSpatial] then begin
+ SetLength(baData, Length(AnsiStr));
+ CopyMemory(baData, @AnsiStr[1], Length(AnsiStr));
+ Result := True;
+ end;
+ end;
+end;
+
+
+function TMySQLQuery.Col(Column: Integer; IgnoreErrors: Boolean=False): String;
+var
+ AnsiStr: AnsiString;
+ BitString: String;
+ NumBit: Integer;
+ ByteVal: Byte;
+ c: Char;
+ Field: PMYSQL_FIELD;
+begin
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ // Row was edited and only valid in a TGridRow
+ Result := FCurrentUpdateRow[Column].NewText;
+ end else begin
+ // The normal case: Fetch cell from mysql result
+ SetString(AnsiStr, FCurrentRow[Column], FColumnLengths[Column]);
+ if Datatype(Column).Category in [dtcBinary, dtcSpatial] then
+ Result := String(AnsiStr)
+ else
+ Result := Connection.DecodeAPIString(AnsiStr);
+ // Create string bitmask for BIT fields
+ if Datatype(Column).Index = dbdtBit then begin
+ Field := FConnection.Lib.mysql_fetch_field_direct(FCurrentResults, column);
+ // FConnection.Log(lcInfo, Field.name+': def: '+field.def+' length: '+inttostr(field.length)+' max_length: '+inttostr(field.max_length)+' decimals: '+inttostr(field.decimals));
+ for c in Result do begin
+ ByteVal := Byte(c);
+ BitString := '';
+ for NumBit:=0 to 7 do begin
+ if (ByteVal shr NumBit and $1) = $1 then
+ BitString := BitString + '1'
+ else
+ BitString := BitString + '0';
+ if Length(BitString) >= Field.length then
+ break;
+ end;
+ if Length(BitString) >= Field.length then
+ break;
+ end;
+ Result := ReverseString(BitString);
+ end;
+
+ end;
+ end else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_(MsgInvalidColumn), [Column, ColumnCount, RecordCount]);
+end;
+
+
+{function TAdoDBQuery.Col(Column: Integer; IgnoreErrors: Boolean=False): String;
+begin
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ Result := FCurrentUpdateRow[Column].NewText;
+ end else begin
+ try
+ case Datatype(Column).Category of
+ dtcReal:
+ Result := FloatToStr(FCurrentResults.Fields[Column].AsExtended, FFormatSettings);
+ dtcTemporal:
+ Result := FormatDateTime(Datatype(Column).Format, FCurrentResults.Fields[Column].AsFloat);
+ else
+ Result := FCurrentResults.Fields[Column].AsString;
+ end;
+ except
+ Result := String(FCurrentResults.Fields[Column].AsAnsiString);
+ end;
+ if Datatype(Column).Index = dbdtBit then begin
+ if UpperCase(Result) = 'TRUE' then
+ Result := '1'
+ else
+ Result := '0';
+ end
+ end;
+ end else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_(MsgInvalidColumn), [Column, ColumnCount, RecordCount]);
+end;}
+
+
+function TPGQuery.Col(Column: Integer; IgnoreErrors: Boolean=False): String;
+var
+ AnsiStr: AnsiString;
+begin
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ Result := FCurrentUpdateRow[Column].NewText;
+ end else begin
+ SetString(AnsiStr, FConnection.Lib.PQgetvalue(FCurrentResults, FRecNoLocal, Column), FColumnLengths[Column]);
+ if Datatype(Column).Category in [dtcBinary, dtcSpatial] then
+ Result := String(AnsiStr)
+ else if Datatype(Column).Index = dbdtBool then
+ if AnsiStr='t' then Result := 'true' else Result := 'false'
+ else
+ Result := Connection.DecodeAPIString(AnsiStr);
+ end;
+ end else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_(MsgInvalidColumn), [Column, ColumnCount, RecordCount]);
+end;
+
+
+function TSQLiteQuery.Col(Column: Integer; IgnoreErrors: Boolean=False): String;
+begin
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ Result := FCurrentUpdateRow[Column].NewText;
+ end else begin
+ Result := FCurrentResults[FRecNoLocal][Column].OldText;
+ end;
+ end else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_(MsgInvalidColumn), [Column, ColumnCount, RecordCount]);
+end;
+
+
+{function TInterbaseQuery.Col(Column: Integer; IgnoreErrors: Boolean): String;
+begin
+ if ColumnExists(Column) then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ Result := FCurrentUpdateRow[Column].NewText;
+ end else begin
+ Result := FCurrentResults.Fields[Column].AsString;
+ end;
+ end else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_(MsgInvalidColumn), [Column, ColumnCount, RecordCount]);
+end;}
+
+
+function TDBQuery.Col(ColumnName: String; IgnoreErrors: Boolean=False): String;
+var
+ idx: Integer;
+begin
+ // ColumnNames is case insensitive, so we can select wrong cased columns in MariaDB 10.4
+ // See #599
+ idx := ColumnNames.IndexOf(ColumnName);
+ if idx > -1 then
+ Result := Col(idx)
+ else if not IgnoreErrors then
+ Raise EDbError.CreateFmt(_('Column "%s" not available.'), [ColumnName]);
+end;
+
+
+function TDBQuery.ColumnLengths(Column: Integer): Int64;
+begin
+ Result := FColumnLengths[Column];
+end;
+
+
+function TDBQuery.HexValue(Column: Integer; IgnoreErrors: Boolean=False): String;
+var
+ baData: TBytes;
+begin
+ // Return a binary column value as hex AnsiString
+ if FConnection.Parameters.IsAnyMysql then begin
+ GetColBinData(Column, baData);
+ Result := FConnection.EscapeBin(baData);
+ end else
+ Result := FConnection.EscapeBin(Col(Column, IgnoreErrors));
+end;
+
+
+function TDBQuery.DataType(Column: Integer): TDBDataType;
+var
+ Col: TTableColumn;
+begin
+ Col := ColAttributes(Column);
+ if Assigned(Col) then
+ Result := Col.DataType
+ else
+ Result := FColumnTypes[Column];
+end;
+
+
+function TDBQuery.MaxLength(Column: Integer): Int64;
+var
+ ColAttr: TTableColumn;
+begin
+ // Return maximum posible length of values in given columns
+ // Note: PMYSQL_FIELD.max_length holds the maximum existing value in that column, which is useless here
+ Result := MaxInt;
+ ColAttr := ColAttributes(Column);
+ if Assigned(ColAttr) then begin
+ case ColAttr.DataType.Index of
+ dbdtChar, dbdtVarchar, dbdtBinary, dbdtVarBinary, dbdtBit: Result := MakeInt(ColAttr.LengthSet);
+ dbdtTinyText, dbdtTinyBlob: Result := 255;
+ dbdtText, dbdtBlob: begin
+ case FConnection.Parameters.NetTypeGroup of
+ ngMySQL: Result := 65535;
+ ngMSSQL: Result := MaxInt;
+ ngPgSQL: Result := High(Int64);
+ end;
+ end;
+ dbdtMediumText, dbdtMediumBlob: Result := 16777215;
+ dbdtLongText, dbdtLongBlob: Result := 4294967295;
+ end;
+ end;
+end;
+
+
+function TDBQuery.ValueList(Column: Integer): TStringList;
+var
+ ColAttr: TTableColumn;
+begin
+ Result := TStringList.Create;
+ Result.QuoteChar := '''';
+ Result.Delimiter := ',';
+ ColAttr := ColAttributes(Column);
+ if Assigned(ColAttr) then case ColAttr.DataType.Index of
+ dbdtEnum, dbdtSet:
+ Result.DelimitedText := ColAttr.LengthSet;
+ dbdtBool:
+ Result.DelimitedText := 'true,false';
+ end;
+end;
+
+
+function TDBQuery.ColAttributes(Column: Integer): TTableColumn;
+var
+ i: Integer;
+begin
+ Result := nil;
+ if (Column < 0) or (Column >= FColumnOrgNames.Count) then
+ raise EDbError.CreateFmt(_('Column #%s not available.'), [IntToStr(Column)]);
+ if FColumns <> nil then begin
+ for i:=0 to FColumns.Count-1 do begin
+ if FColumns[i].Name = FColumnOrgNames[Column] then begin
+ Result := FColumns[i];
+ break;
+ end;
+ end;
+ end;
+end;
+
+
+function TDBQuery.ColExists(Column: String): Boolean;
+begin
+ Result := (ColumnNames <> nil) and (ColumnNames.IndexOf(Column) > -1);
+end;
+
+
+function TMySQLQuery.ColIsPrimaryKeyPart(Column: Integer): Boolean;
+begin
+ Result := (FColumnFlags[Column] and PRI_KEY_FLAG) = PRI_KEY_FLAG;
+end;
+
+
+{function TAdoDBQuery.ColIsPrimaryKeyPart(Column: Integer): Boolean;
+begin
+// Result := FCurrentResults.Fields[0].KeyFields
+ Result := False;
+end;}
+
+
+function TPGQuery.ColIsPrimaryKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+
+function TSQLiteQuery.ColIsPrimaryKeyPart(Column: Integer): Boolean;
+var
+ MetaResult: Integer;
+ TableNm, ColumnNm: String;
+ DataType, CollSeq: PAnsiChar;
+ NotNull, PrimaryKey, Autoinc: Integer;
+begin
+ Result := False;
+ TableNm := TableName(Column);
+ ColumnNm := FColumnOrgNames[Column];
+ if not TableNm.IsEmpty then begin
+ MetaResult := FConnection.Lib.sqlite3_table_column_metadata(FConnection.FHandle,
+ PAnsiChar(Utf8Encode(FConnection.Database)),
+ PAnsiChar(Utf8Encode(TableNm)),
+ PAnsiChar(Utf8Encode(ColumnNm)),
+ DataType, CollSeq, NotNull, PrimaryKey, Autoinc
+ );
+ if MetaResult <> SQLITE_ERROR then begin
+ Result := PrimaryKey.ToBoolean;
+ end;
+ end;
+end;
+
+
+{function TInterbaseQuery.ColIsPrimaryKeyPart(Column: Integer): Boolean;
+begin
+ // Todo
+ Result := False;
+end; }
+
+
+function TMySQLQuery.ColIsUniqueKeyPart(Column: Integer): Boolean;
+begin
+ Result := (FColumnFlags[Column] and UNIQUE_KEY_FLAG) = UNIQUE_KEY_FLAG;
+end;
+
+
+{function TAdoDBQuery.ColIsUniqueKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;}
+
+
+function TPGQuery.ColIsUniqueKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+
+function TSQLiteQuery.ColIsUniqueKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+
+{function TInterbaseQuery.ColIsUniqueKeyPart(Column: Integer): Boolean;
+begin
+ // Todo
+ Result := False;
+end;}
+
+
+function TMySQLQuery.ColIsKeyPart(Column: Integer): Boolean;
+begin
+ Result := (FColumnFlags[Column] and MULTIPLE_KEY_FLAG) = MULTIPLE_KEY_FLAG;
+end;
+
+
+{function TAdoDbQuery.ColIsKeyPart(Column: Integer): Boolean;
+begin
+ Result := FCurrentResults.Fields[Column].IsIndexField;
+end;}
+
+
+function TPGQuery.ColIsKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+
+function TSQLiteQuery.ColIsKeyPart(Column: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+
+{function TInterbaseQuery.ColIsKeyPart(Column: Integer): Boolean;
+begin
+ // Todo
+ Result := False;
+end;}
+
+
+function TDBQuery.ColIsVirtual(Column: Integer): Boolean;
+var
+ Col: TTableColumn;
+begin
+ Result := False;
+ Col := ColAttributes(Column);
+ if Col <> nil then begin
+ Result := not Col.Virtuality.IsEmpty;
+ end;
+end;
+
+
+function TMySQLQuery.IsNull(Column: Integer): Boolean;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsNull
+ else
+ Result := FCurrentRow[Column] = nil;
+end;
+
+
+function TDBQuery.IsNull(Column: String): Boolean;
+var
+ i, idx: Integer;
+begin
+ idx := -1;
+ for i:=0 to FColumnNames.Count-1 do begin
+ if CompareText(Column, FColumnNames[i]) = 0 then begin
+ idx := i;
+ break;
+ end;
+ end;
+ if idx > -1 then
+ Result := IsNull(idx)
+ else
+ Result := True;
+end;
+
+
+{function TAdoDBQuery.IsNull(Column: Integer): Boolean;
+begin
+ Result := False;
+ // Catch broken connection
+ if FConnection.Active then begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsNull
+ else begin
+ try
+ Result := FCurrentResults.Fields[Column].IsNull;
+ except
+ // Silence error: "Multiple-step operation generated errors. Check each status value."
+ // @see #496
+ //on E:EOleException do;
+ // Silence more: see #1724
+ end;
+ end;
+ end;
+end;}
+
+
+function TPGQuery.IsNull(Column: Integer): Boolean;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsNull
+ else
+ Result := FConnection.Lib.PQgetisnull(FCurrentResults, FRecNoLocal, Column) = 1;
+end;
+
+
+function TSQLiteQuery.IsNull(Column: Integer): Boolean;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsNull
+ else
+ Result := FCurrentResults[FRecNoLocal][Column].OldIsNull;
+end;
+
+
+{function TInterbaseQuery.IsNull(Column: Integer): Boolean;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsNull
+ else
+ Result := FCurrentResults.Fields[Column].IsNull;
+end;}
+
+
+function TDBQuery.IsFunction(Column: Integer): Boolean;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow[Column].NewIsFunction
+ else
+ Result := False;
+end;
+
+
+function TMySQLQuery.HasResult: Boolean;
+begin
+ Result := Length(FResultList) > 0;
+end;
+
+
+{function TAdoDBQuery.HasResult: Boolean;
+begin
+ Result := Length(FResultList) > 0;
+end;}
+
+
+function TPGQuery.HasResult: Boolean;
+begin
+ Result := Length(FResultList) > 0;
+end;
+
+
+function TSQLiteQuery.HasResult: Boolean;
+begin
+ Result := Length(FResultList) > 0;
+end;
+
+
+{function TInterbaseQuery.HasResult: Boolean;
+begin
+ Result := Length(FResultList) > 0;
+end;}
+
+
+procedure TDBQuery.PrepareColumnAttributes;
+var
+ DB: String;
+ DBObjects: TDBObjectList;
+ LObj, Obj: TDBObject;
+begin
+ // Try to fetch column names and keys
+ // This is probably a VIEW, so column names need to be fetched differently
+ Obj := nil;
+ if FDBObject <> nil then
+ Obj := FDBObject
+ else begin
+ DB := DatabaseName;
+ if DB = '' then
+ DB := Connection.Database;
+ DBObjects := Connection.GetDBObjects(DB);
+ for LObj in DBObjects do begin
+ if (LObj.NodeType in [lntTable, lntView]) and Connection.IdentifierEquals(LObj.Name, TableName) then begin
+ Obj := LObj;
+ break;
+ end;
+ end;
+ if Obj = nil then
+ raise EDbError.Create(f_('Could not find table or view %s.%s. Please refresh database tree.', [DB, TableName]));
+ end;
+ // Obj.NodeType must be lntTable or lntView here, otherwise we get no columns or keys
+ FColumns := Obj.TableColumns;
+ FKeys := Obj.TableKeys;
+ FForeignKeys := Obj.TableForeignKeys;
+end;
+
+
+procedure TDBQuery.PrepareEditing;
+begin
+ // Try to fetch column names and keys and init update data
+ if FEditingPrepared then
+ Exit;
+ PrepareColumnAttributes;
+ FreeAndNil(FUpdateData);
+ FUpdateData := TGridRows.Create;
+ FEditingPrepared := True;
+end;
+
+
+procedure TDBQuery.DeleteRow;
+var
+ sql: String;
+ IsVirtual: Boolean;
+ TempRowsAffected: Int64;
+begin
+ // Delete current row from result
+ PrepareEditing;
+ IsVirtual := Assigned(FCurrentUpdateRow) and FCurrentUpdateRow.Inserted;
+ if not IsVirtual then begin
+ sql := GridQuery('DELETE', 'FROM ' + QuotedDbAndTableName + ' WHERE ' + GetWhereClause);
+ Connection.Query(sql);
+ TempRowsAffected := Connection.RowsAffected;
+ Connection.ShowWarnings;
+ if TempRowsAffected = 0 then
+ raise EDbError.Create(FormatNumber(TempRowsAffected)+' rows deleted when that should have been 1.');
+ end;
+ if Assigned(FCurrentUpdateRow) then begin
+ FUpdateData.Remove(FCurrentUpdateRow);
+ FCurrentUpdateRow := nil;
+ FRecNo := -1;
+ end;
+end;
+
+
+function TDBQuery.InsertRow: Int64;
+var
+ Row, OtherRow: TGridRow;
+ c: TGridValue;
+ i: Integer;
+ ColAttr: TTableColumn;
+ InUse: Boolean;
+begin
+ // Add new row and return row number
+ PrepareEditing;
+ Row := TGridRow.Create(True);
+ for i:=0 to ColumnCount-1 do begin
+ c := TGridValue.Create;
+ Row.Add(c);
+ c.OldText := '';
+ c.OldIsFunction := False;
+ c.OldIsNull := True;
+ ColAttr := ColAttributes(i);
+ if Assigned(ColAttr) then begin
+ case ColAttr.DefaultType of
+ cdtText: begin
+ c.OldText := FConnection.UnescapeString(ColAttr.DefaultText);
+ c.OldIsNull := False;
+ end;
+ cdtExpression: begin
+ // Overtake expression, if it's a simple integer
+ if ColAttr.DefaultText = MakeInt(ColAttr.DefaultText).ToString then begin
+ c.OldText := ColAttr.DefaultText;
+ c.OldIsNull := False;
+ end;
+ end;
+ end;
+
+ end;
+ c.NewText := c.OldText;
+ c.NewIsFunction := c.OldIsFunction;
+ c.NewIsNull := c.OldIsNull;
+ c.Modified := False;
+ end;
+ Row.Inserted := True;
+ // Find highest unused recno of inserted rows and use that for this row
+ // Important: do not raise higher than what TVirtualStringTree.RootNodeCount can hold!
+ Result := High(Cardinal);
+ while True do begin
+ InUse := False;
+ for OtherRow in FUpdateData do begin
+ InUse := OtherRow.RecNo = Result;
+ if InUse then break;
+ end;
+ if not InUse then break;
+ Dec(Result);
+ end;
+ Row.RecNo := Result;
+ FUpdateData.Add(Row);
+end;
+
+
+procedure TDBQuery.SetCol(Column: Integer; NewText: String; Null: Boolean; IsFunction: Boolean);
+begin
+ PrepareEditing;
+ if not Assigned(FCurrentUpdateRow) then begin
+ CreateUpdateRow;
+ EnsureFullRow(False);
+ end;
+ FCurrentUpdateRow[Column].NewIsNull := Null;
+ FCurrentUpdateRow[Column].NewIsFunction := IsFunction;
+ FCurrentUpdateRow[Column].NewText := IfThen(Null, '', NewText);
+ FCurrentUpdateRow[Column].Modified := (FCurrentUpdateRow[Column].NewText <> FCurrentUpdateRow[Column].OldText) or
+ (FCurrentUpdateRow[Column].NewIsNull <> FCurrentUpdateRow[Column].OldIsNull) or
+ (FCurrentUpdateRow[Column].NewIsFunction <> FCurrentUpdateRow[Column].OldIsFunction)
+ ;
+ // TODO: check if column allows NULL, otherwise force .Modified
+end;
+
+
+procedure TDBQuery.CreateUpdateRow;
+var
+ i: Integer;
+ c: TGridValue;
+ Row: TGridRow;
+begin
+ Row := TGridRow.Create(True);
+ for i:=0 to ColumnCount-1 do begin
+ c := TGridValue.Create;
+ Row.Add(c);
+ c.OldText := Col(i);
+ c.NewText := c.OldText;
+ c.OldIsNull := IsNull(i);
+ c.NewIsNull := c.OldIsNull;
+ c.OldIsFunction := False;
+ c.NewIsFunction := c.OldIsFunction;
+ c.Modified := False;
+ end;
+ Row.Inserted := False;
+ Row.RecNo := RecNo;
+ FCurrentUpdateRow := Row;
+ FUpdateData.Add(FCurrentUpdateRow);
+end;
+
+
+function TDBQuery.EnsureFullRow(Refresh: Boolean): Boolean;
+var
+ i: Integer;
+ sql: String;
+ Data: TDBQuery;
+begin
+ // Load full column values
+ Result := True;
+ if Refresh or (not HasFullData) then try
+ PrepareEditing;
+ for i:=0 to FColumnOrgNames.Count-1 do begin
+ if sql <> '' then
+ sql := sql + ', ';
+ sql := sql + Connection.QuoteIdent(FColumnOrgNames[i]);
+ end;
+ sql := sql + ' FROM '+QuotedDbAndTableName+' WHERE '+GetWhereClause;
+ sql := GridQuery('SELECT', sql);
+ Data := Connection.GetResults(sql);
+ Connection.ShowWarnings;
+ Result := Data.RecordCount = 1;
+ if Result then begin
+ if not Assigned(FCurrentUpdateRow) then
+ CreateUpdateRow;
+ for i:=0 to Data.ColumnCount-1 do begin
+ FCurrentUpdateRow[i].OldText := Data.Col(i);
+ FCurrentUpdateRow[i].NewText := FCurrentUpdateRow[i].OldText;
+ FCurrentUpdateRow[i].OldIsNull := Data.IsNull(i);
+ FCurrentUpdateRow[i].NewIsNull := FCurrentUpdateRow[i].OldIsNull;
+ FCurrentUpdateRow[i].OldIsFunction := False;
+ FCurrentUpdateRow[i].NewIsFunction := FCurrentUpdateRow[i].OldIsFunction;
+ FColumnLengths[i] := Length(FCurrentUpdateRow[i].NewText);
+ end;
+ Data.Free;
+ end;
+ except on E:EDbError do
+ Result := False;
+ end;
+end;
+
+
+// Issue #1351 and https://www.heidisql.com/forum.php?t=39239
+// Data view editor truncated for TEXT columns when emoji is present
+// Issue #1658: Saving BLOB to file creates corrupted files
+// Issue #1673: Truncated text in Postgres mode
+function TDBQuery.HasFullData: Boolean;
+var
+ i: Integer;
+ NumChars: Integer;
+begin
+ Result := True;
+ if Assigned(FCurrentUpdateRow) then begin
+ // In case we created a update-row we know for sure that we already loaded full contents
+ Result := True;
+ end
+ else begin
+ // This is done only once, before EnsureFullRow creates an update-row which returns true above.
+ // Delphi's Length() likely counts characters different than SQL/LEFT().
+ for i:=0 to ColumnCount-1 do begin
+ if not DataType(i).LoadPart then
+ Continue;
+ NumChars := Col(i).Length;
+ {if TableName.Contains('issue') then
+ FConnection.Log(lcInfo, 'HasFullData: RowNum:'+RecNo.ToString+
+ ' ColumnNames['+i.ToString+']:'+ColumnNames[i]+
+ ' ColumnOrgNames['+i.ToString+']:'+ColumnOrgNames[i]+
+ ' NumChars:'+NumChars.ToString+
+ ' ColumnLengths('+i.ToString+'):'+ColumnLengths(i).ToString
+ );}
+ if ColumnNames[i].StartsWith('LEFT', True) or ColumnNames[i].StartsWith('SUBSTR', True) then begin
+ // This works at least in MySQL, and fixes issue #1850 where NumChars is > 256 when text contains emojis.
+ // MSSQL does not provide the original column names with function calls like LEFT(..)
+ Result := False;
+ Break;
+ end;
+ if (NumChars <= GRIDMAXDATA) and (NumChars >= GRIDMAXDATA / SizeOf(Char)) then begin
+ Result := False;
+ Break;
+ end;
+ end;
+ end;
+end;
+
+
+function TDBQuery.SaveModifications: Boolean;
+var
+ i: Integer;
+ TempRowsAffected: Int64;
+ Row: TGridRow;
+ Cell: TGridValue;
+ sqlUpdate, sqlInsertColumns, sqlInsertValues, Val: String;
+ RowModified: Boolean;
+ ColAttr: TTableColumn;
+begin
+ Result := True;
+ if not FEditingPrepared then
+ raise EDbError.Create(_('Internal error: Cannot post modifications before editing was prepared.'));
+
+ for Row in FUpdateData do begin
+ // Prepare update and insert queries
+ RecNo := Row.RecNo;
+ sqlUpdate := '';
+ sqlInsertColumns := '';
+ sqlInsertValues := '';
+ RowModified := False;
+ for i:=0 to ColumnCount-1 do begin
+ Cell := Row[i];
+ if not Cell.Modified then
+ continue;
+ RowModified := True;
+ if sqlUpdate <> '' then begin
+ sqlUpdate := sqlUpdate + ', ';
+ sqlInsertColumns := sqlInsertColumns + ', ';
+ sqlInsertValues := sqlInsertValues + ', ';
+ end;
+ if Cell.NewIsNull then
+ Val := 'NULL'
+ else if Cell.NewIsFunction then
+ Val := Cell.NewText
+ else case Datatype(i).Category of
+ dtcInteger, dtcReal: begin
+ Val := Connection.EscapeString(Cell.NewText, Datatype(i));
+ if (Datatype(i).Index = dbdtBit) and FConnection.Parameters.IsAnyMySQL then
+ Val := 'b' + Val;
+ end;
+ dtcBinary, dtcSpatial:
+ Val := FConnection.EscapeBin(Cell.NewText);
+ else begin
+ if Datatype(i).Index in [dbdtNchar, dbdtNvarchar, dbdtNtext] then
+ Val := 'N' + Connection.EscapeString(Cell.NewText)
+ else if Datatype(i).Category = dtcTemporal then
+ Val := Connection.EscapeString(Connection.GetDateTimeValue(Cell.NewText, Datatype(i).Index))
+ else
+ Val := Connection.EscapeString(Cell.NewText);
+ end;
+ end;
+ sqlUpdate := sqlUpdate + Connection.QuoteIdent(FColumnOrgNames[i]) + '=' + Val;
+ sqlInsertColumns := sqlInsertColumns + Connection.QuoteIdent(FColumnOrgNames[i]);
+ sqlInsertValues := sqlInsertValues + Val;
+ end;
+
+ // Post query and fetch just inserted auto-increment id if applicable
+ if RowModified then try
+ if Row.Inserted then begin
+ Connection.Query('INSERT INTO '+QuotedDbAndTableName+' ('+sqlInsertColumns+') VALUES ('+sqlInsertValues+')');
+ Connection.ShowWarnings;
+ for i:=0 to ColumnCount-1 do begin
+ ColAttr := ColAttributes(i);
+ if Assigned(ColAttr) and (ColAttr.DefaultType = cdtAutoInc) then begin
+ Row[i].NewText := UnformatNumber(Row[i].NewText);
+ if Row[i].NewText = '0' then
+ Row[i].NewText := Connection.GetVar('SELECT ' + Connection.GetSQLSpecifity(spFuncLastAutoIncNumber));
+ Row[i].NewIsNull := False;
+ break;
+ end;
+ end;
+ end else begin
+ sqlUpdate := QuotedDbAndTableName+' SET '+sqlUpdate+' WHERE '+GetWhereClause;
+ sqlUpdate := GridQuery('UPDATE', sqlUpdate);
+ Connection.Query(sqlUpdate);
+ TempRowsAffected := Connection.RowsAffected;
+ Connection.ShowWarnings;
+ if TempRowsAffected = 0 then begin
+ raise EDbError.Create(FormatNumber(TempRowsAffected)+' rows updated when that should have been 1.');
+ Result := False;
+ end;
+ end;
+ // Reset modification flags
+ for i:=0 to ColumnCount-1 do begin
+ Cell := Row[i];
+ Cell.OldText := Cell.NewText;
+ Cell.OldIsNull := Cell.NewIsNull;
+ Cell.OldIsFunction := False;
+ Cell.NewIsFunction := False;
+ Cell.Modified := False;
+ end;
+ Row.Inserted := False;
+ // Reload real row data from server if keys allow that
+ EnsureFullRow(True);
+ except
+ on E:EDbError do begin
+ Result := False;
+ ErrorDialog(E.Message);
+ end;
+ end;
+
+ end;
+end;
+
+
+procedure TDBQuery.DiscardModifications;
+var
+ x: Integer;
+ c: TGridValue;
+begin
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then begin
+ if FCurrentUpdateRow.Inserted then begin
+ FUpdateData.Remove(FCurrentUpdateRow);
+ FRecNo := -1;
+ end else for x:=0 to FCurrentUpdateRow.Count-1 do begin
+ c := FCurrentUpdateRow[x];
+ c.NewText := c.OldText;
+ c.NewIsNull := c.OldIsNull;
+ c.NewIsFunction := c.OldIsFunction;
+ c.Modified := False;
+ end;
+ end;
+end;
+
+
+function TDBQuery.Modified(Column: Integer): Boolean;
+begin
+ Result := False;
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then try
+ Result := FCurrentUpdateRow[Column].Modified;
+ except
+ connection.Log(lcdebug, inttostr(column));
+ raise;
+ end;
+end;
+
+
+function TDBQuery.Modified: Boolean;
+var
+ x, y: Integer;
+begin
+ Result := False;
+ if FEditingPrepared then for y:=0 to FUpdateData.Count-1 do begin
+ for x:=0 to FUpdateData[y].Count-1 do begin
+ Result := FUpdateData[y][x].Modified;
+ if Result then
+ break;
+ end;
+ if Result then
+ break;
+ end;
+end;
+
+
+function TDBQuery.Inserted: Boolean;
+begin
+ // Check if current row was inserted and not yet posted to the server
+ Result := False;
+ if FEditingPrepared and Assigned(FCurrentUpdateRow) then
+ Result := FCurrentUpdateRow.Inserted;
+end;
+
+
+function TMySQLQuery.DatabaseName: String;
+var
+ Field: PMYSQL_FIELD;
+ i: Integer;
+begin
+ // Find and return name of database of current query
+ if FDBObject <> nil then begin
+ Result := FDBObject.Database;
+ end else begin
+ // Return first available Field.db property, or just the current database as fallback.
+ // For a view in db1 selecting from db2, this returns db2, which triggers errors in GetCreateViewCode!
+ for i:=0 to ColumnCount-1 do begin
+ Field := FConnection.Lib.mysql_fetch_field_direct(FCurrentResults, i);
+ if Field.db <> '' then begin
+ Result := Connection.DecodeAPIString(Field.db);
+ break;
+ end;
+ end;
+ if Result = '' then
+ Result := Connection.Database;
+ end;
+end;
+
+
+{function TAdoDBQuery.DatabaseName: String;
+begin
+ Result := Connection.Database;
+end;}
+
+
+function TPGQuery.DatabaseName: String;
+begin
+ // TODO
+ Result := Connection.Database;
+end;
+
+
+function TSQLiteQuery.DatabaseName: String;
+begin
+ // TODO
+ Result := Connection.Database;
+end;
+
+
+{function TInterbaseQuery.DatabaseName: String;
+begin
+ // Todo
+ Result := Connection.Database;
+end;}
+
+
+function TDBQuery.TableName: String;
+var
+ i: Integer;
+ NextTable: String;
+ rx: TRegExpr;
+begin
+ // Get table name from a result set
+ Result := '';
+ for i:=0 to ColumnCount-1 do begin
+ NextTable := TableName(i);
+ if (not Result.IsEmpty) and (not NextTable.IsEmpty) and (Result <> NextTable) then
+ raise EDbError.Create(_('More than one table involved.'));
+ if not NextTable.IsEmpty then
+ Result := NextTable;
+ end;
+ {if Result.IsEmpty then begin
+ // Untested with joins, compute columns and views
+ Result := GetTableNameFromSQLEx(SQL, idMixCase);
+ rx := TRegExpr.Create;
+ rx.Expression := '\.([^\.]+)$';
+ if rx.Exec(Result) then
+ Result := rx.Match[1];
+ rx.Free;
+ if Result.IsEmpty then
+ raise EDbError.Create('Could not determine name of table.');
+ end;}
+end;
+
+
+function TMySQLQuery.TableName(Column: Integer): String;
+var
+ Field: PMYSQL_FIELD;
+ FieldDb, FieldTable, FieldOrgTable: String;
+ Objects: TDBObjectList;
+ Obj: TDBObject;
+begin
+ Field := FConnection.Lib.mysql_fetch_field_direct(FCurrentResults, Column);
+ FieldDb := FConnection.DecodeAPIString(Field.db);
+ FieldTable := FConnection.DecodeAPIString(Field.table);
+ FieldOrgTable := FConnection.DecodeAPIString(Field.org_table);
+ // Connection.Log(lcInfo, FColumnNames[Column]+': org_table:'+FieldOrgTable+' table:'+FieldTable);
+
+ if FieldTable <> FieldOrgTable then begin
+ // Probably a VIEW, in which case we rely on the first column's table name.
+ // TODO: This is unsafe when joining a view with a table/view.
+ if FieldDb <> '' then begin
+ Objects := Connection.GetDBObjects(FieldDb);
+ for Obj in Objects do begin
+ if (Obj.Name = FieldTable) and (Obj.NodeType = lntView) then begin
+ Result := FieldTable;
+ break;
+ end;
+ end;
+ end;
+ end;
+
+ if Result.IsEmpty then begin
+ // Normal table column
+ // Note: this is empty on data tab TEXT columns with LEFT(..) clause
+ Result := FieldOrgTable;
+ StripNewLines(Result);
+ end;
+end;
+
+
+{function TAdoDBQuery.TableName(Column: Integer): String;
+begin
+ Result := '';
+end;}
+
+function TPGQuery.TableName(Column: Integer): String;
+var
+ TableOid: POid;
+begin
+ // Get table name from a result set
+ // "123::regclass" results are quoted if they contain special characters
+ TableOid := FConnection.Lib.PQftable(FCurrentResults, Column);
+ if TableOid = InvalidOid then begin
+ // 0 => not a simple reference to a table column, e.g. on SUBSTRING(col, 1, 256)
+ Result := EmptyStr;
+ end
+ else if FConnection.RegClasses.ContainsKey(TableOid) then begin
+ FConnection.RegClasses.TryGetValue(TableOid, Result);
+ end else begin
+ Result := FConnection.GetVar('SELECT '+IntToStr(TableOid)+'::regclass');
+ Result := FConnection.DeQuoteIdent(Result);
+ FConnection.RegClasses.Add(TableOid, Result);
+ end;
+end;
+
+
+function TSQLiteQuery.TableName(Column: Integer): String;
+var
+ tblA: AnsiString;
+begin
+ Result := EmptyStr;
+ tblA := FConnection.Lib.sqlite3_column_table_name(FCurrentResults.Statement, Column);
+ Result := FConnection.DecodeAPIString(tblA);
+end;
+
+
+{function TInterbaseQuery.TableName(Column: Integer): String;
+begin
+ // Todo
+end;}
+
+
+function TDBQuery.QuotedDbAndTableName: String;
+begin
+ // Prefer TDBObject when quoting as it knows its schema
+ if FDBObject <> nil then
+ Result := FDBObject.QuotedDbAndTableName
+ else
+ Result := FConnection.QuotedDbAndTableName(DatabaseName, TableName);
+end;
+
+
+function TDBQuery.ResultName: String;
+begin
+ // Return name of query defined in a comment above the actual query
+ Result := RegExprGetMatch('--\s+name\:\s*([^\r\n]+)', FSQL, 1, False, True);
+ Result := Trim(Result);
+end;
+
+function TDBQuery.GetKeyColumns: TTableColumnList;
+var
+ i: Integer;
+begin
+ // Return key column names, or all column names if no good key present
+ PrepareEditing;
+ Result := Connection.GetKeyColumns(FColumns, FKeys);
+ if Result.Count = 0 then begin
+ // No good key found. Just expect all columns to be present.
+ for i:=0 to FColumns.Count-1 do
+ Result.Add(FColumns[i]);
+ end;
+end;
+
+
+procedure TDBQuery.CheckEditable;
+var
+ i: Integer;
+ KeyCols: TTableColumnList;
+begin
+ KeyCols := GetKeyColumns;
+ if KeyCols.Count = 0 then
+ raise EDbError.Create(_(MSG_NOGRIDEDITING));
+ // All column names must be present in order to send valid INSERT/UPDATE/DELETE queries
+ for i:=0 to KeyCols.Count-1 do begin
+ if FColumnOrgNames.IndexOf(KeyCols[i].Name) = -1 then
+ raise EDbError.Create(_(MSG_NOGRIDEDITING));
+ end;
+ for i:=0 to FColumnOrgNames.Count-1 do begin
+ if FColumnOrgNames[i] = '' then
+ raise EDbError.CreateFmt(_('Column #%d has an undefined origin: %s'), [i, ColumnNames[i]]);
+ end;
+end;
+
+function TDBQuery.IsEditable: Boolean;
+begin
+ try
+ CheckEditable;
+ Result := True;
+ except
+ on E:EDbError do begin
+ FConnection.Log(lcInfo, E.Message);
+ Result := False;
+ end;
+ end;
+end;
+
+
+function TDBQuery.GetWhereClause: String;
+var
+ i, j: Integer;
+ NeededCols: TTableColumnList;
+ ColVal: String;
+ ColIsNull: Boolean;
+begin
+ // Compose WHERE clause including values from best key for editing
+ NeededCols := GetKeyColumns;
+ Result := '';
+
+ for i:=0 to NeededCols.Count-1 do begin
+ j := FColumnOrgNames.IndexOf(NeededCols[i].Name);
+ if j = -1 then
+ raise EDbError.CreateFmt(_('Cannot compose WHERE clause - column missing: %s'), [NeededCols[i]]);
+ if Result <> '' then
+ Result := Result + ' AND';
+ // See issue #769 and #2031 for why we need CastAsText
+ Result := Result + ' ' + NeededCols[i].CastAsText;
+
+ if Modified(j) then begin
+ ColVal := FCurrentUpdateRow[j].OldText;
+ ColIsNull := FCurrentUpdateRow[j].OldIsNull;
+ end else begin
+ ColVal := Col(j);
+ ColIsNull := IsNull(j);
+ end;
+
+ if ColIsNull then
+ Result := Result + ' IS NULL'
+ else begin
+ case DataType(j).Category of
+ dtcInteger, dtcReal: begin
+ if DataType(j).Index = dbdtBit then
+ Result := Result + '=b' + Connection.EscapeString(ColVal)
+ else begin
+ // Guess (!) the default value silently inserted by the server. This is likely
+ // to be incomplete in cases where a UNIQUE key allows NULL here
+ if ColVal='' then
+ ColVal := '0';
+ Result := Result + '=' + ColVal;
+ end;
+ end;
+ dtcTemporal:
+ Result := Result + '=' + Connection.EscapeString(Connection.GetDateTimeValue(ColVal, DataType(j).Index));
+ dtcBinary, dtcSpatial:
+ Result := Result + '=' + FConnection.EscapeBin(ColVal);
+ else begin
+ // Any other data type goes here, including text:
+ case DataType(j).Index of
+ // Support international characters with N-prefix on MSSQL, see #1115:
+ dbdtNchar, dbdtNvarchar, dbdtNtext:
+ Result := Result + '=N' + Connection.EscapeString(ColVal);
+ else
+ Result := Result + '=' + Connection.EscapeString(ColVal);
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+
+function TDBQuery.GridQuery(QueryType, QueryBody: String): String;
+var
+ KeyColumns: TTableColumnList;
+begin
+ // Return automatic grid UPDATE/DELETE/SELECT, and apply LIMIT clause if no good key is present
+ KeyColumns := Connection.GetKeyColumns(FColumns, FKeys);
+ if KeyColumns.Count > 0 then
+ Result := QueryType + ' ' + QueryBody
+ else
+ Result := Connection.ApplyLimitClause(QueryType, QueryBody, 1, 0);
+end;
+
+
+
+{ TGridValue }
+
+destructor TGridValue.Destroy;
+begin
+ NewText := '';
+ OldText := '';
+ inherited;
+end;
+
+
+{ TSQLiteGridRows }
+
+constructor TSQLiteGridRows.Create(AOwner: TSQLiteConnection);
+begin
+ inherited Create;
+ FConnection := AOwner;
+end;
+
+destructor TSQLiteGridRows.Destroy;
+begin
+ try
+ if Statement <> nil then
+ FConnection.Lib.sqlite3_finalize(Statement);
+ except
+ on E:Exception do;
+ end;
+ inherited;
+end;
+
+
+
+
+{ TDBObjectComparer }
+
+function TDBObjectComparer.Compare(constref Left, Right: TDBObject): Integer;
+begin
+ // Simple sort method for a TDBObjectList
+ Result := CompareAnyNode(Left.Schema+'.'+Left.Name, Right.Schema+'.'+Right.Name);
+end;
+
+
+function TDBObjectDropComparer.Compare(constref Left, Right: TDBObject): Integer;
+begin
+ // Sorting a TDBObject items so that dropping them does not trap in SQL errors
+ if (Left.NodeType = lntTrigger) and (Right.NodeType <> lntTrigger) then
+ Result := -1
+ else if (Left.NodeType <> lntTrigger) and (Right.NodeType = lntTrigger) then
+ Result := 1
+ else if (Left.NodeType = lntView) and (Right.NodeType <> lntView) then
+ Result := -1
+ else if (Left.NodeType <> lntView) and (Right.NodeType = lntView) then
+ Result := 1
+ else
+ Result := 0;
+end;
+
+
+
+{ TDBObject }
+
+constructor TDBObject.Create(OwnerConnection: TDBConnection);
+begin
+ Name := '';
+ Schema := '';
+ Database := '';
+ Column := '';
+ Engine := '';
+ Comment := '';
+ RowFormat := '';
+ CreateOptions := '';
+ Collation := '';
+ Created := 0;
+ Updated := 0;
+ LastChecked := 0;
+ Rows := -1;
+ Size := -1;
+ Version := -1;
+ AvgRowLen := -1;
+ MaxDataLen := -1;
+ IndexLen := -1;
+ DataLen := -1;
+ DataFree := -1;
+ AutoInc := -1;
+ CheckSum := -1;
+ Body := '';
+ Definer := '';
+ Returns := '';
+ DataAccess := '';
+ Security := '';
+ ArgTypes := '';
+ Deterministic := False;
+ RowsAreExact := False;
+ NodeType := lntNone;
+ GroupType := lntNone;
+ FCreateCode := '';
+ FCreateCodeLoaded := False;
+ FWasSelected := False;
+ FConnection := OwnerConnection;
+end;
+
+
+procedure TDBObject.Assign(Source: TPersistent);
+var
+ s: TDBObject;
+begin
+ if Source is TDBObject then begin
+ s := Source as TDBObject;
+ Name := s.Name;
+ Schema := s.Schema;
+ Database := s.Database;
+ Column := s.Column;
+ Engine := s.Engine;
+ Comment := s.Comment;
+ RowFormat := s.RowFormat;
+ CreateOptions := s.CreateOptions;
+ Collation := s.Collation;
+ Created := s.Created;
+ Updated := s.Updated;
+ LastChecked := s.LastChecked;
+ Rows := s.Rows;
+ Size := s.Size;
+ Version := s.Version;
+ AvgRowLen := s.AvgRowLen;
+ MaxDataLen := s.MaxDataLen;
+ IndexLen := s.IndexLen;
+ DataLen := s.DataLen;
+ DataFree := s.DataFree;
+ AutoInc := s.AutoInc;
+ CheckSum := s.CheckSum;
+ Body := s.Body;
+ Definer := s.Definer;
+ Returns := s.Returns;
+ DataAccess := s.DataAccess;
+ Security := s.Security;
+ ArgTypes := s.ArgTypes;
+ Deterministic := s.Deterministic;
+ RowsAreExact := s.RowsAreExact;
+ NodeType := s.NodeType;
+ GroupType := s.GroupType;
+ FCreateCode := s.FCreateCode;
+ FCreateCodeLoaded := s.FCreateCodeLoaded;
+ FWasSelected := s.FWasSelected;
+ end else
+ inherited;
+end;
+
+
+procedure TDBObject.UnloadDetails;
+begin
+ if FConnection.FColumnCache.ContainsKey(QuotedDbAndTableName) then
+ FConnection.FColumnCache.Remove(QuotedDbAndTableName);
+ if FConnection.FKeyCache.ContainsKey(QuotedDbAndTableName) then
+ FConnection.FKeyCache.Remove(QuotedDbAndTableName);
+ if FConnection.FForeignKeyCache.ContainsKey(QuotedDbAndTableName) then
+ FConnection.FForeignKeyCache.Remove(QuotedDbAndTableName);
+ if FConnection.FCheckConstraintCache.ContainsKey(QuotedDbAndTableName) then
+ FConnection.FCheckConstraintCache.Remove(QuotedDbAndTableName);
+ FCreateCode := '';
+ FCreateCodeLoaded := False;
+end;
+
+
+
+function TDBObject.IsSameAs(CompareTo: TDBObject): Boolean;
+begin
+ if (not Assigned(CompareTo)) or (CompareTo = nil) then begin
+ Result := False;
+ end else begin
+ try
+ Result := FConnection.IdentifierEquals(Name, CompareTo.Name)
+ and (NodeType = CompareTo.NodeType)
+ and (Database = CompareTo.Database)
+ and (Schema = CompareTo.Schema)
+ and (Column = CompareTo.Column)
+ and (ArgTypes = CompareTo.ArgTypes)
+ and (Connection = CompareTo.Connection);
+ except
+ // No reproduction recipe yet, but numerous crashes from above were reported
+ on E:EAccessViolation do
+ Result := False;
+ end;
+ end;
+end;
+
+
+function TDBObject.GetObjType: String;
+begin
+ case NodeType of
+ lntTable: Result := 'Table';
+ lntView: Result := 'View';
+ lntFunction: Result := 'Function';
+ lntProcedure: Result := 'Procedure';
+ lntTrigger: Result := 'Trigger';
+ lntEvent: Result := 'Event';
+ lntColumn: Result := 'Column';
+ else Result := _('Unknown, should never appear');
+ end;
+end;
+
+function TDBObject.GetImageIndex: Integer;
+begin
+ // Detect key icon index for specified db object (table, trigger, ...)
+ Result := -1;
+ case NodeType of
+ lntNone: begin
+ // Prevent AV with no connection. Parameters may not have been initialized as well
+ if FConnection <> nil then try
+ Result := FConnection.Parameters.ImageIndex
+ except
+ on E:EAccessViolation do
+ Result := -1;
+ end;
+ end;
+
+ lntDb: Result := ICONINDEX_DB;
+
+ lntGroup: begin
+ case GroupType of
+ lntTable: Result := ICONINDEX_TABLE;
+ lntFunction: Result := ICONINDEX_STOREDFUNCTION;
+ lntProcedure: Result := ICONINDEX_STOREDPROCEDURE;
+ lntView: Result := ICONINDEX_VIEW;
+ lntTrigger: Result := ICONINDEX_TRIGGER;
+ lntEvent: Result := ICONINDEX_EVENT;
+ else Result := -1;
+ end;
+ end;
+
+ lntTable: Result := ICONINDEX_TABLE;
+ lntFunction: Result := ICONINDEX_STOREDFUNCTION;
+ lntProcedure: Result := ICONINDEX_STOREDPROCEDURE;
+ lntView: Result := ICONINDEX_VIEW;
+ lntTrigger: Result := ICONINDEX_TRIGGER;
+ lntEvent: Result := ICONINDEX_EVENT;
+
+ lntColumn: Result := ICONINDEX_FIELD;
+ end;
+end;
+
+
+function TDBObject.GetOverlayImageIndex: Integer;
+var
+ EngineUpper: String;
+begin
+ // Detect small overlay icon index for specified table engine
+ Result := -1;
+ case NodeType of
+ lntNone: begin
+ if not Connection.Active then
+ Result := 158;
+ end;
+
+ lntDb: begin
+ if Database = Connection.Database then
+ Result := ICONINDEX_HIGHLIGHTMARKER;
+ end;
+
+ lntTable: begin
+ EngineUpper := UpperCase(Engine);
+ if EngineUpper = 'FEDERATED' then
+ Result := 177
+ else if EngineUpper = 'MEMORY' then
+ Result := 178
+ else if EngineUpper = 'ARIA' then
+ Result := 179
+ else if EngineUpper = 'CSV' then
+ Result := 180
+ else if EngineUpper = 'PERFORMANCE_SCHEMA' then
+ Result := 181
+ else if EngineUpper = 'BLACKHOLE' then
+ Result := 167
+ else if EngineUpper = 'MRG_MYISAM' then
+ Result := 182;
+ end;
+
+ end;
+end;
+
+
+function TDBObject.GetPath: String;
+begin
+ Result := Database + DELIM + Schema + DELIM + Name;
+end;
+
+
+function TDBObject.GetCreateCode: String;
+begin
+ if not FCreateCodeLoaded then try
+ FCreateCode := Connection.GetCreateCode(Self);
+ FCreateCodeLoaded := True;
+ except on E:Exception do
+ Connection.Log(lcError, E.Message);
+ end;
+ Result := FCreateCode;
+end;
+
+function TDBObject.GetCreateCode(RemoveAutoInc, RemoveDefiner: Boolean): String;
+
+ procedure RemovePattern(RegExp: String);
+ var
+ rx: TRegExpr;
+ begin
+ // Remove first occurrence of pattern from result
+ rx := TRegExpr.Create;
+ rx.Expression := RegExp;
+ rx.ModifierI := True;
+ if rx.Exec(Result) then begin
+ Delete(Result, rx.MatchPos[0], rx.MatchLen[0]-1);
+ end;
+ rx.Free;
+ end;
+begin
+ Result := GetCreateCode;
+
+ if RemoveAutoInc then begin
+ // Remove AUTO_INCREMENT clause
+ RemovePattern('\sAUTO_INCREMENT\s*\=\s*\d+\s');
+ end;
+
+ if RemoveDefiner then begin
+ // Remove DEFINER clause
+ RemovePattern('\sDEFINER\s*\=\s*\S+\s');
+ end;
+
+end;
+
+function TDBObject.QuotedDatabase(AlwaysQuote: Boolean=True): String;
+begin
+ if FConnection.Parameters.NetTypeGroup = ngPgSQL then
+ Result := Connection.QuoteIdent(Schema, AlwaysQuote)
+ else
+ Result := Connection.QuoteIdent(Database, AlwaysQuote);
+end;
+
+function TDBObject.QuotedName(AlwaysQuote: Boolean=True; SeparateSegments: Boolean=True): String;
+begin
+ Result := '';
+ if FConnection.Parameters.IsAnyMSSQL then begin
+ // MSSQL expects schema separated from table, and in some situations the whole string quoted as a whole
+ if Schema <> '' then begin
+ if SeparateSegments then
+ Result := Result + Connection.QuoteIdent(Schema, AlwaysQuote)
+ else
+ Result := Result + Schema;
+ end;
+ Result := Result + '.';
+ if SeparateSegments then
+ Result := Result + Connection.QuoteIdent(Name, AlwaysQuote)
+ else
+ Result := Connection.QuoteIdent(Result + Name, AlwaysQuote);
+ end else begin
+ Result := Result + Connection.QuoteIdent(Name, AlwaysQuote);
+ end;
+end;
+
+function TDBObject.QuotedDbAndTableName(AlwaysQuote: Boolean=True): String;
+begin
+ // Used in data grid query, exclude database in Interbase mode
+ if FConnection.Parameters.IsAnyInterbase then
+ Result := QuotedName(AlwaysQuote)
+ else
+ Result := QuotedDatabase(AlwaysQuote) + '.' + QuotedName(AlwaysQuote);
+end;
+
+function TDBObject.QuotedColumn(AlwaysQuote: Boolean=True): String;
+begin
+ Result := Connection.QuoteIdent(Column, AlwaysQuote);
+end;
+
+// Return fitting schema clause for queries in IS.TABLES, IS.ROUTINES etc.
+// TODO: Does not work on MSSQL 2000
+function TDBObject.SchemaClauseIS(Prefix: String): String;
+begin
+ if Schema <> '' then
+ Result := Prefix+'_SCHEMA' + '=' + Connection.EscapeString(Schema)
+ else
+ Result := Connection.GetSQLSpecifity(spISSchemaCol, [Prefix]) + '=' + Connection.EscapeString(Database);
+end;
+
+function TDBObject.RowCount(Reload: Boolean; ForceExact: Boolean=False): Int64;
+begin
+ if (Rows = -1) or Reload then begin
+ Rows := Connection.GetRowCount(Self, ForceExact);
+ RowsAreExact := ForceExact;
+ end;
+ Result := Rows;
+end;
+
+procedure TDBObject.Drop;
+begin
+ Connection.Drop(Self);
+end;
+
+
+function TDBObject.GetTableColumns: TTableColumnList;
+var
+ ColumnsInCache: TTableColumnList;
+begin
+ // Return columns from table object
+ if not FConnection.FColumnCache.ContainsKey(QuotedDbAndTableName) then begin
+ FConnection.FColumnCache.AddOrSetValue(QuotedDbAndTableName, Connection.GetTableColumns(Self));
+ end;
+ FConnection.FColumnCache.TryGetValue(QuotedDbAndTableName, ColumnsInCache);
+ Result := TTableColumnList.Create;
+ Result.Assign(ColumnsInCache);
+end;
+
+function TDBObject.GetTableKeys: TTableKeyList;
+var
+ KeysInCache: TTableKeyList;
+begin
+ // Return keys from table object
+ if not FConnection.FKeyCache.ContainsKey(QuotedDbAndTableName) then begin
+ FConnection.FKeyCache.AddOrSetValue(QuotedDbAndTableName, Connection.GetTableKeys(Self));
+ end;
+ FConnection.FKeyCache.TryGetValue(QuotedDbAndTableName, KeysInCache);
+ Result := TTableKeyList.Create;
+ Result.Assign(KeysInCache);
+end;
+
+function TDBObject.GetTableForeignKeys: TForeignKeyList;
+var
+ ForeignKeysInCache: TForeignKeyList;
+begin
+ // Return foreign keys from table object
+ if not FConnection.FForeignKeyCache.ContainsKey(QuotedDbAndTableName) then begin
+ FConnection.FForeignKeyCache.AddOrSetValue(QuotedDbAndTableName, Connection.GetTableForeignKeys(Self));
+ end;
+ FConnection.FForeignKeyCache.TryGetValue(QuotedDbAndTableName, ForeignKeysInCache);
+ Result := TForeignKeyList.Create;
+ Result.Assign(ForeignKeysInCache);
+end;
+
+function TDBObject.GetTableCheckConstraints: TCheckConstraintList;
+var
+ CheckConstraintsInCache: TCheckConstraintList;
+begin
+ // Return check constraint from table object
+ if not FConnection.CheckConstraintCache.ContainsKey(QuotedDbAndTableName) then begin
+ FConnection.CheckConstraintCache.AddOrSetValue(QuotedDbAndTableName, Connection.GetTableCheckConstraints(Self));
+ end;
+ FConnection.CheckConstraintCache.TryGetValue(QuotedDbAndTableName, CheckConstraintsInCache);
+ Result := TCheckConstraintList.Create;
+ Result.Assign(CheckConstraintsInCache);
+end;
+
+
+
+{ *** TTableColumn }
+
+constructor TTableColumn.Create(AOwner: TDBConnection; Serialized: String='');
+var
+ Attributes: TStringList;
+ DataTypeIdx, OldDataTypeIdx: TDBDatatypeIndex;
+ i: Integer;
+ NumVal: String;
+
+ function FromSerialized(Name, Default: String): String;
+ begin
+ Result := Attributes.Values[Name];
+ if Result.IsEmpty then
+ Result := Default;
+ end;
+begin
+ // Initialize column from serialized values or use defaults
+ inherited Create;
+ FConnection := AOwner;
+
+ // Prepare serialized string
+ Serialized := StringReplace(Serialized, CHR13REPLACEMENT, #13, [rfReplaceAll]);
+ Serialized := StringReplace(Serialized, CHR10REPLACEMENT, #10, [rfReplaceAll]);
+ Attributes := Explode(LINEDELIMITER, Serialized);
+
+ // Apply given or default attributes
+ Name := FromSerialized('Name', '');
+ OldName := FromSerialized('OldName', '');
+ NumVal := FromSerialized('DataType', Integer(dbdtUnknown).ToString);
+ DataTypeIdx := TDBDatatypeIndex(NumVal.ToInteger);
+ NumVal := FromSerialized('OldDataType', Integer(dbdtUnknown).ToString);
+ OldDataTypeIdx := TDBDatatypeIndex(NumVal.ToInteger);
+ for i:=Low(Connection.Datatypes) to High(Connection.Datatypes) do begin
+ if Connection.Datatypes[i].Index = DataTypeIdx then
+ DataType := Connection.Datatypes[i];
+ if Connection.Datatypes[i].Index = OldDataTypeIdx then
+ OldDataType := Connection.Datatypes[i];
+ end;
+ LengthSet := FromSerialized('LengthSet', '');
+ Unsigned := FromSerialized('Unsigned', '0').ToInteger.ToBoolean;
+ AllowNull := FromSerialized('AllowNull', '1').ToInteger.ToBoolean;
+ ZeroFill := FromSerialized('ZeroFill', '0').ToInteger.ToBoolean;
+ LengthCustomized := FromSerialized('LengthCustomized', '0').ToInteger.ToBoolean;
+ NumVal := FromSerialized('DefaultType', Integer(cdtNothing).ToString);
+ DefaultType := TColumnDefaultType(NumVal.ToInteger);
+ DefaultText := FromSerialized('DefaultText', '');
+ NumVal := FromSerialized('OnUpdateType', Integer(cdtNothing).ToString);
+ OnUpdateType := TColumnDefaultType(NumVal.ToInteger);
+ OnUpdateText := FromSerialized('OnUpdateText', '');
+ Comment := FromSerialized('Comment', '');
+ Charset := FromSerialized('Charset', '');
+ Collation := FromSerialized('Collation', '');
+ GenerationExpression := FromSerialized('Expression', '');
+ Virtuality := FromSerialized('Virtuality', '');
+ Invisible := FromSerialized('Invisible', '0').ToInteger.ToBoolean;
+ SRID := FromSerialized('SRID', '0').ToInteger;
+ NumVal := FromSerialized('Status', Integer(esUntouched).ToString);
+ FStatus := TEditingStatus(NumVal.ToInteger);
+
+ Attributes.Free;
+end;
+
+destructor TTableColumn.Destroy;
+begin
+ inherited Destroy;
+end;
+
+procedure TTableColumn.Assign(Source: TPersistent);
+var
+ s: TTableColumn;
+begin
+ if Source is TTableColumn then begin
+ s := Source as TTableColumn;
+ Name := s.Name;
+ OldName := s.OldName;
+ DataType := s.DataType;
+ OldDataType := s.OldDataType;
+ LengthSet := s.LengthSet;
+ Unsigned := s.Unsigned;
+ AllowNull := s.AllowNull;
+ ZeroFill := s.ZeroFill;
+ LengthCustomized := s.LengthCustomized;
+ DefaultType := s.DefaultType;
+ DefaultText := s.DefaultText;
+ OnUpdateType := s.OnUpdateType;
+ OnUpdateText := s.OnUpdateText;
+ Comment := s.Comment;
+ Charset := s.Charset;
+ Collation := s.Collation;
+ GenerationExpression := s.GenerationExpression;
+ Virtuality := s.Virtuality;
+ Invisible := s.Invisible;
+ SRID := s.SRID;
+ FStatus := s.FStatus;
+ end else
+ inherited;
+end;
+
+function TTableColumn.Serialize: String;
+var
+ s: TStringList;
+begin
+ // Return object attributes/fields in a one-line text format, which can later be
+ // restored through passing that text to the constructor
+ // We could also use the .SQLCode method to get a text representation, but that
+ // would require a more complex deserializing method
+ s := TStringList.Create;
+ s.AddPair('Name', Name);
+ s.AddPair('OldName', OldName);
+ s.AddPair('DataType', Integer(DataType.Index).ToString);
+ s.AddPair('OldDataType', Integer(OldDataType.Index).ToString);
+ s.AddPair('LengthSet', LengthSet);
+ s.AddPair('Unsigned', Unsigned.ToInteger.ToString);
+ s.AddPair('AllowNull', AllowNull.ToInteger.ToString);
+ s.AddPair('ZeroFill', ZeroFill.ToInteger.ToString);
+ s.AddPair('LengthCustomized', LengthCustomized.ToInteger.ToString);
+ s.AddPair('DefaultType', Integer(DefaultType).ToString);
+ s.AddPair('DefaultText', DefaultText);
+ s.AddPair('OnUpdateType', Integer(OnUpdateType).ToString);
+ s.AddPair('OnUpdateText', OnUpdateText);
+ s.AddPair('Comment', Comment);
+ s.AddPair('Charset', Charset);
+ s.AddPair('Collation', Collation);
+ s.AddPair('GenerationExpression', GenerationExpression);
+ s.AddPair('Virtuality', Virtuality);
+ s.AddPair('Invisible', Invisible.ToInteger.ToString);
+ s.AddPair('Status', Integer(FStatus).ToString);
+
+ Result := Implode(LINEDELIMITER, s);
+ s.Free;
+ Result := StringReplace(Result, #13, CHR13REPLACEMENT, [rfReplaceAll]);
+ Result := StringReplace(Result, #10, CHR10REPLACEMENT, [rfReplaceAll]);
+end;
+
+procedure TTableColumn.SetStatus(Value: TEditingStatus);
+begin
+ // Set editing flag and enable "Save" button
+ if (FStatus in [esAddedUntouched, esAddedModified]) and (Value = esModified) then
+ Value := esAddedModified
+ else if (FStatus in [esAddedUntouched, esAddedModified]) and (Value = esDeleted) then
+ Value := esAddedDeleted;
+ FStatus := Value;
+end;
+
+function TTableColumn.SQLCode(OverrideCollation: String=''; Parts: TColumnParts=[cpAll]): String;
+var
+ IsVirtual: Boolean;
+
+ function InParts(Part: TColumnPart): Boolean;
+ begin
+ Result := (Part in Parts) or (cpAll in Parts);
+ end;
+begin
+ Result := '';
+ IsVirtual := (GenerationExpression <> '') and (Virtuality <> '');
+
+ if InParts(cpName) then begin
+ Result := Result + FConnection.QuoteIdent(Name) + ' ';
+ end;
+
+ if InParts(cpType) then begin
+ case FConnection.Parameters.NetTypeGroup of
+ ngPgSQL: begin
+ if DefaultType = cdtAutoInc then
+ Result := Result + 'SERIAL'
+ else
+ Result := Result + DataType.Name;
+ end;
+ else Result := Result + DataType.Name;
+ end;
+
+ if (LengthSet <> '') and DataType.HasLength then
+ Result := Result + '(' + LengthSet + ')';
+ if (DataType.Category in [dtcInteger, dtcReal]) and Unsigned then
+ Result := Result + ' UNSIGNED';
+ if (DataType.Category in [dtcInteger, dtcReal]) and ZeroFill then
+ Result := Result + ' ZEROFILL';
+ Result := Result + ' '; // Add space after each part
+ end;
+
+ if InParts(cpInvisible) and Invisible and FConnection.Has(frInvisibleColumns) then begin
+ Result := Result + 'INVISIBLE ';
+ end;
+
+ if InParts(cpAllowNull) and (not IsVirtual) then begin
+ if not AllowNull then
+ Result := Result + 'NOT NULL '
+ else if not FConnection.Parameters.IsAnyInterbase then
+ Result := Result + 'NULL ';
+ end;
+
+ // SRID for spatial columns supported since MySQL 8.0
+ if InParts(cpSRID) and (DataType.Category = dtcSpatial) and FConnection.Has(frSrid) then begin
+ Result := Result + 'SRID ' + SRID.ToString + ' ';
+ end;
+
+
+ if InParts(cpDefault) and (not IsVirtual) then begin
+ if DefaultType <> cdtNothing then begin
+ case DefaultType of
+ // cdtNothing: leave out whole clause
+ cdtText: Result := Result + 'DEFAULT '+FConnection.EscapeString(DefaultText);
+ cdtNull: Result := Result + 'DEFAULT NULL';
+ cdtAutoInc: begin
+ case FConnection.Parameters.NetTypeGroup of
+ ngPgSQL:;
+ else Result := Result + AutoIncName;
+ end;
+ end;
+ cdtExpression: begin
+ if FConnection.Has(frColumnDefaultParentheses) then
+ Result := Result + 'DEFAULT ('+DefaultText+')'
+ else
+ Result := Result + 'DEFAULT '+DefaultText;
+ end;
+ end;
+ case OnUpdateType of
+ // cdtNothing: leave out whole clause
+ // cdtText: not supported, but may be valid in MariaDB?
+ // cdtNull: not supported, but may be valid in MariaDB?
+ // cdtAutoInc: not valid in ON UPDATE
+ cdtExpression: begin
+ Result := Result + ' ON UPDATE '+OnUpdateText;
+ end;
+ end;
+ Result := Result + ' ';
+ end;
+ end;
+
+ if InParts(cpVirtuality) and IsVirtual then begin
+ Result := Result + 'AS ('+GenerationExpression+') ' + Virtuality + ' ';
+ end;
+
+ if InParts(cpComment) then begin
+ if (Comment <> '') and FConnection.Parameters.IsAnyMySQL then
+ Result := Result + 'COMMENT ' + FConnection.EscapeString(Comment) + ' ';
+ end;
+
+ if InParts(cpCollation) and (not IsVirtual) and (DataType.Index <> dbdtJson) then begin
+ if Collation <> '' then begin
+ Result := Result + 'COLLATE ';
+ if OverrideCollation <> '' then
+ Result := Result + FConnection.EscapeString(OverrideCollation) + ' '
+ else
+ Result := Result + FConnection.EscapeString(Collation) + ' ';
+ end;
+ end;
+
+ Result := Trim(Result);
+end;
+
+
+function TTableColumn.ValueList: TStringList;
+begin
+ // Same as TDBQuery.ValueList, but for callers which do not have a query result
+ Result := TStringList.Create;
+ Result.QuoteChar := '''';
+ Result.Delimiter := ',';
+ if DataType.Index in [dbdtEnum, dbdtSet] then
+ Result.DelimitedText := LengthSet;
+end;
+
+
+procedure TTableColumn.ParseDatatype(Source: String);
+var
+ InLiteral: Boolean;
+ ParenthLeft, i: Integer;
+begin
+ DataType := Connection.GetDatatypeByName(Source, True);
+ // Length / Set
+ // Various datatypes, e.g. BLOBs, don't have any length property
+ InLiteral := False;
+ ParenthLeft := Pos('(', Source);
+ if (ParenthLeft > 0) and DataType.HasLength then begin
+ for i:=ParenthLeft+1 to Length(Source) do begin
+ if (Source[i] = ')') and (not InLiteral) then
+ break;
+ if Source[i] = '''' then
+ InLiteral := not InLiteral;
+ end;
+ LengthSet := Copy(Source, ParenthLeft+1, i-2);
+ if LengthSet = DataType.DefaultSize.ToString then
+ LengthSet := '';
+ end else begin
+ LengthSet := '';
+ end;
+ Unsigned := ExecRegExpr('\bunsigned\b', Source.ToLowerInvariant);
+ ZeroFill := ExecRegExpr('\bzerofill\b', Source.ToLowerInvariant);
+end;
+
+
+function TTableColumn.CastAsText: String;
+begin
+ // Cast data types which are incompatible to string functions to text columns
+ Result := FConnection.QuoteIdent(Name);
+ case FConnection.Parameters.NetTypeGroup of
+ ngMySQL, ngSQLite: begin
+ if DataType.Index in [dbdtUnknown, dbdtDate, dbdtDatetime, dbdtTime, dbdtTimestamp, dbdtJson, dbdtJsonB] then
+ Result := 'CAST('+Result+' AS CHAR)';
+ end;
+ ngMSSQL: begin
+ // Be sure LEFT() and "col LIKE xyz" work with MSSQL
+ // Also, prevent exceeding size limit of 8000 for NVARCHAR
+ if DataType.Index in [dbdtUnknown, dbdtNtext, dbdtText] then
+ Result := 'CAST('+Result+' AS NVARCHAR('+IntToStr(GRIDMAXDATA)+'))';
+ end;
+ ngPgSQL: begin
+ if (DataType.Index in [dbdtUnknown, dbdtJson]) or (DataType.Category = dtcBinary) then
+ Result := Result + '::text';
+ end;
+ end;
+end;
+
+
+function TTableColumn.AutoIncName: String;
+begin
+ case FConnection.Parameters.NetTypeGroup of
+ ngPgSQL: Result := 'SERIAL';
+ else Result := 'AUTO_INCREMENT';
+ end;
+end;
+
+
+function TTableColumn.FullDataType: String;
+begin
+ Result := DataType.Name;
+ if not LengthSet.IsEmpty then
+ Result := Result + '(' + LengthSet + ')';
+end;
+
+
+procedure TTableColumnList.Assign(Source: TTableColumnList);
+var
+ Item, ItemCopy: TTableColumn;
+begin
+ for Item in Source do begin
+ ItemCopy := TTableColumn.Create(Item.Connection);
+ ItemCopy.Assign(Item);
+ Add(ItemCopy);
+ end;
+end;
+
+
+function TTableColumnList.FindByName(const Value: String): TTableColumn;
+var
+ Col: TTableColumn;
+begin
+ Result := nil;
+ for Col in Self do begin
+ if Col.Name = Value then begin
+ Result := Col;
+ break;
+ end;
+ end;
+end;
+
+
+
+{ *** TTableKey }
+
+constructor TTableKey.Create(AOwner: TDBConnection);
+begin
+ inherited Create;
+ FConnection := AOwner;
+ Columns := TStringList.Create;
+ SubParts := TStringList.Create;
+ Collations := TStringList.Create;
+ Columns.OnChange := Modification;
+ Subparts.OnChange := Modification;
+ Collations.OnChange := Modification;
+end;
+
+destructor TTableKey.Destroy;
+begin
+ FreeAndNil(Columns);
+ FreeAndNil(SubParts);
+ FreeAndNil(Collations);
+ inherited Destroy;
+end;
+
+procedure TTableKey.Assign(Source: TPersistent);
+var
+ s: TTableKey;
+begin
+ if Source is TTableKey then begin
+ s := Source as TTableKey;
+ Name := s.Name;
+ OldName := s.OldName;
+ IndexType := s.IndexType;
+ OldIndexType := s.OldIndexType;
+ Algorithm := s.Algorithm;
+ Comment := s.Comment;
+ Columns.Assign(s.Columns);
+ SubParts.Assign(s.SubParts);
+ Collations.Assign(s.Collations);
+ Modified := s.Modified;
+ Added := s.Added;
+ end else
+ inherited;
+end;
+
+function TTableKey.IsPrimary: Boolean;
+begin
+ Result := IndexType = PRIMARY;
+end;
+
+function TTableKey.IsIndex: Boolean;
+begin
+ Result := IndexType = KEY;
+end;
+
+function TTableKey.IsUnique: Boolean;
+begin
+ Result := IndexType = UNIQUE;
+end;
+
+function TTableKey.IsFulltext: Boolean;
+begin
+ Result := IndexType = FULLTEXT;
+end;
+
+function TTableKey.IsSpatial: Boolean;
+begin
+ Result := IndexType = SPATIAL;
+end;
+
+function TTableKey.IsExpression(KeyPart: Integer): Boolean;
+begin
+ Result := Columns[KeyPart].StartsWith('(');
+end;
+
+
+procedure TTableKey.Modification(Sender: TObject);
+begin
+ if not Added then
+ Modified := True;
+end;
+
+function TTableKey.GetImageIndex: Integer;
+begin
+ // Detect key icon index for specified index
+ if IsPrimary then Result := ICONINDEX_PRIMARYKEY
+ else if IsIndex then Result := ICONINDEX_INDEXKEY
+ else if IsUnique then Result := ICONINDEX_UNIQUEKEY
+ else if IsFulltext then Result := ICONINDEX_FULLTEXTKEY
+ else if IsSpatial then Result := ICONINDEX_SPATIALKEY
+ else Result := -1;
+end;
+
+function TTableKey.GetInsideCreateCode: Boolean;
+begin
+ case FConnection.Parameters.NetTypeGroup of
+ ngMySQL: Result := True;
+ ngSQLite: begin
+ if IsPrimary then
+ Result := True
+ else
+ Result := False;
+ end;
+ else Result := True;
+ end;
+end;
+
+function TTableKey.SQLCode(TableName: String=''): String;
+var
+ i: Integer;
+begin
+ Result := '';
+ // Supress SQL error trying index creation with 0 column
+ if Columns.Count = 0 then
+ Exit;
+ if InsideCreateCode then begin
+ if IsPrimary then
+ Result := Result + 'PRIMARY KEY '
+ else begin
+ if FConnection.Parameters.IsAnyPostgreSQL then begin
+ Result := Result + IndexType + ' ';
+ end
+ else begin
+ if not IsIndex then
+ Result := Result + IndexType + ' ';
+ Result := Result + 'INDEX ' + FConnection.QuoteIdent(Name) + ' ';
+ end;
+ end;
+ Result := Result + '(';
+ for i:=0 to Columns.Count-1 do begin
+ if IsExpression(i) then
+ Result := Result + Columns[i] // Don't quote functional key part
+ else
+ Result := Result + FConnection.QuoteIdent(Columns[i]);
+ if (SubParts.Count > i) and (SubParts[i] <> '') then
+ Result := Result + '(' + SubParts[i] + ')';
+ // Collation / sort order, see issue #1512
+ if (Collations.Count > i) and (Collations[i].ToLower = 'd') then
+ Result := Result + ' DESC';
+ Result := Result + ', ';
+ end;
+ if Columns.Count > 0 then
+ Delete(Result, Length(Result)-1, 2);
+
+ Result := Result + ')';
+
+ if Algorithm <> '' then
+ Result := Result + ' USING ' + Algorithm;
+
+ if not Comment.IsEmpty then
+ Result := Result + ' COMMENT ' + FConnection.EscapeString(Comment);
+ end
+ else begin
+ // SQLite syntax:
+ // CREATE INDEX myindex ON table1 ("Column 1")
+ // TODO: test on PG, MS, IB
+ Result := 'CREATE ';
+ if not IsIndex then
+ Result := Result + IndexType + ' ';
+ Result := Result + 'INDEX '+FConnection.QuoteIdent(Name)+' ON ' + FConnection.QuoteIdent(TableName) + ' (';
+ for i:=0 to Columns.Count-1 do begin
+ Result := Result + FConnection.QuoteIdent(Columns[i]);
+ Result := Result + ', ';
+ end;
+ if Columns.Count > 0 then
+ Delete(Result, Length(Result)-1, 2);
+ Result := Result + ')';
+ end;
+
+end;
+
+procedure TTableKeyList.Assign(Source: TTableKeyList);
+var
+ Item, ItemCopy: TTableKey;
+begin
+ for Item in Source do begin
+ ItemCopy := TTableKey.Create(Item.Connection);
+ ItemCopy.Assign(Item);
+ Add(ItemCopy);
+ end;
+end;
+
+
+
+
+{ *** TForeignKey }
+
+constructor TForeignKey.Create(AOwner: TDBConnection);
+begin
+ inherited Create;
+ FConnection := AOwner;
+ Columns := TStringList.Create;
+ Columns.StrictDelimiter := True;
+ ForeignColumns := TStringList.Create;
+ ForeignColumns.StrictDelimiter := True;
+ // Explicit default action required, since MariaDB and MySQL have different defaults if it's left away, see issue #1320
+ OnUpdate := 'NO ACTION';
+ OnDelete := 'NO ACTION';
+end;
+
+destructor TForeignKey.Destroy;
+begin
+ FreeAndNil(Columns);
+ FreeAndNil(ForeignColumns);
+ inherited Destroy;
+end;
+
+procedure TForeignKey.Assign(Source: TPersistent);
+var
+ s: TForeignKey;
+begin
+ if Source is TForeignKey then begin
+ s := Source as TForeignKey;
+ KeyName := s.KeyName;
+ OldKeyName := s.OldKeyName;
+ Db := s.Db;
+ ReferenceDb := s.ReferenceDb;
+ ReferenceTable := s.ReferenceTable;
+ OnUpdate := s.OnUpdate;
+ OnDelete := s.OnDelete;
+ Columns.Assign(s.Columns);
+ ForeignColumns.Assign(s.ForeignColumns);
+ Modified := s.Modified;
+ Added := s.Added;
+ KeyNameWasCustomized := s.KeyNameWasCustomized;
+ end else
+ inherited;
+end;
+
+function TForeignKey.SQLCode(IncludeSymbolName: Boolean): String;
+var
+ i: Integer;
+ TablePart: String;
+begin
+ Result := '';
+ // Symbol names are unique in a db. In order to autocreate a valid name we leave the constraint clause away.
+ if IncludeSymbolName then
+ Result := 'CONSTRAINT '+FConnection.QuoteIdent(KeyName)+' ';
+ Result := Result + 'FOREIGN KEY (';
+ for i:=0 to Columns.Count-1 do
+ Result := Result + FConnection.QuoteIdent(Columns[i]) + ', ';
+ if Columns.Count > 0 then Delete(Result, Length(Result)-1, 2);
+ Result := Result + ') REFERENCES ';
+ if (not ReferenceDb.IsEmpty) and (ReferenceTable.StartsWith(ReferenceDb)) then begin
+ TablePart := ReferenceTable.Substring(Length(ReferenceDb) + 1);
+ if ReferenceDb <> Db then
+ Result := Result + FConnection.QuoteIdent(ReferenceDb) + '.' + FConnection.QuoteIdent(TablePart)
+ else
+ Result := Result + FConnection.QuoteIdent(TablePart);
+ end
+ else begin
+ Result := Result + FConnection.QuoteIdent(ReferenceTable, True, '.');
+ end;
+ Result := Result + ' (';
+ for i:=0 to ForeignColumns.Count-1 do
+ Result := Result + FConnection.QuoteIdent(ForeignColumns[i]) + ', ';
+ if ForeignColumns.Count > 0 then Delete(Result, Length(Result)-1, 2);
+ Result := Result + ')';
+ if OnUpdate <> '' then
+ Result := Result + ' ON UPDATE ' + OnUpdate;
+ if OnDelete <> '' then
+ Result := Result + ' ON DELETE ' + OnDelete;
+end;
+
+
+function TForeignKey.ReferenceTableObj: TDBObject;
+var
+ RefDb, RefTable: String;
+begin
+ // Find database object of reference table
+ if (not ReferenceDb.IsEmpty) and (ReferenceTable.StartsWith(ReferenceDb)) then begin
+ RefDb := ReferenceDb;
+ RefTable := ReferenceTable.Substring(Length(ReferenceDb) + 1);
+ end else begin
+ RefDb := ReferenceTable.Substring(0, Pos('.', ReferenceTable)-1);
+ if (not RefDb.IsEmpty) and (FConnection.FAllDatabases.IndexOf(RefDb) > -1) then begin
+ RefTable := ReferenceTable.Substring(Length(RefDb)+1);
+ end else begin
+ RefDb := FConnection.Database;
+ RefTable := ReferenceTable;
+ end;
+ end;
+ FConnection.Log(lcDebug, 'Find object "'+RefTable+'" in db "'+RefDb+'"');
+ Result := FConnection.FindObject(RefDb, RefTable);
+end;
+
+
+procedure TForeignKeyList.Assign(Source: TForeignKeyList);
+var
+ Item, ItemCopy: TForeignKey;
+begin
+ for Item in Source do begin
+ ItemCopy := TForeignKey.Create(Item.Connection);
+ ItemCopy.Assign(Item);
+ Add(ItemCopy);
+ end;
+end;
+
+
+{ *** TCheckConstraint }
+
+constructor TCheckConstraint.Create(AOwner: TDBConnection);
+begin
+ inherited Create;
+ FConnection := AOwner;
+end;
+
+
+procedure TCheckConstraint.Assign(Source: TPersistent);
+var
+ s: TCheckConstraint;
+begin
+ if Source is TCheckConstraint then begin
+ s := Source as TCheckConstraint;
+ FName := s.Name;
+ FCheckClause := s.CheckClause;
+ FModified := s.Modified;
+ FAdded := s.Added;
+ end else
+ inherited;
+end;
+
+function TCheckConstraint.SQLCode: String;
+begin
+ Result := 'CONSTRAINT '+FConnection.QuoteIdent(FName)+' CHECK ('+FCheckClause+')';
+end;
+
+procedure TCheckConstraintList.Assign(Source: TCheckConstraintList);
+var
+ Item, ItemCopy: TCheckConstraint;
+begin
+ for Item in Source do begin
+ ItemCopy := TCheckConstraint.Create(Item.Connection);
+ ItemCopy.Assign(Item);
+ Add(ItemCopy);
+ end;
+end;
+
+
+{ TSQLFunctionList }
+
+constructor TSQLFunctionList.Create(AOwner: TDBConnection; SQLFunctionsFileOrder: String);
+var
+ TryFiles: TStringList;
+ TryFile: String;
+ Ini: TMemIniFile;
+ Sections: TStringList;
+ IniFilePath, Section: String;
+ SQLFunc: TSQLFunction;
+begin
+ inherited Create(True);
+ FOwner := AOwner;
+
+ FCategories := TStringList.Create;
+ FCategories.Duplicates := dupIgnore;
+ FCategories.Sorted := True; // ensures dupIgnore works
+ FNames := TStringList.Create;
+ FNames.Duplicates := dupIgnore;
+ FNames.Sorted := True;
+
+ TryFiles := Explode(',', SQLFunctionsFileOrder);
+ for TryFile in TryFiles do begin
+ IniFilePath := ExtractFilePath(Application.ExeName) + 'functions-'+TryFile+'.ini';
+ FOwner.Log(lcDebug, 'Trying '+IniFilePath);
+ if FileExists(IniFilePath) then begin
+ FOwner.Log(lcInfo, 'Reading function definitions from '+IniFilePath);
+ Ini := TMemIniFile.Create(IniFilePath);
+ Sections := TStringList.Create;
+ Ini.ReadSections(Sections);
+ for Section in Sections do begin
+ SQLFunc := TSQLFunction.Create;
+ SQLFunc.Name := Ini.ReadString(Section, 'Name', Section);
+ SQLFunc.Declaration := '(' + Ini.ReadString(Section, 'Declaration', '') + ')';
+ SQLFunc.Category := Ini.ReadString(Section, 'Category', '');
+ SQLFunc.Description := Ini.ReadString(Section, 'Description', '');
+ SQLFunc.Description := StringReplace(SQLFunc.Description, '\n', sLineBreak, [rfReplaceAll]);
+ Add(SQLFunc);
+ FCategories.Add(SQLFunc.Category);
+ FNames.Add(SQLFunc.Name);
+ end;
+ Ini.Free;
+ Break;
+ end;
+ end;
+end;
+
+
+procedure SQLite_CollationNeededCallback(userData: Pointer; ppDb:Psqlite3; eTextRep:integer; zName:PAnsiChar); cdecl;
+var
+ Conn: TSQLiteConnection;
+begin
+ // SQLite connection requests a yet non existing collation. Create it and show that in the log.
+ // userData is a pointer to the connection object, see caller in SetActive()
+ Conn := TSQLiteConnection(userData);
+ Conn.Log(lcInfo, Format('Auto-creating collation "%s"', [zName]));
+ Conn.Lib.sqlite3_create_collation(ppDb, zName, eTextRep, nil, SQLite_Collation);
+end;
+
+function SQLite_Collation(userData: Pointer; lenA: Integer; strA: PAnsiChar; lenB: Integer; strB: PAnsiChar): Integer; cdecl;
+begin
+ // Implementation of a collation comparison, called by SQLite when an underlying table query needs it.
+ // This is probably not always some case insensitive collation
+ Result := AnsiCompareText(strA, strB);
+end;
+
+
+function mysql_authentication_dialog_ask;
+var
+ Username, Password: String;
+ Dialog: TfrmLogin;
+begin
+ {
+ From client_plugin.h:
+ The C function with the name "mysql_authentication_dialog_ask", if exists,
+ will be used by the "dialog" client authentication plugin when user
+ input is needed. This function should be of mysql_authentication_dialog_ask_t
+ type. If the function does not exists, a built-in implementation will be
+ used.
+ @param mysql mysql
+ @param type type of the input
+ 1 - normal string input
+ 2 - password string
+ @param prompt prompt
+ @param buf a buffer to store the use input
+ @param buf_len the length of the buffer
+ @retval a pointer to the user input string.
+ It may be equal to 'buf' or to 'mysql->password'.
+ In all other cases it is assumed to be an allocated
+ string, and the "dialog" plugin will free() it.
+ Test suite:
+ INSTALL PLUGIN three_attempts SONAME 'dialog.dll';
+ CREATE USER test_dialog IDENTIFIED VIA three_attempts USING 'SECRET';
+ }
+ Username := '';
+ Password := '';
+ Dialog := TfrmLogin.Create(nil);
+ Dialog.lblPrompt.Caption := String(prompt);
+ Dialog.editUsername.Width := Dialog.editUsername.Width + (Dialog.editUsername.Left - Dialog.lblUsername.Left);
+ Dialog.editPassword.Width := Dialog.editUsername.Width;
+ Dialog.lblUsername.Visible := False;
+ Dialog.lblPassword.Visible := False;
+ Dialog.editUsername.Left := Dialog.lblUsername.Left;
+ Dialog.editPassword.Left := Dialog.lblPassword.Left;
+ Dialog.editUsername.Top := Dialog.lblPrompt.Top + Dialog.lblPrompt.Height + 15;
+ Dialog.editPassword.Top := Dialog.editUsername.Top;
+ Dialog.editUsername.Visible := _type=1;
+ Dialog.editPassword.Visible := _type=2;
+ Dialog.ShowModal;
+ case _type of
+ 1: Result := PAnsiChar(AnsiString(Dialog.editUsername.Text));
+ 2: Result := PAnsiChar(AnsiString(Dialog.editPassword.Text));
+ else raise EDbError.CreateFmt(_('Unsupported type (%d) in %s.'), [_type, 'mysql_authentication_dialog_ask']);
+ end;
+ Dialog.Free;
+end;
+
+
+
+end.
diff --git a/source/dbstructures.interbase.pas b/source/dbstructures.interbase.pas
new file mode 100644
index 00000000..c392cc26
--- /dev/null
+++ b/source/dbstructures.interbase.pas
@@ -0,0 +1,176 @@
+unit dbstructures.interbase;
+
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ dbstructures;
+
+var
+
+ // Interbase field types
+ // Taken from https://docwiki.embarcadero.com/InterBase/2020/en/RDB$FIELDS
+ InterbaseDatatypes: Array[0..13] of TDBDatatype =
+ (
+ (
+ Index: dbdtUnknown;
+ Name: 'UNKNOWN';
+ Description: 'Unknown data type';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtBlob;
+ NativeTypes: '261';
+ Name: 'BLOB';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtBool;
+ NativeTypes: '17';
+ Name: 'BOOLEAN';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtChar;
+ NativeTypes: '14';
+ Name: 'CHAR';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtVarchar;
+ NativeTypes: '37|40';
+ Name: 'VARCHAR';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtFloat;
+ NativeTypes: '10|11';
+ Name: 'FLOAT';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtDouble;
+ NativeTypes: '27';
+ Name: 'DOUBLE PRECISION';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtBigint;
+ NativeTypes: '16';
+ Name: 'INT64';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtInt;
+ NativeTypes: '8';
+ Name: 'INTEGER';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtNumeric;
+ NativeTypes: '9';
+ Name: 'QUAD';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtSmallint;
+ NativeTypes: '7';
+ Name: 'SMALLINT';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtDate;
+ NativeTypes: '12';
+ Name: 'DATE';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtTime;
+ NativeTypes: '13';
+ Name: 'TIME';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtTimestamp;
+ NativeTypes: '35';
+ Name: 'TIMESTAMP';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcTemporal;
+ )
+ );
+
+
+implementation
+
+end.
\ No newline at end of file
diff --git a/source/dbstructures.mssql.pas b/source/dbstructures.mssql.pas
new file mode 100644
index 00000000..3bd21afd
--- /dev/null
+++ b/source/dbstructures.mssql.pas
@@ -0,0 +1,412 @@
+unit dbstructures.mssql;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ dbstructures;
+
+var
+
+ MSSQLDatatypes: array [0..33] of TDBDatatype =
+ (
+ (
+ Index: dbdtUnknown;
+ NativeTypes: '99999';
+ Name: 'UNKNOWN';
+ Description: 'Unknown data type';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtTinyint;
+ Name: 'TINYINT';
+ Description: 'Integer data from 0 through 255.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtSmallint;
+ Name: 'SMALLINT';
+ Description: 'Integer data from -2^15 (-32,768) through 2^15 - 1 (32,767).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtInt;
+ Name: 'INT';
+ Description: 'Integer (whole number) data from -2^31 (-2,147,483,648) through 2^31 - 1 (2,147,483,647).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBigint;
+ Name: 'BIGINT';
+ Description: 'Integer (whole number) data from -2^63 (-9,223,372,036,854,775,808) through 2^63-1 (9,223,372,036,854,775,807).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBit;
+ Name: 'BIT';
+ Description: '0 or 1';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtDecimal;
+ Name: 'DECIMAL';
+ Description: 'Fixed precision and scale numeric data from -10^38 +1 through 10^38 1.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ DefLengthSet: '10,0';
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtNumeric;
+ Name: 'NUMERIC';
+ Description: 'Functionally equivalent to decimal.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ DefLengthSet: '10,0';
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtMoney;
+ Name: 'MONEY';
+ Description: 'Monetary data values from -2^63 (-922,337,203,685,477.5808) through 2^63 - 1 (+922,337,203,685,477.5807), with accuracy to a ten-thousandth of a monetary unit.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtSmallmoney;
+ Name: 'SMALLMONEY';
+ Description: 'Monetary data values from -214,748.3648 through +214,748.3647, with accuracy to a ten-thousandth of a monetary unit.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtFloat;
+ Name: 'FLOAT';
+ Description: 'Floating precision number data with the following valid values: -1.79E + 308 through -2.23E - 308, 0 and 2.23E + 308 through 1.79E + 308.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtReal;
+ Name: 'REAL';
+ Description: 'Floating precision number data with the following valid values: -3.40E + 38 through -1.18E - 38, 0 and 1.18E - 38 through 3.40E + 38.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtTime;
+ Name: 'TIME';
+ Description: 'The time data type stores time values only, based on a 24-hour clock. '+
+ 'The time data type has a range of 00:00:00.0000000 through 23:59:59.9999999 with an '+
+ 'accuracy of 100 nanoseconds. The default value is 00:00:00.0000000 (midnight). The '+
+ 'time data type supports user-defined fractional second precision, and the storage '+
+ 'size varies from 3 to 6 bytes, based on the precision specified.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDate;
+ Name: 'DATE';
+ Description: 'The date data type has a range of January 1, 01 through December 31, '+
+ '9999 with an accuracy of 1 day. The default value is January 1, 1900. The storage size '+
+ 'is 3 bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetime;
+ Name: 'DATETIME';
+ Description: 'Date and time data from January 1, 1753, through December 31, 9999, with an accuracy of three-hundredths of a second, or 3.33 milliseconds.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss.zzz';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetime2;
+ Name: 'DATETIME2';
+ Description: 'Date and time data from January 1,1 AD through December 31, 9999 AD, with an accuracy of three-hundredths of a second, or 3.33 milliseconds.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss.zzzzzzz';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetimeOffset;
+ Name: 'DATETIMEOFFSET';
+ Description: 'Defines a date that is combined with a time of a day that has time zone awareness and is based on a 24-hour clock.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss.zzzzzzz';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtSmalldatetime;
+ Name: 'SMALLDATETIME';
+ Description: 'Date and time data from January 1, 1900, through June 6, 2079, with an accuracy of one minute.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtTimestamp;
+ Name: 'TIMESTAMP';
+ Description: 'A database-wide unique number that gets updated every time a row gets updated.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtChar;
+ Name: 'CHAR';
+ Description: 'Fixed-length non-Unicode character data with a maximum length of 8,000 characters.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtVarchar;
+ Name: 'VARCHAR';
+ Description: 'Variable-length non-Unicode data with a maximum of 8,000 characters.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtText;
+ Name: 'TEXT';
+ Description: 'Variable-length non-Unicode data with a maximum length of 2^31 - 1 (2,147,483,647) characters.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtNchar;
+ Name: 'NCHAR';
+ Description: 'Fixed-length Unicode data with a maximum length of 4,000 characters.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtNvarchar;
+ Name: 'NVARCHAR';
+ Description: 'Variable-length Unicode data with a maximum length of 4,000 characters. sysname is a system-supplied user-defined data type that is functionally equivalent to nvarchar(128) and is used to reference database object names.';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtNtext;
+ Name: 'NTEXT';
+ Description: 'Variable-length Unicode data with a maximum length of 2^30 - 1 (1,073,741,823) characters.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtBinary;
+ Name: 'BINARY';
+ Description: 'Fixed-length binary data with a maximum length of 8,000 bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtVarbinary;
+ Name: 'VARBINARY';
+ Description: 'Variable-length binary data with a maximum length of 8,000 bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtImage;
+ Name: 'IMAGE';
+ Description: 'Variable-length binary data with a maximum length of 2^31 - 1 (2,147,483,647) bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtCursor;
+ Name: 'CURSOR';
+ Description: 'A reference to a cursor.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtSqlvariant;
+ Name: 'SQL_VARIANT';
+ Description: 'A data type that stores values of various SQL Server-supported data types, except text, ntext, timestamp, and sql_variant.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtTable;
+ Name: 'TABLE';
+ Description: 'A special data type used to store a result set for later processing .';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtUniqueidentifier;
+ Name: 'UNIQUEIDENTIFIER';
+ Description: 'A globally unique identifier (GUID).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtHierarchyid;
+ Name: 'HIERARCHYID';
+ Description: 'Represents a position in a hierarchy.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtXML;
+ Name: 'XML';
+ Description: 'Lets you store XML documents and fragments.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ )
+ );
+
+
+implementation
+
+end.
diff --git a/source/dbstructures.postgresql.pas b/source/dbstructures.postgresql.pas
new file mode 100644
index 00000000..35fb90fd
--- /dev/null
+++ b/source/dbstructures.postgresql.pas
@@ -0,0 +1,578 @@
+unit dbstructures.postgresql;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ dbstructures;
+
+type
+ // PostgreSQL structures
+ TPQConnectStatus = (CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE, CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV, CONNECTION_SSL_STARTUP, CONNECTION_NEEDED);
+ PPGconn = Pointer;
+ PPGresult = Pointer;
+ POid = Cardinal; // Object ID is a fundamental type in Postgres.
+ TPostgreSQLLib = class(TDbLib)
+ PQconnectdb: function(const ConnInfo: PAnsiChar): PPGconn cdecl;
+ PQerrorMessage: function(const Handle: PPGconn): PAnsiChar cdecl;
+ PQresultErrorMessage: function(const Result: PPGresult): PAnsiChar cdecl;
+ PQresultErrorField: function(const Result: PPGresult; fieldcode: Integer): PAnsiChar;
+ PQfinish: procedure(const Handle: PPGconn);
+ PQstatus: function(const Handle: PPGconn): TPQConnectStatus cdecl;
+ PQsendQuery: function(const Handle: PPGconn; command: PAnsiChar): Integer cdecl;
+ PQgetResult: function(const Handle: PPGconn): PPGresult cdecl;
+ PQbackendPID: function(const Handle: PPGconn): Integer cdecl;
+ PQcmdTuples: function(Result: PPGresult): PAnsiChar; cdecl;
+ PQntuples: function(Result: PPGresult): Integer; cdecl;
+ PQclear: procedure(Result: PPGresult); cdecl;
+ PQnfields: function(Result: PPGresult): Integer; cdecl;
+ PQfname: function(const Result: PPGresult; column_number: Integer): PAnsiChar; cdecl;
+ PQftype: function(const Result: PPGresult; column_number: Integer): POid; cdecl;
+ PQftable: function(const Result: PPGresult; column_number: Integer): POid; cdecl;
+ PQgetvalue: function(const Result: PPGresult; row_number: Integer; column_number: Integer): PAnsiChar; cdecl;
+ PQgetlength: function(const Result: PPGresult; row_number: Integer; column_number: Integer): Integer; cdecl;
+ PQgetisnull: function(const Result: PPGresult; row_number: Integer; column_number: Integer): Integer; cdecl;
+ PQlibVersion: function(): Integer; cdecl;
+ protected
+ procedure AssignProcedures; override;
+ end;
+
+const InvalidOid: POid = 0;
+
+var
+ PostgreSQLDatatypes: Array[0..38] of TDBDatatype =
+ (
+ (
+ Index: dbdtUnknown;
+ NativeTypes: '99999';
+ Name: 'UNKNOWN';
+ Description: 'Unknown data type';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtSmallint;
+ NativeTypes: '21';
+ Name: 'SMALLINT';
+ Names: 'smallint|int2';
+ Description: 'Small-range integer. Range: -32768 to +32767. Storage Size: 2 Bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ ValueMustMatch: '^\d{1,5}$';
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtInt;
+ // 26 = oid, 28 = xid
+ NativeTypes: '23|26|28';
+ Name: 'INTEGER';
+ Names: 'integer|int4|int|oid|xid';
+ Description: 'Typical choice for integer. Range: -2147483648 to +2147483647. Storage Size: 4 Bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ ValueMustMatch: '^\d{1,10}$';
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBigint;
+ NativeTypes: '20';
+ Name: 'BIGINT';
+ Names: 'bigint|int8';
+ Description: 'Large-range integer. Range: -9223372036854775808 to 9223372036854775807. Storage Size: 8 Bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ ValueMustMatch: '^\d{1,19}$';
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtSerial;
+ Name: 'SERIAL';
+ Names: 'serial|serial4';
+ Description: 'Autoincrementing integer. Range: 1 to 2147483647. Storage Size: 4 Bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBigSerial;
+ Name: 'BIGSERIAL';
+ Names: 'bigserial|serial8';
+ Description: 'Large autoincrementing integer. Range: 1 to 9223372036854775807. Storage Size: 8 Bytes.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtVarBit;
+ NativeTypes: '1562';
+ Name: 'BIT VARYING';
+ Names: 'bit varying|varbit';
+ Description: 'Variable-length bit string.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBit;
+ NativeTypes: '1560';
+ Name: 'BIT';
+ Names: 'bit';
+ Description: 'Fixed-length bit string.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtNumeric;
+ NativeTypes: '1700';
+ Name: 'NUMERIC';
+ Names: 'numeric|float8|decimal';
+ Description: 'User-specified precision, exact. Range: no limit. Storage Size: variable.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtReal;
+ NativeTypes: '700';
+ Name: 'REAL';
+ Names: 'real|float4';
+ Description: 'Variable-precision, inexact. Range: 6 decimal digits precision. Storage Size: 4 Bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtDoublePrecision;
+ NativeTypes: '701|1700';
+ Name: 'DOUBLE PRECISION';
+ Names: 'double precision|float8';
+ Description: 'Variable-precision, inexact. Range: 15 decimal digits precision. Storage Size: 8 Bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtChar;
+ NativeTypes: '18|1042';
+ Name: 'CHAR';
+ Names: 'CHARACTER';
+ Description: 'Fixed-length, blank padded.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtVarchar;
+ NativeTypes: '18|19|24|1043|1043';
+ Name: 'VARCHAR';
+ Names: 'char|bpchar|varchar|name|enum|character varying';
+ Description: 'Variable-length with limit.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtText;
+ NativeTypes: '25|22|30|143|629|651|719|791|1000|1028|1040|1041|1115|1182|1183|1185|1187|1231|1263|1270|1561|1563|2201|2207|2211|2949|2951|3643|3644|3645|3735|3770';
+ Name: 'TEXT';
+ Names: 'text|int2vector|oidvector|bool';
+ Description: 'Variable unlimited length.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtCiText;
+ NativeTypes: '?';
+ Name: 'CITEXT';
+ Names: 'citext';
+ Description: 'A case-insensitive character string type. Essentially, it internally calls lower when comparing values. Otherwise, it behaves almost exactly like text.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtCidr;
+ NativeTypes: '650';
+ Name: 'CIDR';
+ Names: 'cidr';
+ Description: 'IPv4 and IPv6 networks. Storage size: 7 or 19 bytes';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtInet;
+ NativeTypes: '869';
+ Name: 'INET';
+ Names: 'inet';
+ Description: 'IPv4 and IPv6 hosts and networks. Storage size: 7 or 19 bytes';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtMacaddr;
+ NativeTypes: '829';
+ Name: 'MACADDR';
+ Names: 'macaddr';
+ Description: 'MAC addresses. Storage size: 6 bytes';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtMoney;
+ NativeTypes: '790';
+ Name: 'MONEY';
+ Description: 'Currency amount. Range: -92233720368547758.08 to +92233720368547758.07. Storage Size: 8 Bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtDate;
+ NativeTypes: '1082';
+ Name: 'DATE';
+ Description: 'Calendar date (year, month, day).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtTime;
+ NativeTypes: '1083';
+ Name: 'TIME';
+ Description: 'Time of day.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetime;
+ NativeTypes: '1082|1114|702';
+ Name: 'TIMESTAMP';
+ Names: 'timestamp|datetime|abstime|timestamp without time zone';
+ Description: 'Date and time without timezone, e.g. "2020-06-27 16:24:41".';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetime2;
+ NativeTypes: '1184';
+ Name: 'TIMESTAMPTZ';
+ Names: 'timestamptz|timestamp with time zone';
+ Description: 'Date and time with time zone, e.g. "2020-06-27 16:24:41+02".';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDate;
+ NativeTypes: '1082';
+ Name: 'DATE';
+ Description: 'Calendar date (year, month, day).';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtInterval;
+ NativeTypes: '1186';
+ Name: 'INTERVAL';
+ Description: 'time interval from -178000000 years to 178000000 years';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Format: 'yyyy-mm-dd hh:nn:ss';
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtBlob;
+ NativeTypes: '17';
+ Name: 'BYTEA';
+ Description: 'Binary data ("byte array").';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtPoint;
+ NativeTypes: '600';
+ Name: 'POINT';
+ Description: 'Point on a plane (x,y). Storage size: 16 bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtLinestring;
+ NativeTypes: '628';
+ Name: 'LINE';
+ Description: 'Infinite line ((x1,y1),(x2,y2)). Storage size: 32 bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtLineSegment;
+ NativeTypes: '601';
+ Name: 'LSEG';
+ Description: 'Finite line segment ((x1,y1),(x2,y2)). Storage size: 32 bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtBox;
+ NativeTypes: '603';
+ Name: 'BOX';
+ Description: 'Rectangular box ((x1,y1),(x2,y2)). Storage size: 32 bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtPath;
+ NativeTypes: '602';
+ Name: 'PATH';
+ Description: 'Closed path (similar to polygon) ((x1,y1),...). Storage size: 16+16n bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtPolygon;
+ NativeTypes: '604';
+ Name: 'POLYGON';
+ Description: 'Closed path (similar to polygon) ((x1,y1),...). Storage size: 40+16n bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtCircle;
+ NativeTypes: '718';
+ Name: 'CIRCLE';
+ Description: 'Circle <(x,y),r> (center point and radius). Storage size: 24 bytes.';
+ HasLength: True;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcSpatial;
+ ),
+ (
+ Index: dbdtBool;
+ NativeTypes: '16';
+ Name: 'BOOLEAN';
+ Names: 'boolean|bool';
+ Description: 'State of true or false. Storage size: 1 byte.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ ValueMustMatch: '^(true|false)$';
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtRegClass;
+ NativeTypes: '2205';
+ Name: 'REGCLASS';
+ Names: 'regclass';
+ Description: 'Relation name';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtRegProc;
+ NativeTypes: '24';
+ Name: 'REGPROC';
+ Names: 'regproc|regprocedure';
+ Description: 'Function name';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtJson;
+ NativeTypes: '114';
+ Name: 'JSON';
+ Names: 'json';
+ Description: 'JavaScript Object Notation data';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtJsonB;
+ NativeTypes: '3802';
+ Name: 'JSONB';
+ Names: 'jsonb';
+ Description: 'JavaScript Object Notation data in a binary form';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtUniqueidentifier;
+ NativeTypes: '2950';
+ Name: 'UUID';
+ Names: 'uuid';
+ Description: 'The data type uuid stores Universally Unique Identifiers (UUID) as defined by RFC 4122, ISO/IEC 9834-8:2005, and related standards.';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ ValueMustMatch: '^\{?[a-f0-9]{8}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{12}\}?$';
+ Category: dtcText;
+ )
+ );
+
+implementation
+
+procedure TPostgreSQLLib.AssignProcedures;
+begin
+ AssignProc(@PQconnectdb, 'PQconnectdb');
+ AssignProc(@PQerrorMessage, 'PQerrorMessage');
+ AssignProc(@PQresultErrorMessage, 'PQresultErrorMessage');
+ AssignProc(@PQresultErrorField, 'PQresultErrorField');
+ AssignProc(@PQfinish, 'PQfinish');
+ AssignProc(@PQstatus, 'PQstatus');
+ AssignProc(@PQsendQuery, 'PQsendQuery');
+ AssignProc(@PQgetResult, 'PQgetResult');
+ AssignProc(@PQbackendPID, 'PQbackendPID');
+ AssignProc(@PQcmdTuples, 'PQcmdTuples');
+ AssignProc(@PQntuples, 'PQntuples');
+ AssignProc(@PQclear, 'PQclear');
+ AssignProc(@PQnfields, 'PQnfields');
+ AssignProc(@PQfname, 'PQfname');
+ AssignProc(@PQftype, 'PQftype');
+ AssignProc(@PQftable, 'PQftable');
+ AssignProc(@PQgetvalue, 'PQgetvalue');
+ AssignProc(@PQgetlength, 'PQgetlength');
+ AssignProc(@PQgetisnull, 'PQgetisnull');
+ AssignProc(@PQlibVersion, 'PQlibVersion');
+end;
+
+
+end.
diff --git a/source/dbstructures.sqlite.pas b/source/dbstructures.sqlite.pas
new file mode 100644
index 00000000..87ac0a86
--- /dev/null
+++ b/source/dbstructures.sqlite.pas
@@ -0,0 +1,392 @@
+unit dbstructures.sqlite;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ dbstructures;
+
+
+const
+ { SQLite Result Codes
+ result code definitions
+ Many SQLite functions return an integer result code from the set shown
+ here in order to indicate success or failure.
+ New error codes may be added in future versions of SQLite.
+ See also: [extended result code definitions]
+ }
+ SQLITE_OK = 0; // Successful result
+ // beginning-of-error-codes
+ SQLITE_ERROR = 1; // Generic error
+ SQLITE_INTERNAL = 2; // Internal logic error in SQLite
+ SQLITE_PERM = 3; // Access permission denied
+ SQLITE_ABORT = 4; // Callback routine requested an abort
+ SQLITE_BUSY = 5; // The database file is locked
+ SQLITE_LOCKED = 6; // A table in the database is locked
+ SQLITE_NOMEM = 7; // A malloc() failed
+ SQLITE_READONLY = 8; // Attempt to write a readonly database
+ SQLITE_INTERRUPT = 9; // Operation terminated by sqlite3_interrupt()*/
+ SQLITE_IOERR = 10; // Some kind of disk I/O error occurred
+ SQLITE_CORRUPT = 11; // The database disk image is malformed
+ SQLITE_NOTFOUND = 12; // Unknown opcode in sqlite3_file_control()
+ SQLITE_FULL = 13; // Insertion failed because database is full
+ SQLITE_CANTOPEN = 14; // Unable to open the database file
+ SQLITE_PROTOCOL = 15; // Database lock protocol error
+ SQLITE_EMPTY = 16; // Internal use only
+ SQLITE_SCHEMA = 17; // The database schema changed
+ SQLITE_TOOBIG = 18; // String or BLOB exceeds size limit
+ SQLITE_CONSTRAINT = 19; // Abort due to constraint violation
+ SQLITE_MISMATCH = 20; // Data type mismatch
+ SQLITE_MISUSE = 21; // Library used incorrectly
+ SQLITE_NOLFS = 22; // Uses OS features not supported on host
+ SQLITE_AUTH = 23; // Authorization denied
+ SQLITE_FORMAT = 24; // Not used
+ SQLITE_RANGE = 25; // 2nd parameter to sqlite3_bind out of range
+ SQLITE_NOTADB = 26; // File opened that is not a database file
+ SQLITE_NOTICE = 27; // Notifications from sqlite3_log()
+ SQLITE_WARNING = 28; // Warnings from sqlite3_log()
+ SQLITE_ROW = 100; // sqlite3_step() has another row ready
+ SQLITE_DONE = 101; // sqlite3_step() has finished executing
+
+ { SQLite Flags
+ These constants define various flags that can be passed into
+ "prepFlags" parameter of the [sqlite3_prepare_v3()] and
+ [sqlite3_prepare16_v3()] interfaces.
+ New flags may be added in future releases of SQLite.
+ }
+ SQLITE_PREPARE_PERSISTENT = $01; // prepared statement will be retained for a long time and probably reused many times
+ SQLITE_PREPARE_NORMALIZE = $02; // no-op
+ SQLITE_PREPARE_NO_VTAB = $04; // return an error (error code SQLITE_ERROR) if the statement uses any virtual tables
+
+
+
+ { SQLite Fundamental Datatypes
+
+ Every value in SQLite has one of five fundamental datatypes:
+ 64-bit signed integer
+ 64-bit IEEE floating point number
+ string
+ BLOB
+ NULL
+ }
+ SQLITE_INTEGER = 1;
+
+ SQLITE_FLOAT = 2;
+ SQLITE_BLOB = 4;
+ SQLITE_NULL = 5;
+ SQLITE_TEXT = 3;
+ SQLITE3_TEXT = 3;
+ { CAPI3REF: Database Connection Configuration Options
+ These constants are the available integer configuration options that
+ can be passed as the second argument to the [sqlite3_db_config()] interface.
+ }
+ SQLITE_DBCONFIG_MAINDBNAME = 1000; // const char*
+ SQLITE_DBCONFIG_LOOKASIDE = 1001; // void* int int
+ SQLITE_DBCONFIG_ENABLE_FKEY = 1002; // int int*
+ SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003; // int int*
+ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004; // int int*
+ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005; // int int*
+ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006; // int int*
+ SQLITE_DBCONFIG_ENABLE_QPSG = 1007; // int int*
+ SQLITE_DBCONFIG_TRIGGER_EQP = 1008; // int int*
+ SQLITE_DBCONFIG_RESET_DATABASE = 1009; // int int*
+ SQLITE_DBCONFIG_DEFENSIVE = 1010; // int int*
+ SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011; // int int*
+ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012; // int int*
+ SQLITE_DBCONFIG_DQS_DML = 1013; // int int*
+ SQLITE_DBCONFIG_DQS_DDL = 1014; // int int*
+ SQLITE_DBCONFIG_ENABLE_VIEW = 1015; // int int*
+ SQLITE_DBCONFIG_MAX = 1015; // Largest DBCONFIG
+
+
+type
+
+ Psqlite3 = Pointer;
+ Psqlite3_stmt = Pointer;
+
+ TSQLiteCollationNeededCallback = procedure(userData: Pointer; ppDb:Psqlite3; eTextRep: Integer; zName: PAnsiChar); cdecl;
+ TSQLiteCollation = function(userData: Pointer; lenA: Integer; strA: PAnsiChar; lenB: Integer; strB: PAnsiChar): Integer; cdecl;
+
+ TSQLiteLib = class(TDbLib)
+ sqlite3_open: function(const filename: PAnsiChar; var ppDb: Psqlite3): Integer; cdecl;
+ sqlite3_libversion: function(): PAnsiChar; cdecl;
+ sqlite3_close: function(ppDb: Psqlite3): Integer; cdecl;
+ sqlite3_db_config: function (ppDb: Psqlite3; op: Integer): Integer; cdecl varargs;
+ sqlite3_enable_load_extension: function(ppDb: Psqlite3; onoff: Integer): Integer; cdecl;
+ sqlite3_errmsg: function(ppDb: Psqlite3): PAnsiChar; cdecl;
+ sqlite3_errcode: function(ppDb: Psqlite3): Integer; cdecl;
+ sqlite3_prepare_v2: function(ppDb: Psqlite3; zSql: PAnsiChar; nByte: Integer; var ppStmt: Psqlite3_stmt; var pzTail: PAnsiChar): Integer; cdecl;
+ sqlite3_prepare_v3: function(ppDb: Psqlite3; zSql: PAnsiChar; nByte: Integer; prepFlags: Cardinal; var ppStmt: Psqlite3_stmt; var pzTail: PAnsiChar): Integer; cdecl;
+ sqlite3_exec: function(ppDb: Psqlite3; sql: PAnsiChar; callback: Integer; callvack_arg: Pointer; errmsg: PAnsiChar): Integer; cdecl;
+ sqlite3_finalize: function(pStmt: Psqlite3_stmt): Integer; cdecl;
+ sqlite3_step: function(pStmt: Psqlite3_stmt): Integer; cdecl;
+ sqlite3_reset: function(pStmt: Psqlite3_stmt): Integer; cdecl;
+ sqlite3_total_changes: function(ppDb: Psqlite3): Integer; cdecl;
+ sqlite3_column_text: function(pStmt: Psqlite3_stmt; iCol: Integer): PAnsiChar; cdecl;
+ sqlite3_column_count: function(pStmt: Psqlite3_stmt): Integer; cdecl;
+ sqlite3_column_name: function(pStmt: Psqlite3_stmt; N: Integer): PAnsiChar; cdecl;
+ sqlite3_column_decltype: function(pStmt: Psqlite3_stmt; N: Integer): PAnsiChar; cdecl;
+ sqlite3_column_database_name: function(pStmt: Psqlite3_stmt; N: Integer): PAnsiChar; cdecl;
+ sqlite3_column_table_name: function(pStmt: Psqlite3_stmt; N: Integer): PAnsiChar; cdecl;
+ sqlite3_column_origin_name: function(pStmt: Psqlite3_stmt; N: Integer): PAnsiChar; cdecl;
+ sqlite3_column_type: function(pStmt: Psqlite3_stmt; iCol: Integer): Integer; cdecl;
+ sqlite3_next_stmt: function(ppDb: Psqlite3; pStmt: Psqlite3_stmt): Psqlite3_stmt; cdecl;
+ sqlite3_table_column_metadata: function(ppDb: Psqlite3;
+ zDbName, zTableName, zColumnName: PAnsiChar;
+ var pzDataType, pzCollSeq: PAnsiChar; var pNotNull, pPrimaryKey, pAutoinc: Integer
+ ): Integer; cdecl;
+ sqlite3_collation_needed: function(ppDb: Psqlite3; userData: Pointer; Func: TSQLiteCollationNeededCallback): Integer; cdecl;
+ sqlite3_create_collation: function(ppDb: Psqlite3; const zName: PAnsiChar; eTextRep: Integer; pArg: Pointer; xCompare: TSQLiteCollation): Integer; cdecl;
+ // Additionally, for use in Multiple Ciphers library:
+ sqlite3_key: function(ppDb: Psqlite3; const pKey: Pointer; nKey: Integer): Integer; cdecl;
+ sqlite3mc_cipher_count: function(): Integer; cdecl;
+ sqlite3mc_cipher_name: function(cipherIndex: Integer): PAnsiChar; cdecl;
+ sqlite3mc_cipher_index: function(const cipherName: PAnsiChar): Integer; cdecl;
+ sqlite3mc_config: function(ppDb: Psqlite3; const paramName: PAnsiChar; newValue: Integer): Integer; cdecl;
+ sqlite3mc_config_cipher: function(ppDb: Psqlite3; const cipherName: PAnsiChar; const paramName: PAnsiChar; newValue: Integer): Integer; cdecl;
+ private
+ FWithMultipleCipherFunctions: Boolean;
+ protected
+ procedure AssignProcedures; override;
+ public
+ constructor Create(UsedDllFile, HintDefaultDll: String); override;
+ constructor CreateWithMultipleCipherFunctions(UsedDllFile, HintDefaultDll: String);
+ end;
+
+var
+
+ SQLiteDatatypes: Array[0..15] of TDBDatatype =
+ (
+ (
+ Index: dbdtUnknown;
+ Name: 'UNKNOWN';
+ Description: 'Unknown data type';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtTinyint;
+ Name: 'TINYINT';
+ Names: 'INT2|BOOLEAN|BOOL';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtInt;
+ Name: 'INTEGER';
+ Names: 'INT|MEDIUMINT|INT8';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtUint;
+ Name: 'UINT';
+ Names: 'UINT';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtBigint;
+ Name: 'BIGINT';
+ Names: 'UNSIGNED BIG INT';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcInteger;
+ ),
+ (
+ Index: dbdtChar;
+ Name: 'CHAR';
+ Names: 'CHARACTER|CHAR|NCHAR|NATIVE CHARACTER';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtVarchar;
+ Name: 'VARCHAR';
+ Names: 'VARCHAR|VARYING CHARACTER|NVARCHAR|CHARACTER|CHAR|NCHAR|NATIVE CHARACTER';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ DefLengthSet: '50';
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtText;
+ Name: 'TEXT';
+ Names: 'CLOB';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: True;
+ Category: dtcText;
+ ),
+ (
+ Index: dbdtUniqueidentifier;
+ Name: 'UNIQUEIDENTIFIER';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtBlob;
+ Name: 'BLOB';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: False;
+ LoadPart: True;
+ Category: dtcBinary;
+ ),
+ (
+ Index: dbdtReal;
+ Name: 'REAL';
+ Names: 'REAL|NUMERIC|DOUBLE|DOUBLE PRECISION|FLOAT|DECIMAL';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcReal;
+ ),
+ (
+ Index: dbdtDate;
+ Name: 'DATE';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtTime;
+ Name: 'TIME';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtDatetime;
+ Name: 'DATETIME';
+ HasLength: False;
+ RequiresLength: False;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ Category: dtcTemporal;
+ ),
+ (
+ Index: dbdtEnum;
+ Name: 'ENUM';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ DefLengthSet: '''Y'',''N''';
+ Category: dtcOther;
+ ),
+ (
+ Index: dbdtSet;
+ Name: 'SET';
+ HasLength: True;
+ RequiresLength: True;
+ HasBinary: False;
+ HasDefault: True;
+ LoadPart: False;
+ DefLengthSet: '''Value A'',''Value B''';
+ Category: dtcOther;
+ )
+ );
+
+
+implementation
+
+
+constructor TSQLiteLib.Create(UsedDllFile, HintDefaultDll: String);
+begin
+ FWithMultipleCipherFunctions := False;
+ inherited;
+end;
+
+constructor TSQLiteLib.CreateWithMultipleCipherFunctions(UsedDllFile, HintDefaultDll: String);
+begin
+ FWithMultipleCipherFunctions := True;
+ inherited Create(UsedDllFile, HintDefaultDll);
+end;
+
+
+procedure TSQLiteLib.AssignProcedures;
+begin
+ AssignProc(@sqlite3_open, 'sqlite3_open');
+ AssignProc(@sqlite3_libversion, 'sqlite3_libversion');
+ AssignProc(@sqlite3_close, 'sqlite3_close');
+ AssignProc(@sqlite3_db_config, 'sqlite3_db_config');
+ AssignProc(@sqlite3_enable_load_extension, 'sqlite3_enable_load_extension');
+ AssignProc(@sqlite3_errmsg, 'sqlite3_errmsg');
+ AssignProc(@sqlite3_errcode, 'sqlite3_errcode');
+ AssignProc(@sqlite3_prepare_v2, 'sqlite3_prepare_v2');
+ AssignProc(@sqlite3_prepare_v3, 'sqlite3_prepare_v3');
+ AssignProc(@sqlite3_exec, 'sqlite3_exec');
+ AssignProc(@sqlite3_finalize, 'sqlite3_finalize');
+ AssignProc(@sqlite3_step, 'sqlite3_step');
+ AssignProc(@sqlite3_reset, 'sqlite3_reset');
+ AssignProc(@sqlite3_total_changes, 'sqlite3_total_changes');
+ AssignProc(@sqlite3_column_text, 'sqlite3_column_text');
+ AssignProc(@sqlite3_column_count, 'sqlite3_column_count');
+ AssignProc(@sqlite3_column_name, 'sqlite3_column_name');
+ AssignProc(@sqlite3_column_decltype, 'sqlite3_column_decltype');
+ AssignProc(@sqlite3_column_database_name, 'sqlite3_column_database_name');
+ AssignProc(@sqlite3_column_table_name, 'sqlite3_column_table_name');
+ AssignProc(@sqlite3_column_origin_name, 'sqlite3_column_origin_name');
+ AssignProc(@sqlite3_column_type, 'sqlite3_column_type');
+ AssignProc(@sqlite3_next_stmt, 'sqlite3_next_stmt');
+ AssignProc(@sqlite3_table_column_metadata, 'sqlite3_table_column_metadata');
+ AssignProc(@sqlite3_collation_needed, 'sqlite3_collation_needed');
+ AssignProc(@sqlite3_create_collation, 'sqlite3_create_collation');
+ if FWithMultipleCipherFunctions then begin
+ // Additionally, for use in Multiple Ciphers library:
+ AssignProc(@sqlite3_key, 'sqlite3_key', False);
+ AssignProc(@sqlite3mc_cipher_count, 'sqlite3mc_cipher_count');
+ AssignProc(@sqlite3mc_cipher_name, 'sqlite3mc_cipher_name');
+ AssignProc(@sqlite3mc_cipher_index, 'sqlite3mc_cipher_index');
+ AssignProc(@sqlite3mc_config, 'sqlite3mc_config');
+ AssignProc(@sqlite3mc_config_cipher, 'sqlite3mc_config_cipher');
+ end;
+end;
+
+end.
\ No newline at end of file
diff --git a/source/loginform.lfm b/source/loginform.lfm
new file mode 100644
index 00000000..dd8fd124
--- /dev/null
+++ b/source/loginform.lfm
@@ -0,0 +1,99 @@
+object frmLogin: TfrmLogin
+ Left = 0
+ Top = 0
+ BorderStyle = bsDialog
+ Caption = 'Login'
+ ClientHeight = 176
+ ClientWidth = 270
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ Position = poScreenCenter
+ OnCreate = FormCreate
+ OnShow = FormShow
+ DesignSize = (
+ 270
+ 176)
+ TextHeight = 14
+ object btnOK: TButton
+ Left = 164
+ Top = 143
+ Width = 98
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = 'Login'
+ Default = True
+ ModalResult = 1
+ TabOrder = 1
+ end
+ object pnlBackground: TPanel
+ Left = 0
+ Top = 0
+ Width = 270
+ Height = 137
+ Align = alTop
+ Anchors = [akLeft, akTop, akRight, akBottom]
+ BevelOuter = bvNone
+ Caption = 'pnlBackground'
+ Color = clWhite
+ ParentBackground = False
+ ShowCaption = False
+ TabOrder = 0
+ DesignSize = (
+ 270
+ 137)
+ object lblPrompt: TLabel
+ Left = 38
+ Top = 13
+ Width = 44
+ Height = 13
+ Caption = 'lblPrompt'
+ end
+ object lblUsername: TLabel
+ Left = 38
+ Top = 44
+ Width = 52
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = '&Username:'
+ FocusControl = editUsername
+ end
+ object lblPassword: TLabel
+ Left = 38
+ Top = 90
+ Width = 50
+ Height = 13
+ Anchors = [akLeft, akBottom]
+ Caption = '&Password:'
+ FocusControl = editPassword
+ end
+ object imgIcon: TImage
+ Left = 10
+ Top = 13
+ Width = 16
+ Height = 16
+ end
+ object editPassword: TEdit
+ Left = 38
+ Top = 109
+ Width = 224
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ PasswordChar = '*'
+ TabOrder = 1
+ Text = 'editPassword'
+ end
+ object editUsername: TEdit
+ Left = 38
+ Top = 63
+ Width = 224
+ Height = 21
+ Anchors = [akLeft, akRight, akBottom]
+ TabOrder = 0
+ Text = 'editUsername'
+ end
+ end
+end
diff --git a/source/loginform.pas b/source/loginform.pas
new file mode 100644
index 00000000..950dd30c
--- /dev/null
+++ b/source/loginform.pas
@@ -0,0 +1,58 @@
+unit loginform;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, StdCtrls, ExtCtrls;
+
+type
+ TfrmLogin = class(TForm)
+ btnOK: TButton;
+ pnlBackground: TPanel;
+ lblPrompt: TLabel;
+ lblUsername: TLabel;
+ lblPassword: TLabel;
+ editPassword: TEdit;
+ editUsername: TEdit;
+ imgIcon: TImage;
+ procedure FormCreate(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ private
+ { Private declarations }
+ public
+ { Public declarations }
+ end;
+
+
+implementation
+
+uses apphelpers, main;
+
+{$R *.lfm}
+{$I const.inc}
+
+
+
+procedure TfrmLogin.FormCreate(Sender: TObject);
+begin
+ Caption := APPNAME + ' - Login';
+ //MainForm.VirtualImageListMain.GetBitmap(144, imgIcon.Picture.Bitmap);
+ lblPrompt.Font.Size := 10;
+ lblPrompt.Font.Color := GetThemeColor(clHotlight);
+ lblPrompt.Font.Style := lblPrompt.Font.Style + [fsBold];
+ editUsername.Text := '';
+ editPassword.Text := '';
+end;
+
+procedure TfrmLogin.FormShow(Sender: TObject);
+begin
+ if editPassword.CanFocus and (editUsername.GetTextLen > 0) and (editPassword.GetTextLen = 0) then
+ editPassword.SetFocus
+ else if editUsername.CanFocus then
+ editUsername.SetFocus;
+end;
+
+end.
diff --git a/source/main.lfm b/source/main.lfm
index d21ace40..4f16b424 100644
--- a/source/main.lfm
+++ b/source/main.lfm
@@ -851,423 +851,688 @@ object MainForm: TMainForm
end
end
object ImageListIcons8: TImageList
+ Scaled = True
Left = 50
Top = 209
Bitmap = {
- 4C7A0400000010000000100000007C0400000000000078DAC5546D4C535718AE
- 09BFFCED7EB022529C20CE820A5591825459290502945A2AB4A032C9C63EE250
- 6CEBC06C88E0BC44C95C6024A8CB5C80586D635089F3839AB8B84FCDB2641831
- 314B9625339898B5B80552DE9DF7B607EE477BEF9565DB499EA4F73DEFF39CF7
- B3168B45A5D56AE7915390BBD4CC34D63A7C5D671A6F9F0A201CBEEEB325CC9E
- DAECADB94BB9BE9628971E8D2DAF6AABF7E0AFDB46DB20160A7DEEDF343579D5
- D49FEAE0497FDB7C88F8CCC5E37281BEAA250B7C4DCD962AA5DC28E656D8B758
- 68BE3466F358075CFCE51BB8FBF411049EFC2CA951E8F7FC8EF5C05A71EDFD0F
- AEC1D7847FFBC904CF7FDFDDD3F0C9C418CF56723C5267FA5D7DFD18797B127A
- 7EBA04C38FEFF07CBB7EBCC8EADA6FF6CCDB9CBE6EB647F4DBF5EDE7AC4FD597
- DD60BAFA218F8F36CC0B7DA88D70C7B97CF777E7587EC5B52E51BE68433EFA70
- F9381BF4DB768361F9877F1816F1D1867775B74EF0E217D66FF0E14DB833F510
- DA897FD9D811166DDF0FC157C47676729CA769EE68646712E78ADA30EFCF1E05
- D8B7B09608FC8DB6127E4DA6B20B22F38C33298CD739DE4B621862514F7E0BEF
- 53EC7956FEFC96BEC8FC7AB8F3CB9E25ECFE5870AE24B853A9B6FC1DE84BF7C7
- 22D85F7626495D9C91FDC5FE8EE36FB65605E2FD058079CC4E786BFE18290D3C
- 1B8E0DBC9B7DE0DDC9E5703173EFD303CCEB59B0B34C2782FEE372F8A8310B88
- 8F4B8A7FD99D017D6F6A4538E9C9865157862CDFBB7F0D1CDB9325427BDB6638
- DFF2AA2CBFB729137657E48860624AE0C4DE4C59FEFDCE34B8D5B63A26EE913B
- 397EE7AE755051BC4984B58356E8685827CB9F3CFE0ADC3F9A161378B7D8F7D7
- 9FAC50F4FE554F060CBCA515E1947B3D5C71CBF72FD8A70129FC97FCD0E92C98
- 1E2A5A34FFAFEBFB6076E20284FA5317F7FE401A84CE6C50FC3EEEEFF3F3E680
- 1484FBDBDCDCAC2AAB3CBACAE6F01BF4FA7C955EAF9741BECA56EB3720877273
- 0C4F1F6FDC1E0AA9533D2695CC494AF114A32F72906BADF31B74E49BD880609A
- DC1B25B846F489FA8668CC89445347EDDB82A1581A2C97DCD17730569A2F9EC4
- 14B751C7B9C7380531B3FABAED41CC93D5A735A187D5E0C4873C9A2FE7DDF9D8
- 84FC8806DF9F9BAF5AB310533CBE30DE58F9BC281F734A54C84F8A133FF658A8
- 21E40BFB2BAC5F240EF76BB1F8C2FEC6ED1FF1A11A749E79F92A9C9F97492FE8
- 2E6C5438BF6A417E3B1CFEC252CEFEC4EA914883CC2D777F70075103E350BCBF
- 8E85FDFD3F4E4242C2A2B9F5F6F216A6D3CD28D53015E5553247F65FF8C0D33C
- B8B7A1FA7DDF17BD61D490E3AD5D9D6CA8B31A0F12FF190288228C1A72DC64F5
- B2A426A771A4BDC51E3E37D0C9E503C621F9FF98B86CB9E79DEA6796D2CDAE37
- 1A8A470F0B34482E5E29BED9985FD9C71C081E7AD71A261AAD44E3324763C654
- A4AF8CC75D93BE5247F3EDEF69FD9368CC59CCB9A8318AB9603DA4DEDE949359
- C5CD35AA11261AAEA67AE388366385418ABF52B33C9DF0A6B91A7D4C6B10EB91
- AC7E2949AEEE4E7BF97BD82382E751FE0CD6036BAA80DB825CD4C0383017AC87
- D279C699543257FFC64EFC93F33731D97C4E
+ 4C7A0B00000010000000100000004F0A00000000000078DAED9A0958CDE91EC7
+ 7F27AD4E94DC622C63692AC7F64CD3E624330AA598189424310A59B2D4A4CD16
+ 85242A4B0B49752AE79C3643C254644418CFCC658C6D0CB9F79919849C4A94FA
+ DDF7FD53F7749C73FA97E5DE67C6FB3CDFA7FFFFFDBD9FF7B7BCFFDFBFD302A0
+ 60A870404543558D0A381C603BB4FB1BF41EB472C2A69119CB2FDB1D5A25A1A2
+ D726648EDA94B1FDA6F167D81E0C79421894276AA36B14B1644DA322564A8DB2
+ 7BD0B89AFDAEB998857B6F1461E2F5EF70DCE1350AE3E00E30E8D5CCD37C9B6D
+ 9E2762B0FCE1AF58567903ED0BD6B6304BCFEC4597A22D2DF7A41E1B1998D4F6
+ 55AD98F9E49BC598F7AFF39879FB740B3FE6F06ACCB97B8EB135AF23CCA5E633
+ 7A556766BEF4FE550CBE908E8E47C218AE799ECE519B541E92E6F395E6BF7F70
+ 0D03CFA7BD96339DA336595E36FEFDBF9E4471C5D956B9D36B3A476D2DF1A7BE
+ 8C9F0E13A9FACD288E66E214DE3983FEE7521889C8F54932476D2DF5F37F553F
+ 99F3A3722F89C6F45BA79833A04ABF55CACC293A3F45CF0FAD9F740D153D3FED
+ 797EFB4F1D39A38DFEE94573A3F569E99FD46597E81CB5B16E44E9FE5561DFBF
+ 6F3A021D75FD840B7B8AE42967714F515B7CAA570FB1FB440B7475B26C91CB44
+ 2BFC3CC2094FAC1E846DF182790662BAAE78958CD6F0F06EB4619BFC7EE27FAA
+ A3254E72B06A91B3A3155A6E98C0ECC3C6FFD1101E1E09961199BBBDB56DFF7F
+ 85FC4B56BDEEBF88C454C1C27FC67C0371F5EE01A848EF93AFCD1A87F557B2B0
+ 267E6087F89A4463668FF6F8A7FD93B7A4874891E0FF7474EA049C2EDAA0D111
+ 96670C9FC447C28F59F1702F2208C4DA9D41B33DBC9F0F24CE7387B53A5D801B
+ BD0E4EBB4E8239F2D6D9F2C172DB1AD8B53B0292479A8379F3BCAF1744052C84
+ BDFFD0031DC27F3F612C4C97E6C8BE9ADBD6C2969F8BA091084F1E801F2D3F05
+ 5EB3BD873EE88507421E89FF3951D3FC9910C8EDFCB2167C331876520C3F528E
+ A8695738C4D2FD6463A31FB14C0CC178F50A48257B6042245CD914009B2E1D87
+ A794BD7804EEBB4D02675267E5AF7D158065F321F8EC41C08274C6271EDC0B45
+ 838DA137AB7354014E64084450EEFC616824E752E7E106C5FDFBC0C7CA383512
+ 978D057C264E803CCA9608E1DCF4C910E4BF041EA5C442435A1C548EFB0226CB
+ FB2839F063E8714C00C584ABA1350AF081C55A9AA04A6D434C60D08E08F881D6
+ 84D6969C532C97DBBA7E51A110B37303C4D1BAE62641F1D7AEE0D1EA7B2A398B
+ A5DE1049F846BA4F5C38FC308C07C6CDF6D830D81D1E009BFBF506BDA302384F
+ F859F272B4B6007E902FEC0D590AC9438CFF7BFEC306C180E2037091C45E9511
+ 0359BA5D41BDBD3DA04AB2EDA6D3B1FE791B434B5D4543569A6A2A6A6CD8EE5D
+ D4B46A33EC1E12D5494B92EDF2C0D9429FCF92AF254269355C1761757150D524
+ 0B7DEBF6F04F854E5877D0031BAEA463E3EFA7B1FAE8B2AA8805E3BC3FFFCC70
+ 301BBEEE5B0F7C7E3EA695EA2FC461EDE5AC270ED63CBE22FE7EC208FC79B511
+ DE897794CB3755FF86D5970F548DB7E659CBE31F27DB90EF91A6782FED2BB97C
+ E3EF658C24E776DFD7D1D6D490E5ABF68DC27FC798E103C194D778193DEDAE4B
+ DE50B2F1C78FC04B2186787BD77866DD9F0277BC1D37BA959E1C5DA99897C9FF
+ 5E962756EC1AC7E86EBC033E3FB74DA9FF5A812D56A78DC6DAFC99CCBAAA023F
+ 7C20F66A5165AE0FD6966E50CCCB9C1F8DF561EEC25692C7CB0EAFAFF82EB2F9
+ DFD961A7307E453CDBFACB0EFA8C1D8AF3112954AC8FA02B57F39DBE2B4C870F
+ 32A36A0FA3A1A1A6C9E56A751DC5FF6C5458F0A2F475418B04E6A6436CB89DB5
+ BA6A6AA8697194FC1EC161B4698A8FA7634D76DAB6FA9CF4ED2F089F4BF83C7A
+ 2DDA1FF57CD1D7136B268C314F51E477C12CC79A9A1A091EC9D983B98298563A
+ 9A978CD5922AF4F1746A52C06B115B6D437D3D2A1A0D0DF5B870B613B2E2EBAB
+ B1F1D18D566AB8FF0B266E988B5E93F9AE445334D4553B29E2E9FAF63C3FEF84
+ 27CF7AC56E7BBCB3736C2BDDCB9CC592DF8E95D9F349DF7A33AACC9E87CFCE46
+ E1F3F26DEF897FD3F85BF3F45A51FFABABA9AA7BBBDB3F2A3B751C2F9497E2CD
+ 73F9CCBA67E55B5B4959FF0F31E93B75BCADE90122D166FF6967DFE4FD31C6CA
+ 842F8C9C2B56224117AEC6FFECB386DCF7CDE0A186F93BF7651DDC95225426BA
+ C6943774A02C3FDFD5C3F5DEE97F221BD1B56F9B27F10F641DFFE0D7E3FF90FF
+ DF3BFFBFFBF8F0FCFFB5F32F4C1314EC3F1C5BBF2E27009DD73B667633D63162
+ 9BBF6847A2704282FD5587FD6330FCE47AF410BBA1FD3E3B89015FDF8A4DFC69
+ 87772065A9E888207BD0EBD1D136656C781A3365A447E9ED93748F7A36F9FBEF
+ 59F8138D39FCD51EB957B27179812F8E4D1A7D874D6F7433D635B24FB193D098
+ 4FDD29C5E5477C99F88D5C0C97B0ED2F03337D2B9A2F8D99FAA52C4705381FFA
+ FF43FFBF8FFC0B920E15246EA9A80F58FE278E75389EA9AD63CDBAFF85DB5385
+ A3C6FD76956F5F8B96636A907EB5B09348BAE94F63D5FF7BB65630CCE52B8DB8
+ 72ED33BCFC4B237A2CA8C3E136D758F53F8D99FAA52C1DC74A5E30F744ACFADF
+ C7FBA79F18FFC42F652B1F36E10CEFA76836FA01ABFED7D6B536A2F9D298A95F
+ CA5A93FDFA18AE67DDFFDDF4BFB4A2F9D298A9DF976CA70FFDFFA1FF59F3D70A
+ 8BF17C4E367A4E9ED2AEFC4F6766971C4FCEC8CF8859F7306AF51C1C6F6B3657
+ 4F575B4F53434DB3ADFCC99C3032C4FBB74DC1B3EE07FB4E43BF0593709DBFDB
+ B3CDA19E4F5CBFB4096B2BFEBB27CA714FA43F861036E29B0558909484CBE64E
+ C68D411EB868B653129BFC4F085218BF94A5F71B0316BE58EBEF8646037A19B5
+ 95BF2866774EF45AAF1761DFCCC0A5C4EFE6C0C5B8788E1346867A62E0E229A5
+ 6C7AC3D3C52E7ABEBBFD5EB24725F51BB464EA451FCFF1B9A34C8DBF6C4F8FCD
+ 9A669B40EB6FF9A9D1C4F7DDDFAA6FF83F08CE86BD1CDD8DFAB875949F64D8DB
+ B5E0AB2F9A8247F0A2B8AA9D54A56DEBAD8709890DD92A7E8C79712FAE964147
+ F93CE7518F1DFA7F34B69937EFA1C71FDFFF2357650AB2E46DA76CC684113F0F
+ EDAE63D291FCE33EFF54ACA7A9D6A523F5EBA3A5A1ABAEF26EFF9144431D3854
+ 6CEFA5C72703A067412A5CA7EADB0B7AB6752FCDAEF09DE79795FC7549693620
+ 15BD6EEB9E32D27B74D602B5D0A51011EA0B11F4BAADFBBFE2CFF0B63C18B1D9
+ 8523A20A5B649BBFE69BB987A842FD6633A2D7D4663510E4FE3DCFC5025CBE5D
+ C2412A71941B1ED8B38191AFD76446E294482C0E54C1427F4E95F41E6BA6F7F5
+ 4B59FA49BE6059BF1F0AB6B962E11E7F2CCCDC8A85D9898C521336626E980D0A
+ 637DB0700587D983C4216CE6F7F91A09CB360FC563EB78582A0AC7B2927C46E1
+ A13E8CCE148BF146121FAFC65BA17801178FFA713AC4DF4C30C7B2F50399FC64
+ F9F2A8E1783C8CF082002C2BCA66949FB913F3B37662F9B114BC9968A994A7B6
+ 1BF16678837E4DB46274B3452F5965FCDDEC99F8C7F1954A45D728E2AB6F7D87
+ 2F1AEAB146F258AE18DBAD22A53CFD1B4BF5934772D5C0827F56578BF7FEA890
+ 2B6A7B1BFCA930C3DFB74FE7E4FA8E053F695E72ED103635D46163FD53B9A236
+ C9F543981FC213CB3EF72B9C7BAF4859C613A5F80D57AA7D640D7DD6DF66CFFE
+ 07F15DCBE1
}
BitmapAdv = {
- 4C69010000004C7A040000006400000064000000BE2E00000000000078DAED9D
- 075454471780E91D0489898205A5837416588AC08262C10A1A8DE68FD12836EC
- 1D2CB1A6A8680CD895A231162C88628905C4168DDDD84B127B3489021AA3C9FC
- F7BE7D0B88ECB2C07BDB9839E79E3511D9DDF7BDF9A6DD99F7ECD933AD828202
- 958DE397CE68171E3BA275EEE96DFDF34FEF58403484B083708070867083F060
- A325840B8423440B882610D6E79EDE313E79ED82CE9907377555F9BB9E3E7D9A
- 79559962AEA5A5D7DA48C7DADFDEAABE9FBD93B59F83D07158BB0E1F883C5AB7
- CA9DDC33323779AC6847D222888D913B9277411C88CC493E0E71868DD3A21DC9
- 05913B92F6C2DF6D879F4B87D759113BA60C6C99F461C790CCB171F07B2321BC
- 211A5B04D99A6805AACED7F7F5F5552E0F6708132D2D9D787D9DF75BB9D9BCDF
- C72DD8EE5A684F516EF274B8A65BE0DA5E83EBF912A208FE4C6A13F03B5EC1EB
- 7FF0FA047EF791881D494B030F0C4E7C7FAF5BBBF7C3DCDC2D626CCCB57CE1F3
- 74AA7B3C8C3FB0D4B58DF1B1B599E7D3DAA6978FB0D9F590BE91B949DFC2B5FA
- 09A2A4B6D7BE1AF12FBCDF7D78CDF12BEC3BC926DDA79DCD199FEE36313E5E0D
- C2DD8D359D87617D73DD6671423BB7D19D7B47E5242D039F5C802880EBF19702
- 19488B57506FAE423C843FEF0A593F6672D338612B70A599A6F2D0D6D3D5327C
- CFC2C836C6B769F4F6A45EC0629728276905BC3E50360FAC979139498720AEC0
- 9FE704AE19E10D9FB59E81A5A9AEA6F1D0D6D5414719BE1FE26A17B523F97FF0
- 7D0BE13E7CA3027542069FA43FC1A16982D40101461F585A01171D75E6616066
- CC7030B77D4FD7DACFDE263A27E943F89E4781C57FAACCA1927AF307D4DF34EF
- 39BD7D4D6DADCD0CEB996A9935ACAF563CF4CD8DF5DC3F6DD3F83D5F07CBCED9
- 5343E17B6541FCA34E1CDE6DF793AEC66C4B1EEE3F3ACEAEDDAA316EA64D1B98
- A83A0F7D537DADFA2E1F98D8F71585419F35136232DC5BF7D59843C5288631D0
- 3678BD189235B29FA5ABAD8D65734B95E4A1676AA46DDFDDBF4187AD833F867B
- E944A4D8C1FF69100B89BF24AFC5D1391396B45EDAC3CBC2A5B1BE2AF10016BA
- 763DC31CA02ECC80CF7A57D318C8887F804B7EE08AC44E961ECDCC5481879EA9
- A15EF3DEE19EF0D9A0DF9AFCB20EB1286D5B701E2170D99001563E2DEA2B9387
- 9E89A1BE435F9110BCB451CDDBEBDA06CEC3DC09481B34CEDADFA191B27868EB
- 686B433D7D1FC65013C1552FEB300F9C7739ED35B34F04F42D0D14CD03EB05C3
- C2BD6943185FCF80FAF1BA0EB328574792CE7A26756F8F4C606C6FA2081EC042
- CFE1535130D60BA813B335B10F55CBFED74F6E63BBC484648E4C306A6869CC27
- 0FEC4761DB0D7EDA00318ED68B4A79FC07D7E607B8368F43D247F43269F29E11
- 1F3C707C817DDA88DCE495F09EAFE1FD5ED1EB2F95C96BB68DFF2D387D789C99
- 7D433D2E79E0B8DB215ED0409433E573788F17F49A57ABDF7545B83A31D8DAC9
- 9A331E380782E3EE3A36D6E36C5D253A67E2EEF0AF3AB5E08207CE0DE27C1470
- 3E41AF6D8DE3B97055E2226B81A3556D78E89B1B6A390F086F8C738391F49AD6
- B64DB917B87A703FCB483BED9AF2D0D6D5D6B2F66B6129CA9D3259DDD62D5490
- C735BFF99F06EB591AD5B87E9836B6D26DB373546875E7CC5BEF9C4A3AED9E4D
- 3AE4CD24DDF67EC14974DD3B97C4EE9EA9CE4CFE6EB567C246C705EDCD6BC203
- D7F6705D0F586455F7BD7BEE9F47965CD943669EDD44F63C3847F21FFF5CEBD8
- FFF02249BD9CA7EEF5E4379F391FF733B032D3AD2E0F5CEFC635D69A8E333E39
- B488B4DF35837C797E2B3908D7F3F8D31BB58AC22757C9F2AB7B35612E787FCB
- B1DDECE5E5813939980782B907B8DE5DDBCFD03E6F06997B7E0B39F0E812E521
- 8EC7DE337B4F366E68A567EED0C8A02A1E981F8539396C1E08276D38B62373CE
- 65931FC0399407133F380FE9E01C923E3244160FCC1BC45C35CC8FC29C1C2E3F
- 436CDE2C32EBEC66B2EFE1853ACF03AEED03AF19BD46857E377A9DB5BFBDA134
- 1E98C389798398ABC6477E54C7DDB3C8DC735B48C1EF57549A4774EE1412B7EF
- 0BD2FD87AFF89C9BDF06F12078F5B0E04A79386B69613E2DFCEC05CC97E4E373
- B4DDF53999F863568DDA7745F040AF46E74E65FA2269977793C59777916EC085
- A73A72135E5F08370D4BD3EA57090F13E0D1CB4788F9FAEC5A382FF75DC2E125
- 64EF83F32AC9035948EAF1A4935924F9A7EF489B9DD3F8E271165E8B8419C392
- B41ABDCB0373FE31CF5C9CDBCC4F3E6DF77D5F91E5D77E50C9F60359F4857AD1
- 79CF1C706A3639F6F43AD9FBF03C19797C7529278E79E01E8A37C17B871F37BC
- 686E509107EEBFC09C7FBEBE2FF67BA79FFE1EBEA7EAF5AF703EA15FFE629275
- 339FA45CDCF1D6FB6EBF7B8AFC0F38F1D9FF0D5C36ACD55B3C60048F7B61A07D
- 39C5D7BD37F0701AF4772FA85C7F177DF459C1B764DDADC395BEEF9127D7C8AA
- EB074847E81FF2C4A344983E6411B6DF121EB847CCEE5A582F114F6B4D38F7B4
- 06BE93AA8D079105DE271BEE1C95F9DEFBA0BD4B3AB58E69FFF818B387EC1D7E
- DAECD80786121EB85F0FF788F1C102BFF3C863AB98FBACAA6B8E3F73F0D1CF0A
- E3D165CF5CB2F4CADE2A3F173A76FDED42D283BFFEEFBD80D4413E121EB87792
- DDAFC7D97B44B1F7127EE7F5B78FC871FF5F23DF8133BEBEB0BDD231231F3CB0
- FF3DFEC70C78BFAAFB7B38B7300DDA3FAC23D8DE70CCE38FC0D484A1A5F5C3CF
- 1EFBB9D7B87C0F9C6F6F0DEDC690C26555D60DECCB6CFBED24F934FF1BA6AF89
- E3F88A7D62BEDA0FFC9C38E759D51815EBC89A1B07613C32977C74603EE773F1
- 81A983D6218F1397CE683B0DE9D02192E3DCDB0105A930BE9A41965CD95DE5BD
- 77E8F165E6DE0367965E23C95CBD22FA57BD0F2C20DF818FAAFA9CB9F77E2223
- 8EAD2473CE6DE67CBC1E9A35EA54EC984FF470AF7D4391676BF0551197EFF1BF
- 830B493BF0C15770EF55F53D0FFF7E957C0B636111EB38F138651FE33045F0E8
- 5FB018AEF5E92A3FE7965F4F40FB9F4A269ECCE4BC4D0F5A36A45018D54AFBDC
- 933BFA11DBA7F6E2FA3B8E39B186195B6D070F49FB7E47C153F89A77FF2CE955
- C1013DF7CF272BAFEF6798A04BF8E4818EFCEAFC36E6F3C8722BAE197C7E7A03
- C9B871A8B47DE4AEBD4DBE3AB47069BD734F6F5B44E4248DE7F277C7EC9ACECC
- AF631D39CE5EF3CA62D7FD33A410EAC66229EB7EBD0FA490D5E06C6CE3F73FBA
- C8EBF81CFBBD3BC047DFDF91DDF74803FFE2B8BDF39ED95C7F863B0905A9DE78
- 1E08B41D8BB89D17F9922C836B37E5A7EF647EB7D53026C1EB3C001C20D57BEC
- 3AE36CE0BBE8522E6F3C708CF4CDCFBB98FE96ACCFBCF65601F4C92F91614796
- 73FD19EE271C4EEB8C67B388C4FB3738FBDD380FF43DF47171BD5BD658031D91
- 73F71489D939BDEA3A073F83731A7CCEB14F3EB996B9970E3DBA2CF573633F10
- FD89F707C79FE1494241DA503C272792E3F9F541854BC94E681F658D7BB13F3B
- E1C74C26EF4155D689861E59C6B425EB65F4B5F6C0E7C6366D29F7EEFC03784C
- C2338B80C7412E7FF708188F63DBB7BB5C7FB5B2FB6C3070FBFCCC4695E1D1F7
- D037CCBCE7A29F774AFDDCF9D0373FFAE43A53FF397EFF67030B5267E2F951F0
- E7E35CFEEED1C7D7401FF68ACC7527AC3BF8FD471F5FAD323C7A419FAE2DF445
- 66C03D22CBB3C7D8FB89E3F72F4AC84F5D8067788972924E73F9BBC79D48AFB2
- EF8873281F1F4C21894757A80C8F1E3F7CCDF40D65F5438EB1FD45F431D77BDB
- 81472A6F3C80058E19A47DAFEF591EC35588C78772F0908C9970ACCE1B0F8ED7
- 3DC69FC860EEA3631A5C3F76F1533F16030F5711C7B93D6398F6E32A3924A3FD
- D878E718E99BFF0D338E57151E384F8873BEB3CE6E92B90E834CB6FEFA23E77B
- 12603CF805F07082FED53E4EFB57475792FD0F2F91BC7B6765F6AF8640FF7286
- 0AF5AF707E19FB578B615C286BEE13FB57EB6F1572FDFE7F417F772A9EC3293E
- FB91CBF1C712667EEE7B19EB1E38FE9878328B8F7E7C8D63D8D1E5CCF8E37B19
- E3A63DF7CF317564C965CEC74D4F81C7183C1315DAF3742E7FF727ECF8FC5B19
- F719F6BDE65DD84E76DCFD89990756360B5C63C2FC1ECCCDCF7F2C7D2D043D55
- 00756426F7F5FA11F0F804CFA78DCC499AC9E5EF8EDFF725590AE36E9C7F9035
- 17947EE310336E1C7C78A9D279E01E139CDFA9EA33630E0A7EE6214738FFCCBF
- 0D2A480DC7B38223B6272770BB0E3A9D59E3C3F95D9CBF95F6DD764BEAFE95BD
- 707F4E531A0B9C3BC7399E5CA8ABB21C8B7D5D9C73C479E98EBB399FDFBD31F8
- 705A333CB719CF0A66CFA7E5ECF7630E03AEF36DFAE578957D476C4BFA177CAB
- 341E9DF6CC66DC899FE7A88C3E3AE62B259FFA8EC9E9E37A7DB0D5FAB167E2E6
- 0C33C033B4F1DC6611C7FB0371AEB0CB9E394C1DA86A6C85F71BCE772B8B07D6
- E7A93FADAF720C8B735758EFD7733F774522B74DFE7960EE7C13717E8943249E
- DBCC47DFB162BE5F6581F3D75F5DD8CAE43F28C355D8FFA82A074BD24747AF4D
- 01761C7F8ED781A9837E28977FE5CDC55EA8CA72C571AD49D63D57368F7D8E71
- 56143F396732F24B66912FCF6F912307EB3AB37E8C6E8BE73E0FAB283035E1CB
- D2FC2B7FFBC691B9494BF85A9BCE843E4955DF17C7581B6E1FE573CF45A5F957
- 634FA433EB7D557D3E6CE3269F5ACB5F0E6F6A4267098F7AC2C626C2834313D9
- FD861CF7EBA7317354F2ECC1C1399695D7F633F9257CD7136C33128FAE247932
- DAB7F2ED1CDE53717BF9D90B12B165E24DF7B15D6C4BF3A903B5B4F09C7FF66C
- 795E72CE565CDB27579E2EAE9BAC80FE0BE644F1B1F70239E33E769C63DB79EF
- 8C5C9F095D3AF1C74CBEEE8D7F84CB87E6BDB3DF20CCCD1DCFF9E72BBF1DD7BE
- E5FDFED8DE60BE39D62BC6D730BEAC6D7D8985F60CEB2A72C6F9427973EDB1CE
- 624E09E6F6F1C4E3AFA015438756E46111636BEE57F8E924F139FFDC3FF301E7
- 44B0EF2EEFBEE7636C3FF8AB0BDB985C95E1C75632F9CCD5A933E2BA308BB917
- 66433F15FBE038BE3E2A2307E99D360DFA5D9FF0B3FF03CFFFFC2F7CD784EBF6
- B95176EFEC57F3D5D2B249F7690F2CAEE299B27CDC0B381EC1FE6F75F6734AD6
- 4799FC2BF0188E13705EB8CFC114C6E778BDB15F2D09CCC1C3752564804EFA1A
- 786E863129D6B9EABC2FF6A7706E0DC7B53CD58B5FF07CDCE0AD89E95AD32AD9
- 3FD809789CF5E90E2C1E45729C5B5D7EBD07F3978EC9797F32EEFA5DBCDE8039
- 8C38378F6B14C800EB1BD6151CB360AE8E38A6324EC235256483634C6487FD54
- 6471B09A6712605DC67A25DAC14BDFE2298E3B827727EED52AAC7CBF333E1746
- 247E2ED31CBE729C305716C75535DDB77618C6C8B82F0173D2C79DC860C667FD
- A12E60E03E27EC33611D4ABDBC9BE4DE3D2D333FB2AA312AEEB1952737AC8671
- 04E2CFE055899F49DB7F8ECFE8C1E7C2E0B348F0F9177CCD6B271E59CEE40354
- 355FA4E890D45B64B10AEA14F60178DA5BFB2AECFBB1A911DB265FF4FDA28FB5
- ACF3329A7613B6C2E7C2C0BF59C257DF1F3D8379FB584FB01F75F8F7AB4A6781
- 6D3CAEA1E1FE2CCCD14327F238FEB9E83CA45DC7E0358963AB3ABFE483484F73
- 7D4B535DFF450981A21D494FF95CFF41B7E37E7B9C6FDF5F8BB34D6A7D7610DC
- 0FD8E68F81B1FA97D0FEB7E5777DEC55AB75A3D3EDE284F5BCA7F4785FDEF396
- F0193DF85C183EC6ECA5C18E29DA41DB8B7374585F145957D04F380EC1F1673F
- 9CEFC7CFC3F3BC0078E7B2DBB8AEDDAA7BFE153E2F099FD183CF8551C87A29BB
- 1FFFDBCB794C3B2CCF1C64CD3988FB4E98A78B6D3F0F7B07A4CE1B86AC1D95D6
- B89BD0AC46E7F5D95A9BE1337A30374851737C5857705C8E5C703F12FA1CF75B
- 1CAB613FE9EDBCDB6BCC1813C784384667F601E62A6C2EF94DC4CEC9C7DD6675
- 15D4F4FC447C76153E2F099FD1A3E8F34571FC807B10316F00E755313702C72E
- 393046C37BBBAA7E19CE83E1BC13AE4FAE041FCD8171048E45718C88E3200572
- 9078EA5150D6B0910DE2DD6A7CBE283E470C9F5D85FD0165ACA14AE637965CDD
- 4376C37810C773D81795672F3BD627741ED60B6C23D04D38DE9E7F713BF9F4D0
- 37BCB71395B92A6CC7D8C576D323746A731E323E470C9F5DC5C533646BDAAE44
- C1F818D75186C2B805E7B370EE7BC7DD53CC9A04EE552F1FF8FF901DF697B0DF
- 8AE72AF43990C2F4B1DB2A2FAFE8DFF02D938EBB8D8F73E2E2BC704B375B9BA8
- 9C096922157A064E343B6F1E0FFD65CC99C2C079AB2E7BE7306B1BAA74863BF4
- 536F07CCFF5F77AECE6FC767BAB55EDADD0BFA5AF9BCF67F3533FE146D1E333B
- 6854903697CF9B3077B5D50F5835A43378EB3A9B8B42B9C8AC13CCEB8B88EC89
- 1B5A8EEF62C1C7F3582C3D9A99072E1D92004C6E47E6249F12D133C4A59E5709
- F17778F6C41CB7B15D9AF0F9BCA2FA3E2DEA07A4268CF39EF15104F8EB1CBDFE
- EFB078225C39ECCBF0EC09792E89B12E8A789E97B5BF838D9EB9B18147528F58
- 78FF93F4ACFD5216BF038B458DA23CED9C06B6F151F4F3EEF03962EE63BBB481
- CFF1038E3FEB727B818E12AE4AFCA251B4979DB29E3F88CF740BCD1CF1193EBB
- AAAEB7256C7B91E79410E3ADCCE7A51A35B4320ECE1885E7D1DCADC37DAE17E1
- 5B26E6B88CE8E8AC0ACF13C667BA05678CEC8273C9A2BAF5DC5474C29FC062A3
- EBB8CECD54E979DBF84C37E1EAC41018C7EFC1BD897580C5BF5139C9B7DBAE9F
- 34CB6D623C270F41E7FA79F4F84C377C8E9870D5B045F8BC24364FE56F0D6280
- 7D9647383718B175D289902FFB778F8F8FD7E6EAFA71CD4352DEF377B00A593E
- B49FE0EBBEC1ADB74FC1B3837ED380B6A5283237F97848E68851E1DB27A67A8C
- EFE6C2F575E38B07F3AC8400176D037313ADE049BD2C7CE6F4E907DF673FE671
- AB2107CCD7BC82EB7A1E9FF71434EAE4AFED3CB1B30E1FD78C4F1E6FADFD5A99
- E9E2337A604C3F292A2709F7BA3F50F5FE31BB7FEF22E61EB88DEB1A276D8D55
- 1D79943EEFA5A1A59ECBE0764E5E333E1A15919BBC0DBEEF4DC939FF2AC0E01F
- 368713736A8E607E14E6E4601E88A2AE8FA27958D83732085D9D181CBA6EF4BA
- 48711D79C1B278A3227D573CF3169F05FC27E6AA617E54C59C1C4DE2513A27E9
- EF6014943E2244B829314D98919814B277C471B66D295142BB8FF5E22FCC33C7
- DC66CCA7C51CCEF279839ACEA3B4E073611A6969E1F32F82970F89102E1FBA28
- 387DC4193CCF9C59C711F795B95A6B91B457AFD93AF918F725E15E18DC7FC1E4
- FC639E79A1F22E87D2795436AE6CFE816140EA20DFC0B4418978867668D6C853
- 7856B02897C903BB2D12EFE1C2BDC07FE01977ECB52D2E17CF58964FD9B1C26F
- A2DCE41B61EBC79C8EDC3EE9E780B484BDB87712F7EBE11E3155FAEEC8E3F4E9
- D3CCABAA46FBB17DF482A2C2B4071F5F5C6F507E9AE7C082D4CE780E279EFD08
- 3133213F6DC1C0FCB4343652E1BFE74224E1D92C107D130EA7450C2A4C6DDAF5
- 8BC106FD77CF3551E5EFDABB776F2D4208E7F1DFCB3FCCDEDC3BEE0AE1F2EFBD
- E36E1A198FCE3AF071EDF888D7B7768714A5DA1D84F8A128ADF9514D8C927591
- 1BD485C79B5BBB3B028B971045F0D9892606F0B84679501E948766F27896DA9C
- 3C5AD4823C58A85EF110E2C9372D348EC7DD147BB276A43B491DE4A956B174B0
- 07C99BE8A2713CAE7EE94086C6FB91CE31812A17B19D84A4ED87A124B6A3F09D
- BF8B6F1740BEEEEF4579F01C1DDB07914E6D03814318F14CED4A1CB23F22FE73
- 3B920E5D83290F2544CC476124365648FCBEE848F45E8F23BA643CA9F7CB5012
- 323186F25042B48B0B61EA48F0C4B6C4E0EFB10C8FFAD7069388A15194871242
- 34209274E81C4C5C337A302C304C7F1F417C1774A63C9410D87E77EC104482A6
- B523264F4712C3E2D1A4C185041236BA35E5A1E4BE9560562C71C9FA907117B6
- F17581C7F5AF1CC8989EBEA4576C805AC5C79D0464D1404F8DE3711FC6B95BC6
- B9918CE12DD52AB246B42407A738D3F92B3A7F4579501E9407E54179403C5DDC
- 829C9BE3484ECE7052AB3835D389E9ABD3F1876A041D0F2A783C182B246163DA
- 9080991D48C49028D2A91DE5A1CCC0F178FBB810D2B667E83B73ED9487E2D73F
- 5A7F1C4E9A1CEC4BEAFD3A8C386DE845DAC587501E4A88E8BEE1CCDC95E7926E
- A5F3BBE60F8613C1EC58CA43293C22C43CD2BA11BDD7E2F50F8B7B89246016E5
- A1145F75607DF5BF56A4C9A1BEA4C1C504E2B4B12769D72394F250761FABA390
- C4F40E6318D595F6FC97F9F66451822799F2B1B75AC5E79F7893EF47B96BE4FA
- C7B85EBEA47747815A45DFCEFE647182E6AD7F608E1FCE3D144E7756AB380271
- 69AE239D4FA4F38994471DE2F1D7B7E236FDE6D70E6A15B720EEA5685E3EF5AF
- 0BECC9B2211E64EEA7DE6A155F415F77CB58373AFE50F07C6274BF083A9FA842
- 738B3877D2A95D10E5A1C47A81AF98331A322186787EDB95B41A11FDCE189DF2
- 50503E7577365F34B91DD17F259E4FB4BA3184840F8FA63C9410519F89F3A9DD
- 56772F9D6F377B3C9CF8CDEB447928A37EC48730F5035D65796B2893DB6E73B4
- 1FDD6FA0ECB63C56C830F149E9C2B8AAAEE453AB6C3E436721F19DDF99D86FEF
- 4D02A7B7AF333CAE7DE54086F7F02371F0FDD4297A760820F33FD3BCF9DD5FE7
- DB9325303E9F0D635E758A2FA06E646BE0F89CCE27521E9407E5A12E3C1E2E6A
- 41F64E7661F648A9536C8338365DF3F647FD952A3EC304D740D42DF04C193AFE
- A0F9ED7572FDA35D10737646F4A711A46D8F503A3E57620E1C5EFBA8FE91C4E6
- 587F2657D47E5B6F12D32B8CF25046FEEEA7E2FC5D8F657165F3BB0F8713FFB9
- 347F5719D1A64F2BA68EF8CEEB440C5E8E21BAFF8D279677861261723BCA4319
- ED06BBFF23A667287159D783D8EDFE98782DEE4ADA77A3FB3F9499DF1ED52F82
- 585F1EC4AC7F34DDFF096D3F54203A741692D69F86337B09EB4A7FF7C6D70E24
- B98F0FF9ACABBF5AC5A0383FE64C4B4DDD7FFEE30C27B58A931ABAFF9CCE2752
- 1E9407E54179501E9407E54179501E9407E54179501E9407E54179501E9407E5
- 4179501E9407E54179501E9407E5519B285ED282142F7723251941A4385D404A
- B24299285EED478A973A521E0AE4C1B058E34F5EEE194AFE399F495E9D5E4A5E
- 5FCF25AFAFE5905727169017D95D48F13267CA43513C56B4242FF78D20AF6FE4
- 91D7B7F6BD13FF9CCF202F3675A43C14C403BDF4FAD2FA4A593071338FFC5D30
- 95A923C52BDC280FBE796408C93FE7564BE7716317F9FBE044F09A3DB4250E94
- 07DF3CBE8B82EBBE573A0F8857C7E791E2555EA418DA7BCA833F1ED896BF58DF
- 1A9CB45B0E1E3EA4243398F2E0DD5741E49FB3ABA4F3B8BE93FC7D6002E3AAE2
- 654E9407DFFDABE5AEE4E58EBEE49F9F378ABD7573CF5B6DC7AB1F53C069D1B4
- 7FA5C8F120F4795FECF81F78E96BF2EAC86CF2EA542AC462F2777E3229019F15
- 2DA1E34145CF9730E3C2951E4CBF16FB5C251981BCF889F29097873D2981713A
- 8E314AD6B7611C852EA33CE87C22E54179501E9407E54179501E9407E5516778
- B40316BF433C80CF5EA489013CCEAA0D8FBB475D4B36769853B2A1C3CC171B3B
- 7CAD89F1326FC0783EAEDDE3C78FB576EEDC5965EC3F704977F7EE43DA474FBC
- 3138987F43E7F0B1677AF2FCBBBA18FBF69DD4CECBDBAF75F4D86BFD8385F7F5
- 0A8E171BC8F3EFF2F3F399D7AA8AA151335D17AFCDC2660EB36D03A38A47BAF9
- EF6EE51B7EEB23EB0FBA1A68D1F256D1D3B3D676705EEAD8ACD9D4FA4111CF07
- BB079FEAE61D797FA0B5EDC7C655FD5B575757B978E8E89868377598696B69DD
- C64C185D1C60EFBEB4B177D8055703A3C63A94C0DB455BDB40AB51A30196F5EA
- 85190A5B3DF772F4DFE9EC1971D3C7C0D84EB7B63CB05E200B5BBBF1502F4ABE
- 098A2A49081015FF1A105DB45C105574B465D06181B1A9938EB955B05E5DE7A0
- AFDF405B5BDB50AB61A3FE9641E1CFC7018B1E82A8E29B82E8E25DF07AD123EC
- B2C8D4DC5BDFC2AA95414D7894396A16B2580C1C8A21FE08882A26F0FB8BE1F5
- 3530392C76D7ED5EF5EBB0BB1847392D756CDA2C191D3505AECF53880778AD20
- 5E41FC1B1055F2B357D04FDD04E1F707BEDFA87277C9E251DE51502F06322C44
- C577591E4FC5EF53B4C1BE25E32E1703235B9DBAEDA881E0A850C3E056CF3AC0
- F5F915E21ACBE305C41BE071D8CD77978B7FD82D5FBCD7E5E55189A390C513F6
- 77578C17E0AE156277150A8CD05D9642BDBAE7A87EE51DF5AB946BF51AAE671E
- 70B9E01B72258A71976598812C1E124761BD4016E51D252D2A77571783BAE1A8
- 25E0A8242B7054720547498B8AEE1AD0A0511F63693CB05E607B5199A364F060
- DC2560DCB5CCD63BEC629D7017E3289B044B8B3247FD56CE5144AA4F5877B9FB
- E6A1BB7CCABB4BC2A382A31657E1285295BB3C4ADD15A4A7B18E6A58EAA8EE32
- 1C45E4739717B82BD40079E0B8BBBA8EAAC25D6FD05DEE7E7BC27C5BDDEEF9DE
- 7B5DF43587C57BACA32657C751B2DD252A75D767E1A2D9C6380782E36ED65109
- F2384A0E776DB46FB9DCD607DD6560ABAD798E0AA98EA364BB4B04EEF2DBCDB8
- CBDBB7ADEE91136F0CE0FA8F0814150BE0F5971AFEEE77DE2B30B2684580A8E8
- 8897EF113F6363471D0B8B205D0D72547C0D1C25B59E044615AF866B9FDFABFF
- 8BF7F61FBAA1E3E8B33BB4A9CB121BBFC8E7CBE0EF8B6AD0764875173039DCD2
- 6B4FA87FF0ED1ED6D69DF5D5D6514D27A1A3926AE9A8777C05BFEBB257C8F95E
- 7E91F78747B55F6E947FE499AE67C8AD9E6E81E79D05A2A2426C6BD89FAD2D0F
- F19831B268A393F30A1BFF808B4E060636DAEAE9A8419616169C38AA7CFCCB5E
- A7277E910F6708447FAEE9DAFB9219F6AF2C1B74D5D737B4D576F63BEC0F4C0A
- FC238BD6C3CF96F0E52E73F3405DF571D4A77C380AEBC5158887C06256138764
- 63078F55A6EF8C074D9C58772DB5F1E7D75DDD55D95D124735693AB1BCA3EE73
- ECA88FC051236C9A8F3292361E34AD17CC97BBB0FFFC0FB86B9393F34A70D725
- 95765799A382F974D4E7E0A87417DF4D66B2E6AFEA35E82271971FEBAEEFD9BE
- 19C7EE3AEA2B765780AE0A3B2A0EAEDD2F1C3BEA51A9A33C579BCA3DDF5EEAAE
- 654CBF0B9DC3B6D1DCB9CB5BE2AE4EFA2AE228A7264D27A0A326F3E528FFC8FB
- 236D9A8F36AAEE7CBB693D21BAEB43B7C00B4EACBBDE70EFAE55E82E47557017
- 3ACAC666B0C451B1ACA3AEF2EDA8EAAC0F96BACBBFD0CF1FEE67880D1CBB6B25
- BACBBBD45D025D6539EA83324775E3D3518E151C551D1E15DC15D6D465B98D9F
- E8F90ABEDC2508BE1D6F6DDD515FB18E4A736AD2643CCF8E7A30CAA6F918A3DA
- AE9F97BACB2248D703DD1574C109EAC8111EDCB5D9C96535EBAE46DA8A75D410
- BE1D355D96A36AC243ECAECE15DDB5911F771DF33136760077F9EB2ACE51CFF8
- 72D46CB1A3D698CAF3B9AAC3A3D45DE079A6DFE5FA96BBFEE0D65D7BD15D717C
- B84BE2A8C64DC659018B49780FF3E4A8D1B67238AAB63C4C2D02D15D3DDC822E
- 9677D73F5CBACB9971D7CFBCB88B7194ED5045382AC3D9A76A47D59607E3AEF7
- D05D36E5DDB5894377BD2C75971F77EEAAE0A8F1E0A8AE3C38EA7199A3D2CDAA
- FB196BCAA3A2BB9AB9AE5080BB62F56BEFA8B17C39EA0AEBA831B6CDC71AD5F4
- 73D6968789D85DDD797457B6B3CB1A7497436DDC55E628A101EBA8BB5C3BCA3F
- F2E1B49A388A4B1E627775D2D73740771DF1E5C95DABC4EE3ACEB8CBCCCC4FB7
- FA8EEACBBBA31AA3A3BC32CC6A732DB9E0F1B6BBF680BB56A2BB56F2E4AE1070
- 573779DC55E6A831E8A889ACA3EE71ECA8DE8CA35A8C33E2E21A72C9C3C42240
- E22E478EDDF56799BBD2E57697D851C3248EEAC8AFA3369AA91A0F29EEDACC93
- BBBC8D8CECC15DBEBA7238AA0B7F8E4A0247659A7175FDB8E651E62E07D65DAB
- F8725721EBAEAEF5EB77D07FC7518D475BF2ECA8B15C394A113C4C2C04E0AEDB
- F16E4197F872D71667970C70D765705743EDB71D95C8B7A3A682A332B9729422
- 7888DDD511DCD548DBC5FFA84F3977BDE4D05DABC5EE3AE18DB95D188A729493
- 5796191FD78C4F1EEFB8CB6D356FEE12B67ADE078367478DB36D31DE88CF6BA5
- 081E26E6FEE8AE383EDD1512F6CC15031CD549DD1CA5681E52DC95CDA5BB0244
- 4CEEC735666F23478E82DF75055E1FFB8783A3ECF973943278BCEBAE35E8AE55
- ACBBFEE4E81A12AE1C852C7C02CF7F24087B30AE4993F1868ABA3E8AE66162EE
- 2771970397EEE230FE655F9F08C21E260B22FECC7073DB60AAA93CC4EE8AD5D7
- 63DC75CC07E74070DCCDA1BBB870D4EFC06276D3A64946AEAE6B4D14796D94C1
- 4352706E10C774380782E36E25BAABA2A3C6376932C15019D744993C709E16E7
- 06713E2A40B9EEAAE8A84C453A4A557860C1795A9C1BC4F9281C4760DF55C1EE
- AAE0A8C9E0A875A6CABA1ECAE62129383728765706BA6BB502DC55D1511394E5
- 2855E481F3B4383788F3510A725779472529D351AAC8030BCED3E2DC20CE47F1
- EC2EA65E08C42C94EE2855E52129382F88735138FFC18EBB39E5011C5E08229F
- CF13888AF6B6F43964A64ADF5DE57944F1C32320E2F97CE4E1E94D79482BB896
- 84EB17EC9CF9449C1BE4703EAA7CFCED2F7A9E013C8EB9087EF434306AAE6364
- E6A14B799467D18059D7C3B5245CBF60E7CCEFF2D59E4BE6EAFDA19D72F2FD21
- D823E476AC79FD183DCA43B2AE67A8856BACB8AEC7AE25DDE368CE5C1A8FBFB0
- FF063CB6376FF99D8D9BF0720B3D830FB4EB3A8F4A1CD591C3753DB9DCE5277A
- 9E8E739B2E01273D187799B6D4AD8B3CCA1C354C218E92D35D42705707F3FA6D
- F4EA1A0FC6514DC65829CA51F2B96BBD8DBB12DDA50C1E2AE02859EECAA8E02E
- 1D4DE651E6A8A14A7554F5DCD55A4F5379881D3556E2A8AECA72941CEECA69DE
- F27B85BB4B513CCA1CC5EC8599C4E6FCFFC2E1DCE00B36FEE6CE5D458CBB5C03
- 4E81BBECC05DEE3A9AC0438AA37EE372CE3C30E2F93C88F981914519EC5CFD5F
- 1CD495923277ED4777B5E7DB5D8AE02176D4382BDCAFC73AEA3E97F951CCF943
- 91457B9910151DE330BFEB5999BB3680BBAEF0EE2E3E79F0EE28F1BA1E3367EE
- ED7DC80CC3C7EFA42733570F7D579EDCD552EC2E371D75E251EA289B21CCFE6E
- 3E1C255ED77B9884EB1765EB8CCD753CBC7F0876755B8FEB8CE95CB92B40726E
- AAA8E830B82B08DCD5D6BC7EB49EBAF04047E11914ACA3BA71EE2866BDFBC104
- 5CD72BBF96646AEAA12B08BE132B08B8D282D3754691B8DF25882CDADEC26323
- BAAB391FEEE29AC75B8E8A783E994F47E17A77656BAC5656317AFAFA1F68FBF8
- 9FF2E0C35DFED867807AE21AF093BBD85DAE3AAAC843ECA8254E786611EBA8A7
- 3C392AB9BCA3A415B1BBF60B5DDDBEB7E1B2DF55A9BBACA2F4548D07E3A8A613
- 58473DE7CB51E32B3A4A5A61DDD5814777E5B4F0D8C4BAEB7D6D55E121715443
- 253A4A5AB1B26A53D15D39FCB9AB998E9149EDDC555B1EACA31CF1EC47FE1D95
- 54E3BD3065EEDA207157093BBEE0D05D0702C15D31E656223D65F1103B6AA2C4
- 51713C38EA31E6FC639E796D729B4D4D5BEAF0E4AE6765EEDA5C6B77D5944705
- 4725B167A2F2E4A8F1865CE4AA29C05D9962779D66DC6568E2A2A3081E0A74D4
- 94DA384ABABBECC4EE72DFC8B7BBDA9855D35D35E1818EC2B3E5F13C73F60CED
- FB5C9E15CC3A6A2C3ACAC5258BF3FD172626EE3A02E19DF63CBA6B470B8F6CB1
- BBF4ABE7AEEAF0287354BFF28EE272BFDEE5B71DB591B71C4E2BABD68CBB7CFD
- 7F6AC9BA6B074FEE7233306A0AEE72D6E1924799A312D4D251B2DD7540E8E6BE
- 09DD95C9A1BB4A5877153AFB1E4477B536B38AD4E38A07E3A86693ADF0B930AC
- A31EF0E0A8317C394ABABBDCC05DB7DB0902AEF2E72ECF2D72BBAB2A1EF84CB7
- 728E4AE6D751E37875947477459777D7111EDC952576D7992ADD258B87021D35
- 55918E92E92E9F03416EEE9BF97397DFC1008F905BD1E6F52374ABCB43ECA824
- 2B7CBE1EFB4C371E1D9569A26C1EACBBDAF2E8AE5C7BCFAD32C78C95F1287554
- 23DE1D354B598E92562C2DA358779D7667DD95CBA5BB04A2E7E8AE8296C1A75D
- C05DDA46264E3AB278881DB5D4119F8DCBA3A37A018B69CD9A251B69A9683134
- 6CC6BA2B1BDD95C583BB0EBB0A0E0ABC5BDD1299D70FD795C643ECA8E4FAF5C4
- 8EEAC183A31E81A3468B1D9561A2AA3C4C4C5CD15D31E0AEE60165CFDAE0CE5D
- 50EF1C7DB736F20CBBDCACBCBB243CCA1CD51F1D35855F478D05476D32D552F1
- 2276D7FB0A70D7197057137097A30EF2D8B7EFA4B683333A6A407947FD5AD71C
- 25D35DBE0783DC5A6EB109882E5ACBBAEB3997FD2ED700C65D9101218374F3F2
- F66B356B36151C15C6A7A34681A3D25D5CD24DD48D8789898B8E2018DC1578B5
- B920AAB4DFF59A03773D97B8CBC9771BE32E4FAF70EDA3C75EEB83A306030B2F
- E070930F47356E3C462D1C25ADD4B316E9E983E7BD42CEB44426103B3974D74B
- 70571A70D9D3BDDF73F38385F7F5DC834F7573F4DFE92C882EDEC57AE605478E
- 9AAE8E8E92EA2EE3663AEE8107835C7CB7DAA2BB026AE72EAC67FF05884AEEFB
- 86DE1E27087F92D2AE735EBD82E3C506DE91F7077A46DCF481DF7D91F5CC9B5A
- 3B2AF4C1487494B3F31A134DE1616CEAACE317713BC637FCAA3DD491C25ABAEB
- 3FF6F5B920E2E92241E4F3ED711FDDB1C6FE95B5EDC7C606C676BA1E619745C0
- E46788821ABC8F46394ABABB22C5EE0A2D75D7AE1AB88BA91762167FACB6735A
- 58CFD57BA775C5F187B18537BAABABA3FF2EA76AB84BA31D25D55D464DC15D87
- 825CFCB6350677AD93D35D6F3B2AE2698A9D534A3D69E341B3FA61E8AE01E02E
- 6FF8DD17E474573947DD1F2176D46A134DE7C1BAAB4D35DD55DE510BD151AE3E
- BBAC65CD5F89DDD5ACBCBB0ECB789FF28E9AD9B8F1688D74945477D58F60DCE5
- 1D7AD6BD9CBB5EC9E7A8947AAE3E3BADE59DDF35367FCB5D7915DC55271D25CB
- 5D2DC15DAE7EDB2B735799A3C218472D6CEE5CE6287979985985EA83BB3E93E2
- 2E89A31E82A38683A3D6D4054749779713BAABB514779577544A0038CADDF7ED
- 7A21EFFA607D9B3E1277459673D7ABB71D35AA4E394A5AB178D75D794CBFAB9C
- A3B05EB8FBEEB2AEEDFAB9B1B917B8EBA7AE4E823C2760B20A1CD513587C5E97
- 1D5585BB025DFD721A0744152D651DB5A885F3C27A55FD5B797998598580BBEE
- F5F788B889732AF9E0A844B1A3569950026F17235347745734B8AB05D491BD82
- 88278CA3DCA438AA228FFCFC7CE6B5AA088D9E65ECE5DB56B77BBF92FAAD5B2F
- 378C8FBF642ACFBFAB8B11143248CFD33B42BBE767C5961DBAE4D58BEF73C75A
- 9E7F171B1B4B6F685A68A185165A68A185165A68A185165A68A185165A68D1F0
- B2796D4A2B428836BD12CA2F5BD6A60CC8CE4AF92F3B6BE172CA4435586C59BB
- 90605026AAC38232513D169489EAB1A04C548F859807FCDCBA45E1F4AAA9080B
- F8797AD5DE2EDB33E73B6C5DB7C84F9ED89231CF89B2E0AF6CCD5C18B7252BE5
- 8D3CD79009FCD9AC945E94050F2CD62DE8B8256BE12BB95948614259D4BE6467
- 2E6A5B2316159850161CD48BAC8551D96B534A6ACC828DAD62CFFD4E59D486C5
- 82302E58948B373086784259D4A02FBA2E4598BD76E1730E5954C9A4AEB3189F
- D8B5F1A4E1F1472625C6BB95FFFF9B3216F8437BF1170F2CA432A12CBA369E9C
- 18777DF2F078024C1E49986C5D9BE20D2C9EF2C8E21D269445190B492093B479
- E33A413FE8B10258943211B7F194457916929832A6E7EF9B32E7DF50200FA963
- C63AD35E24C65FAB8C0565A2D8327648F78655B1A04C1457A64DEB6E3029316E
- 8B3C3C94C9647BE637CD2913D561B275ED7C0F4D6E2F268E8A775027269ACAA3
- 5CDBFD5BF2F06E8EEAC244137954D28F521B269AC643469F562D9868120F5963
- 3D7561A2293CE460A1164C3485075CCF65F25E4FF8D9BBAACA4453784CFBE413
- A3C9C3E3F2D49D8926B51F9AC044D3FA57594B67F9CE9B3EF8A43A32C9CE5AF8
- 5A93E64BC46B49298FB3B352FE9EF779ED99C0DF652B8C0993BF92D24D635860
- 3E60B9753D7562829F155E35E66024CC3DA86CBD5B1D9860FECAE67529311A53
- 2F989C1CE97920AACC8461B17651A4E6B0902F574D1599E03D84F792A6B06073
- 385F54C7D1D5653271E8874EBC3041B78263358645564AA79AE4D32A83C9D4D1
- 3D9FBCC504FA1CD8F7D01C160BE26B93DBAC5426D017C73EB9268DF7AAB5FF42
- C5982CFD7A4C474D5B6BE26A5E42194C260F8F7B9C3C32CE9DF2A04CD48187B2
- 98600E37E5A11A4C98B5E451F1B69487F299681A0B3E7970C904AEFBE64ADA8D
- AB9AC6826F1EFC31D14C168AE051132670BDEF4967A2B92C14C5834B26D30675
- 7D5F4B838B22739BB960A2E9654B56CA1D55675271AE5E93CBD6F52976AACC44
- 13FBB4EACB44B3DBEEAA986C5D9BF244E14CA4E612D55D160C8FB50BFBCB7B36
- 0B87FBC91EAF4B9BEBF76ECE1D65A16816F07E7F48D692DECE83A42C145F2F16
- FE959DB550F0D6D8029840DBBD64DCD09E36948502EB05E6126950EE0157253B
- 6BC1678A6781F95129ADE8D5AF390BFC39A8474F3870D48BCDEB1644D3AB5F4B
- 16EB160DACF5F8449CDBDC8E5EFDDAB3A8F59811586CCD4CE942AF3E772C6ACC
- 047389D62DEC4EAF3EF72C68A12C280BCA82B2D0FC82CF50A0E736AB4EC1678C
- E0B346280BF5614259A80E13CA4275985016AAC384B2502D2674AE9B165A68A1
- 85165A68A185165A68A185165A68A185165A34BFFC1F558FF11B
+ 4C69030000004C7A0B0000001800000018000000381000000000000078DAED9C
+ 095853C716C72709040221A0B8146507412A0A2D8AAF16515CA80B20EE4AD52A
+ 5A91AA805B59055C40F68A02EE8A2C6E08A81537D02A6269C556FBDADAD716DC
+ 157714655190F366AEDC3484242417F2D5D797F9BEFF97C9DC33BF997BCEB933
+ 631244E8EF2D9C2EEA5CFD8F2DED2DE60C9F63396F643091C5ECE1733A7F6C61
+ 4FAE31E56A1BEAEBDA0478840C3EB2BC62D8D7612049830F2FAFB059E211A285
+ 6D15611B7BF477199A17745D1A575CC496F491876D367DB017EE532F2F9B960B
+ EE633ADDD9ABAD79BB30608B8E21ED3E880F457DB2FCFB7488FEE9A050F38A53
+ E41A63487ED0752DA3D6F120B114B59B54180BC7EF5D81EF9E94538ABE725022
+ 6FC4D195ADDA302B583C07C5F3C4B52002726F7D2F93FF45C966C8AC28A66CC5
+ F2AA5C3477497E8BF70D29CBA2B8C7EE5E8663F72EB7E28FC4CCC3B7CB289B90
+ 4B59ADC6C64C3B9A4F9E17F1EB39374BA9BE7EA5DB295F2DC3F110B70928DD41
+ D9105BF16B967386CFA6F9E49914BF7EFED1EF943C4E44498D25B946DBB5E263
+ A62C7EC9E33FA87EEE27D64AE5936BC4A6E4716B7E2F11BE24FFE4DFBA48DDBB
+ 2F8EA134FE17255B289B3C6C2BCB3F92E21BF1E33EAA2F1967CCF1D5ADFA9336
+ 7A0EC4B6557C1D2CEC5AE4E7E196F939EAD82A3872E752F3FCBE87F9E7D36034
+ 6E2322F5BCE6DC2536C4B6557EEAB45C5BC59F2FA2C94571F0F59D1F84CFC0B9
+ 87BF51A2DF936BC4A6F5F3E51EDCD6FA408BCC77CDE51C8A558A99A5CD5CD236
+ 5A6CDECDEBC3359E91BE40D21A64344EF6FA36B2209C928CF5AD0EAF6F4365AD
+ A1648D7561B43E87D6994D779EAEACFDC5D46DE05006FB63B033CE0519FB6339
+ 89258E9D80F1FE8EF34CBFBFA51DDEDF6793679288D4F5FB5BD889E7E0FF53D1
+ E2B2B8FA7CB68EBC52941FEED129A42AC5ACFAE10673997A84F522CDAC5A71BE
+ DEAA3D4BFAC074B7011235CDC3119C93DC60D6F801F034C50C98F0B302FAC0E4
+ 318E92E536100625B881D73847C6FCE7A96654DFB684FDC3889FEE670BE33E19
+ 28511EA30742FF58779884EFE3E94666FC6CEC9F69631D256AAABB233825BAC1
+ 0C4FE6FEC1F90137132D64EA56920563FFA8F2E79F9D3F78FD01BCBEB429A6FE
+ 21FDE4958A6F06B5073DA1E1CFA3D4ABD2F8BF1F521A9FE8E5E65E1DE49F4E21
+ 64DF9357EFDAFEAE2A924B673DA4D9A533D2EA68AE8539EA14158432B2D3D083
+ BD9BD0A375A128B78F1532E808369B8DD0DA209485B9758BBD514CC0E72896D4
+ 53D7A1B3BA02C4969763668C385C09276A1D3ED222730E5B8C36D16D8BE7A2D5
+ B8EDCD800F90B52C66B7CE881BE68796971D45BFFC5C886A7F3886EEAC5E8696
+ AB71108BB6E16B232EF6CBADB895E8A8BADADBF9E2FB5885F9AFFBF446A6D2D8
+ AE4351DF0BF9E8C75F8A10085588AA027DD14CCC6F51BE98838230AF2926141D
+ 0D988F9271BD36250A156848B85F810E528BFE12F963DE4B51F6A96C543A6220
+ B2923417EC23F6ACC9C86777322AC76C6856CDF20528A64777A447DBD9BD8F8C
+ 4FEF43275ACCB908356C8941B1065D91665B3112F09186BB2BF2D8128F2ED0E3
+ EC5A8F6E4C7643D3433E47D37F3C8E1E89B289BF674C40A3343414CB294D0DA4
+ 16BA0405627EE381AD084AF25113E63589B24FEF475FDBDA30CF61926BC17E68
+ 655901021C47D8F995905DB776050AD0D6429CF63C175C75C4DA14851209F3DF
+ A710941E46703607D505FAA36AD7E12804C75E8D29DBD000F1D2A25082884F1A
+ 710E3F5B301735FACC450D49ABD14B9C5F272D4D91A1225CF39E4837602E9A87
+ E37895F6F3A563E8DFB327A3117DFBA0B17E0B50C5F624544FC73E6303BA37CA
+ 058DE170DA5CD7D437C7A2B09F4FA2873417FBE449E432B4B4AB3EE209D72953
+ F45E42243A2492C3448D810B514C173D24358B9257A168CC7CB33B09A5AF598E
+ C270FD197E5E0A718C59E2B6D8EF1C9F596821E6BE141D6763142AC1CFBB85B8
+ BD9E0069E0B5E36EDE769443F322025008F1F92087D6F67419608F6CF1737259
+ 748CEC54F4C4DB0B4DE688AC8A7ABA8887590FB6C5A21DE43D0B8F901A85E209
+ FF230764D9865F35027C5048C646748DACB9F8F5FACC496886289FC4266B03DA
+ 8E79AF713EEEC8DE88769167FE78263A29C93F129F474D6A7DD6D2D6966C8FAF
+ 0932D6A31D38A6F731FB51CE26B4EF036BD4B5A3F7B1EE5D90A641B78EDF1F55
+ 4576D1D356D3889D61F92556B834854E349DADC661B198F08DF4350435D9C3AA
+ B040926AF7BA42FDA945B027C036429DC1186DF2F77D024DCFAE427D514053B6
+ BF6D04574DB131E4E2BF2887A6EA3F856328721FD2F8B57993A1FEB82FD49F5C
+ 048DD78F41E38D93D0587104EA0BFD9B2E6E9B975EB07E41F887BD0D4D99F2EB
+ 4EF8C2AB8BEBA5AAE1D72C787436FE3A1EC34C5EFECB4C177891318452EDB105
+ 32F98D374EC09B0717E1C199D80A87F78DCCE5E15F8BED07DF2EEA41E9C6B6C9
+ 6DF29B6A6E5263DC3F135BDEFF7D638BB6F8D5BB8740D54E274A2F8FCE97CDBF
+ 760CC7BD82D29B7B17A0F274CC557D5D6D9ECCF9C7F4850B0B0D285D6F63FEAF
+ CB36C0EB1F52FE5259F273A3F73AE9C98C6F960B8EC1504A75C765FB5F82DAE4
+ 9747DB42B14F374AD7B64CA4FAD59726C0AFA1A6F04BB0A14455664C959BFF22
+ 63283CDBE54CA9A6C04738B7DAF351AD547721068F1D0FAFBE4F929B2F297FEA
+ BF4B80FFACB681DF22AC5AE87EB697C2FE217E273944249AFF35E75643CDD996
+ AA3D1F0D75DFC6E1F113E5E64B7A7EC9FC7F8BB486ABE19612753FEBD376F1E9
+ 31A4A92DFF4B2BA9C1532265E54FE5EE2972F9BF2DFEDB31E25B4964DEEDE6B7
+ E7F992C10FC67DAA15D01DCC97FBF7227C1E97ABAFA7ADA380F86C664700A515
+ 369BCDE272D535381C76874FCCA07B177E4A42485E5ED6FACA8DF1C1F97ABA3A
+ ED3A9B6A6AA873F95A9A824E7A82AE44BEDE53962645AF386C6264D0EDAB755F
+ 1E9939D56DE9DB6B3A5DF9DA9A029E2657EEEF20FB58198D9F3D65D88D795E23
+ AB727627D611E566263560FE29CC37C5FC22F29EB4EFDD115BBB60D6E867DE53
+ 87DFFCC0D6DC539E7913F6EA105F38929B09D81FE26A68F13E3B19DB6500B1F7
+ 9E36BCB2CDEF7BB14FC8BC0F1FDC0DA49C3DB15FD21842159FCA81A6A637D45C
+ 703F909F9F018A94779DDF547D1B1AEF964AD5D5A22D90BF6121E0756455B382
+ 3B09B434E5E537DEFDAE43CE0FFF74FEB382A5702F7D9274917DECED5EF33FC5
+ 27EDCAF48FB2F9FF7FFEDFAD14BE065EFF3F6B5EFFC918B47E2DDC021D75FEE9
+ 6B6332DE7BFA08B27F55D3CADFB8F055479EAF34B8EA5CBC97E9D0DA1C3A6D9D
+ 32CF6FEE436C87AEF21DB34A0105EBF2353591AA302A7E33E77AA4AE8C0E6722
+ D257E667E786C65AF7CF5FA97A50F2133011EEFBD4DCD044EAF9B197899900DB
+ 5533E593BE84F177F1C9BD917B54967FDEC6D75B69F155E58F2A7F54F9A3CA9F
+ BF337F54455554E71FD5FAA3DABF54F9A3886E9F2F839BC517AB2CCDCDB43B2A
+ 7FD222D745661CDE50E6B7FF739898E901E333DC1A86243B9DB518673A81CD95
+ FC59BBBCF993B2322ADC3371CC9E4FD2873761C1F86C77F0CC760352276D0342
+ 1CE258FC965F3428923F0527B36054FA089A07B5AF6BE049ED636A1C7A0C334F
+ 134FA6F1F5DF3F5FC82622A5B6A116C6EF11F261E4C661E798F0492C297F6316
+ 618A971A7C2F9E6FEFA39649FEDC3E7F0926113E66109F10DFD0858CF7B8E611
+ 1D8B1A26F993823579DBB8ABC4078443C6A1D95E07A60AE33C74C3E0734CF739
+ 8B71E613E8DC91165FD351C69E4CF91C9CDF03710E4ACBCF0F97D8C5B2D5DAF7
+ 7D138BCD62998D35F12479426249FCED827D42E6CD5667B354A719D5F94775FE
+ 519D7F54F9D3B66E7DF333DC3CF37395A5B175C79D7FC2E323B7C69797797B3F
+ 8611EED5E032B6BAC1DEE9DAD91EA6C113D86C5EFBCE3F61D1E1AEA3CEED711C
+ FEB2090BC4D464E3501887583A8CCF3F87B655C0C0112F61F0981A884A7C85EB
+ 35F0AF9135B026FE1538E3363286814910E3F38FF7DCC7D45C099B94BD071B60
+ 4F4E0355276DE49ADDB0BB8CCE3F2496C4DF8441E6BDE76083F07C42EAA4ADD9
+ 4F8CCE3FA27CE2137ADE349FB435F3199F7F5CDD6F5F250CE26F9A4BC6696A02
+ AA8D5CB31F7C83F1F9A78779F00412436709F11D32F66D7CDF33F6637CFE21F9
+ DDDBE1549CB4FCB4B2CB8D65B135DA77466171580626CB3CED86DD393700C792
+ F8FB03EC1332EF76B355E71FD5FEA53AFFA8CE3FEF40FE5416FF08F7B0F0AB52
+ F2675BEC32F82A721EA4452DAE363334D4E988FCF926333337336E7DFC91CD5B
+ B62645CC7D13BF723660D5BD6F65D45B4FA0C5EB636D64CF347F120243D6C486
+ CE7ABE36F0D35BF8F571C4D26910B4682290D798D059555181336EE0B16AAD2D
+ 8DAC98E44FDEA6383257880BFB0C96CCF7A07E5F37FFD34FA857F29EB493EB2B
+ 7CC7E73289EF4F478E42EADA85D47C09332F7503DCFEA68C7A25EF23964D8384
+ B0D90F8739D98D619A3F47B626513E21F3266CD276BDA8F40D7EFF3A18B7FBCF
+ 73CF649A3F6FE7BFA8C5FC6F9EFE0E72539AE78FDBA382663C1CF291ED5826F9
+ B376C5DCB3E2FE5F3073742BFF2FF31997C7E8B31F93F74CE257CE79B62E64D6
+ 6D9C2F4F44F307B755ADF9D28BCA9FFE663DAC98EEA356163DFB75EB24D0EE65
+ DEB3279E6F1D9DFF7D6D4C7AF3B53579BDCC7BD877D49E8D7D5D4EF8D141332A
+ B5B534D9AA534CF367702C16E27294E70E231D9E4E9CB35D8289863A4F497C41
+ C1F821D559A3FFF56D9FEE7C2365F1B1E0F0B8C1951EE63D874AB21370D575B0
+ CD7362D74EBD0E70B00AD0E0B0D94AE2538A73B64FD310F92D7F47F2F78D1DF4
+ C730A3EE0EA2F3D7E4B0B98BEC7B052FB6EFB54A110539DAC460E62B9ABD6978
+ FF022B3D9DAE4A88EF9B70279BB5D80FEA4AC89F3BF3AD8D2728E31F5BDDB96A
+ 5CC76E7AE6AA95ACE34A8FEE487BE16CE4B3F033E443EA4CDB25151E5E05F7A5
+ A143E70E2220CA4C46F94CDAC58BAEAE807FE1CCA10717CE1CA83B97AB0DB47D
+ 31AE9336C5DB0F3D204CE13E81F7086B2B0B9EB595B95646325B381F52276D8A
+ B75BF03852F69DEE5D10DFDF1BF9F86175C375A6EDAAF2EE162D2EE2EAF2900E
+ 2D81961ACE36CDCEB244EC703FB9FE1794755350E0E9405635AD6389AEB5F919
+ 710DB4F2B00EEC88A294D7DC46EC0EF9B1AE5A1B48FFFF28E8123D098517AE60
+ 01ADA3F1AE909B1E27D4BEEDD13073A20B2552277F4F753A904D29DF8F552E3E
+ 0679146C8DB57836863C81754FCDCEAB3CD4E37679B161D7A71C4A3B977F0C3B
+ 929609B539CE1F460CEE07239CFAE1FA12D8B3295CC82722F721EAAB4E7C35FE
+ 9F9BFB3F284BE8577F2AD2E6D5C9C8DE8D6776F84249E18166E5404951AE50E7
+ B18A8F67C12F9B3E862BA98321E32B7FC8FF82253206EB398987085F07F39F5F
+ 4CE80727236D287D93BE18AF2587842A2E3C083E33475122F56F8BF6C31FDB9C
+ 00F7839F529D20731617F27D594AE1536324F6819DD3DFC64B19FCDFD33E849D
+ 5EB2F96589767FF177FAB6E09714E5415A621825522F2DDA0B7F6C1DA410FF3F
+ 690E40C620F75196FC115CDEEF07970F2C11EA4ACE5FFA35CB4BC896972F6AAF
+ A8E4E1976F71848A6D831416E9270FFFD18504686AA85358A49F3CFC2797B650
+ DF45D4D5BE80A78F2BDB14B123E5E9A56D2AFE3BC0DFE5C57A85F97598FF00F3
+ F91DC9FF73B343BD6B5FCD1EBDBA232D2C9EE8AF4F84F959120F6F5EBF545824
+ 3F71FF3AB28748DAAF9AD7FF3BF839A9BEB6D3A5FADA2EC544FAE1FE0F315FE2
+ DF4DB19AC720F7D11E71FE864F66FE0B13AD8A384C7A0B000000200000002000
+ 0000941500000000000078DAED9D0754544717C72FBD491144106C68EC0A0650
+ C4020A520C0A1A54B06305546284885D11B0809880A8A0265294204A040B5800
+ C5061A633E63248A5813A3A8C1164014E17E339BDD65815D7617DEC2317973CE
+ FFF87C6F667E3377EEBD6F665704E0E32BF2AA4A724A5A6A6A449A5CA9C9AB28
+ C9C992A96CA4A16638D374B8698447F8E0F84567ED8EAEBA47F49CAB7BF41E7D
+ 46EB2891BA4C71D5F4B5557BFAB9CCB13D10788370AA89508CAA6D0E06FEDA83
+ B451256D9BC26EEFD8AF2FE1E64BC0142AD236CFD8B15F9FC6B04D26DB8C1E71
+ 64E58BC6B279227D9474267D49C326F5C79076654D650B8CA1ACF324C9C6406D
+ CEC4BC858CE1457B316BA1AAAFA56A7B6049ADF5F63E1F838BF2BFAD25BFBCDD
+ E8706C8DD463187670499E8A8196489FA47E5EB7CDCCB3D178E6E96F78A9E40E
+ 5F679FDDC4CF8E0737CA0E3DFD46CF1615DFDC18ABD786DA20AFA44862FEC4EC
+ 7074CC582BF4198D4D61F981E60D51F11DF1EB6189E73FE7DC76CC29BE81E1BF
+ A68BCC0F865EA6B675F9A6119EE1C2EA4FCAD98217FFBACDE1D27E4F3F2D10C9
+ 77CE5C87471E5DE5D4A56D685B617D5256AD7C4E723737A7D6ABFBF58D239CFE
+ F289165FDA83B3CF6DC3CCC7FF1339FF80CBF19CBAB40D6D2BACCE10C25250AD
+ 795FD0F707379FD7AB7BE4D14F9CBE4E3CFE059D328238F7A69D8914B9BEB40E
+ AD4BDBD0B622D6E01E650AF035B9EF917A752FFC55C8E92BE14EAEC43E9E70F7
+ 2CA7CD85E785A2EA3CA74C49F8BCB58FBF7346623EADCBF30109F922ED9FF1E7
+ FF387DD13F1D32C4E71C5AA7A6CDCF12D9BF21FF8BFEED38A72F1AFFBE1762C5
+ F2699D7C6EAE88FE2D5322FF6B28FE66E446F1FB3B4A626BDCA98D22D9F4D9B1
+ 473F73E3A588D35668FCADF108AB977FBC84E71FFB63AB71C7AD13FCDC437D7A
+ DEF91D3892DCE7D5A1D7F41E2FF6A976DC3CC1692B34FFB8D7CF3F3427D2DC28
+ 6CBC2E24D6531EE4F1FBCEFBAB08D3FFB88231B74E7244AFE93DDE735AD74544
+ 7EB03910F8ABB288FD590F21EF9F9A3184606CE1297E6E11266A735A87D615FD
+ FE719925FAFDABAD4AF74CA2DA527BD2FCB79330B2492EE671E935BD479F89B0
+ 39EFFD7B51C540B3C13D21DDAF49BAFF70CA0CE248D27D18D97FF49668FF3589
+ B3F76374FF6532C9C645CA3D201D430943FB4F97C6EC81E95AD8A688F6077122
+ BE9ED7D9EED3DE4D3E7F2C7499CD8D4DC9CE1F24C6889FCF5669DBB4F347DDFC
+ D08EE40DD3B59EE14312EA9FBF86247C914BF31ACD2DCA0C9EBF84150521E7CF
+ BAF99C2DFF8ED2DD40A9C3023BAD098D5553F91E03343CFEDE6182C5915DF089
+ 147ABEB50BD2764CF09F91BEE68EB3C469AE03C46AF4527B9C34711046CE3365
+ 8CFF34AA0B7ABA0C443727AB0635DAD51AF56EFAA0CD970EB861A61963FC92E8
+ 2EB865AE296E9A652656EB7D3FC58DB3FB636A406FC6F8B49FC6AA39ED4F456D
+ 4FD78149FB4BB3FEBA45BE386C31F3EB1F31C794D3A73885CEED8FEB891F1C64
+ 78FDD9F867E39F8DFFFF6EFCB7E4FAB37C962F4CE587DCB1B2E818BE3BBFAE45
+ F815E7D662E5BD2CACBC75084B63BB353BBF2CDE0A2BCE07113B8C67FD4F46FC
+ 963E7FB1852DCD5DB43441D1C81074895A3527B79D212807F8C097BBB7C0F5E4
+ 18784AF447F86A481E3210BAC99AADA808729B56C366C2AC26AA22FA9DE82D11
+ 7EBF031E7EDA074C64C9B7300513C22A272A737781D19AAD40DDB43798C486C1
+ 153A869808F8AE31FD0EB6847613C78043A02F8C196209FDD45441E8E784A3EC
+ C09572A283214B55A9E6BE9B1338D2FB49DBE14107635094943BCE117A1F8D83
+ C337B2A18208B9FA702605B2BA9B809E10FE70CAF9760BFCAAAD05FCCF6DDD1C
+ C181DEDF1309B7747541EC679CADB541F1DBAFC18FB0CA04B87CFE8118D849F8
+ F5FCBA7347D0DAB70DEE53565000ECE8D2098CCDFA4097D870B844EFAD5E0CEB
+ 1BE2CA93910D1D009D8F274286102E5E3F054F37F8818728FBD3327522389331
+ BCA03CA2F74495DCEB77BE936094A282F0B63ADA20BF7E29781664C31361EC8B
+ 3FC0E96103A0AB24EB66650E7DB6AC817D84F99ACBE6A9227429EC32EB051D04
+ EB9BF58636C9DB208EDA5608BB625F3404B5D307A93E875721FED7AB1B74F4F7
+ 8615DC38E48F23712B147F311BFC3AB705D585D3C1EEF211281236E74B47E0AE
+ CF34B0D7D46CE2FBBF2BE8127F8824EC0FBC31A4EC04CC4982C23ABECD5375E6
+ 5E4831EF07064CE5063535900B5B07DED41FD2BE03BC72ACFE7CB97AB37333F8
+ 90F851643A3FD13190F50FB99AF90F6BFB269293C26BD83F1D836BAE0E602A27
+ A36F2134D4417E6F24C4F07879E980F987495C654175FC5678397B3A1498F707
+ 6B59B05BA983626224ACA56B5BC7C7DE862C87D773BC0089AA972D86E28079E0
+ DF4A03949962BB3A8349663C1CA89BC78EEF85335FF8C073CAF69E05959BD6C0
+ 2BF29EA2EF2D8C0A86F41E5DC1A8B14C7D4D50F86C04987F1F0DB184F54A907D
+ F538DC0A5A02EE24CF2A74EF0696F367C36592234BEBE40ADCF3353C18370A1C
+ 95A5B004ADFBD502189E9F0A3984F5AECE9C5F46AD83B5DDBB808E601B934EA0
+ 15B20CA2057265AD9CB5663104E9EB808A24FC9025309570CAB9BC67442F7879
+ 6CA1170C531411552A2A20E73116C626ED804742C680BB3743B659DF86F70FBD
+ BB81DE2F27A198FA18F1F3556DF5408BCC55EFEC41384EC710B705A21A6A4FE3
+ 8EE4E20E51217054D818C8D81E4F18036355445862DAE7E0C87957A4C315BDD6
+ 35F983BC7F6DE97D9273EE181B80BCD8BD632B50FC6A3E2CE2EE65EA8EA372F3
+ 5AF886AC99AA10BE0BE59CDC076704DF91DE53C08DCBBF4BF6A30A92FAD288C1
+ D02F79179CE2BE4779FC771B96C3B71D8DA0DEF7B3C387407B9A3FA9DFAD5800
+ 7389FD75BDA782D52F59709B6BFFE8C6C452FFBEF0C9EC29E03E7B124C18680E
+ DD44C5833279E7C56C8420C2AAE2FADC5B9EEF179C8222323E6359EF810DDA82
+ E2C6A5E07B310DAE116E31D94FFC9EBE1BF60E3583CECD790ED0D50105938ED0
+ BA4B47D0604F636C69AEA2200F727A9A4A9A44DA9248534D4199497E0F2375DD
+ B2A411456549762FC5A93CC9FEE51F3B879EE9DB41A335837C3DD2F7332214A7
+ 8AAC855871D2071FC60C3DD7AF632BDDE6E6BFCB5D8AD5AF7EC38A13DEF860C7
+ D0B3A69D5AE9353BBFEC21770C3E789F8131348A4FF5B28033066A87A6AC8538
+ FEDBCCB958713A90A377F99BB0B230B5463712C8FDA5589AB3F2C6EBDC8894DB
+ 69AB77756AA7ABCD249FC3FD3152ACDEFF148DEFAFC7E1FD8C75F9D28C8131FE
+ D5EDC42F6E6265C15EBC7374ED852EC67ABA8DE13F8B19848F222DF87A737CB1
+ E4FCBFEF705479632FDE3EBCE65CD70E6DF4A4E517ACE98E17E61BF2557CC047
+ 3A3EF5CD3745C40EFBB0307D75EE271DF4DB48C37F93688BAFE36DF82ACF5E22
+ 3DBFF618B2DAE868A84ACABF17668AD7577CC2D75FE90B25E35FD98A95B75288
+ 0ED6A820893EFBAB47E7B6FA92F2EF13FEAF2B3FE14B527E03928ADF58FF678A
+ 7F9DCC3977AE3E5F8F53BCF97DFD75C807AF07B613ABFBD1231ACD7FBAC30AFF
+ F8C69C2FC1F82B3B1B4CC6E02D56AF8E2F6934BFA1F82F3B1BC2B1813095A42F
+ C07797BF6EB2FD1B8A7FCA285865225437D7F5C4B779614DE63714FF1597B660
+ F9F90D22F5EEC76F64EAFFAFC9BADEDF3A5CAC8A93A6C8867F22101F44DB8955
+ F1F7533F9AF81757F213030E4A12FF052B3BE1DB8B6132E53714FF2569BE8D8A
+ 3F69F82D6DFFFF22DFC3C9DCDA6F92ED8426C8B5B596BA0AB0E5A32CF2F272E0
+ 3472C8C0D5813E3B88629C465A0F684EFE18279B8187F645BE4EDCB9E1C7B81D
+ A13F92EB523B1B2B73C63F07519097D3D652D7D1D152D73368ABDB81A7C88D81
+ 09DF6D0BBED2B64D6B15353555A594B8CD57972D9E1DCFAFA3AFDB5E475B438F
+ B4D35156546CD43741A4BDF17897C1E973263B3C217A76203EA294A71FF67EF3
+ 81F00BF4DBE86A10BE1AE1DF22F72A79CF93766FF87BBE97CB73DA76CA589BC3
+ 4606BA46D2CE9BCB46DF192EF8C59C71486C5C4FC4FE85713B426ED7BD4FC683
+ 01F32772DAD23E268E197258AAB913BBD1B1FBCEF80C1F3F7A88E5E565989614
+ 25740CC274EC602CBE7E59828FFEB88FF3678EA663A890864FD65C8FDADC6FB6
+ 1B969795222D57F34F4934065AE7E7FC2CD2A21ADF9271FBCD19CBB14153F98D
+ 292C5F36FCAAE2AB5879E79858BD2B3C8C575256E2A5A4A5989F10902AA0B84E
+ ED74B51ACBAFBC9321D3FD27CBFF48F8646F5D92365FE4F9B7AEE85E9D49FEDB
+ BC70BC19D4839C333A4BA492F4F98CDB9F9E6F24D5BB4B5F33CAAFC88FC087DB
+ 1D449E791F6E1F49EA6C96D9FAB734BFA5EDDFE2FED7C2F1278E4F9F35E6FCCD
+ 94FD6FAEEDDEA8CF1F085F97F09F2E98E58AA5A56F9AE47F8DC9BFAA2A4A4A53
+ DD6DAFD17D43784800C64406D752E1F1CD32FFFCC7AC4F67A7999EF6740F8F75
+ 45F713CD71FE6F6FA467DCB7474757A20982BABC2F30FFBFFCF987D3E05ED693
+ 46594C6C82C66A69A8B29F7FB0A5C1D2AEAD816AC04CEF51CBE7FAB93321DA17
+ ED53527E6AD4AE90A7177E4126951AB53B5852FED5D4130799E6D33EA5E0A7CA
+ 809FFA11F15BD4FEC4575AD4FF5A3AFED8F867E39F8D7F36FED9F867E39F8DFF
+ FF5EFCB3852D6C61CFFFECFB9F7DFFB3EF7F76FFCFC63F1BFF6CFC4BA7E20BD7
+ F056EE39CC397510334EEEC3F8835149AD3AB5D29693979379FC45EE0C0EFAEA
+ C0FCE7AE89A3D029DEBE4671F67F5AAD340F556FA7AE232BBE5B90F3970EDF8D
+ 28A9C5ADA391BB865F6E63D6C688E9F8BF7AFA04BAEF1D538B75EEC1598E765D
+ 89A975DF66EFE0D32A7ACA4A4CF91F5DEF95A95FD69B2BAF9C7F70AEDEB31E9E
+ DD2633157FD4D7EAADB718FEE85DCEA798E2E79C3AC0EF97B21A2A2167D6F1EA
+ BE64CAFE9924C61AC1AF642AFEF3B28FF0F9D4D7787EC72BCFCB9EF3EF2DCA58
+ C8AD6BF790A9F8FBCACF77CAA804876269D67FE846EB38A6CE0C720A7260B5C2
+ 32540AFE7B432B034B26CF2D1A46EA3A0E24B7087242738339FA32C3AF167FE0
+ 0A8B104972B1B445DF4CCFC82662C8E906F2DF7BC20E5654535092D5F94D4547
+ 59A93BC92D34BE698C117D207A48D79BD8DC4216F3660B7BFE67F7FFECFE9FDD
+ FFB3E77F36FED9F8FFCF9FFFCF5FC7DF320BF0644211A6EFBE83BBC2AE25A9B7
+ 32D506394599C75FF8CAAC20EF79CF9F0F73FE1B07DA97F235C0BEECCF9EE639
+ A16AEA3D6476FE1FE510FFE58011AF4A04B9756539FCF9659D36CE8C9FFF7F3C
+ 7413ED47BFE130AC4696E1883165B5B876AE357F371B5C725A49D998B9F33F59
+ EF450B9FF2D93BE3DF636151153A8FFF87B97865053E7A5C8D7317BDE58FA143
+ B78D8C9DFFA9AFF1D69BCE9BB26929BA5B85EBC2DE6179F93FE7B084E4F77C7E
+ 7FE7378C9DFFA99F0BDA9ACEFBF6DDAA5AE7EEFD872AD1DAB14CC01F4B193BFF
+ A7EFBE5BCFCFE8BC79E5E52B44B7C9E575E2A194B1F3FFB9FD85B5FAA6EBCDB3
+ 3965D3728BACC9684F8131D8953276FEF79FB57A8A95436931CFCFA9AFF16CEE
+ 4AE67D8BEB0F3F1CA9E4F3FB5A5F63ECFC4FF35A4FCB9C505EDFD4CFA9AF593B
+ FCB3DE74DE94EDF8397FFDDFEB1A8C67F4FCAF4AF29A05C92D0DE51E9E7A599C
+ 0A9124174B5BB4F59C8DCC86FC76BA01F67BC20E5650D092D9F95F49D95089E6
+ 161ADF34C6883E10EEC37E64BD89CD2D64316FB6B0E77F76FFCFEEFFD9FD3F7B
+ FE67E39F8DFF968EFF87A7F3F18798304CDDBE91A343DBB7A63667FCDF387602
+ 23D6CCC4CDABBD380A5DE2952CCBF85BE7E7EF999DB03B71EBAA35BEEB172F99
+ 9412BDE1188F4D15BC64F24FC6867A861AEAAA4A3656BDEC4C3A1A746692BFD4
+ 7BC62AC2A9DAB87CDAE3A0AF3C2F92EBEAF54BA7E05A7F4FCAC6F0553370E38A
+ E9BF07054CFA91D65BB6F0F30C0D75757926E2FF51EE15DCB969317FAE612BA7
+ E3E279AE38778A23FF679717787D8621819351C026D5A3EC07B831E17F57D3D2
+ 306275CD5AFB133665FACD74C588158B70F982299CBFFB4C77263698C6AFB761
+ 85571A13F147FFED41DE81648C5C370FA9CDE9BC29BBE05836E7F99FB93F61F8
+ 323FCE1896FBB973D8418B3CF61BE8EBB46632FEE3229691F5F6E070E8BC059F
+ 1DDD195B4AEFFB7BBB71F8569F76376332FEEF669DC3980D5F707C8D334F62F3
+ 4764DEFCB1858554D1FB81F3C771F893C6DACC515252946722FE6F6666E1B6D0
+ 059C7EA99F535FE3FC0C3FB1F989EFF6607C7828FA4C1BC5599790257C1FACF2
+ 9EE6B48189F85B3463C62CD2DF1B9E5F513FA7BE26F8B3FB94BDDC6FBCA0FFA3
+ D744BB20468EFE7272306FAA638840DFE5D4CFA9AFF97BBBE2126273EEBC3F10
+ BDA375A20227DF696FA0ABC9D499A5538776AD7D6738274C701CF4B969EFCEBD
+ 56F88D3F2A38579A97463B0C70EFD1D5B82FB1C73647ABDE6364F92DB895456F
+ 3B41FE8A2FC6274333961E5D8DBAF36C4D357DFC884DECC9F6BF515AAB2AC9B7
+ 243F76A4E5CA9186BABD5A6A10714E56A9275C873E9B6FDAD5ADA5F819E36C91
+ E87DD8C83E6BB49415155A88CFD14EFB0169667ADA0DFE9E0AB7AEC6EE826D98
+ D621D7A137ED3AEA9BCAB5109FAB9733FB98782A2BD4FF0750CDC4C7C36EC38A
+ 2C0D5BB76B09FE1EC78139830CDB7412667FDBF66D07458D304F6542648E8FEA
+ B03F44DB5A4419B552D3687EFFB779B3C4BABB9792BC9C7C73C75FE638DB7B8E
+ 9D0C2CE5E59B3FFF248DB23E354853BD6D4BE43FFF1E1DED0D3554D92F19D8D2
+ A4D2C10894A864F55C54D1D400856D211094950C4FB289B687C0DA561A35BFFB
+ AEA9CFC595D06530FD6C2A54132157D5BED3613A53CF859589EE63DC2F9E4E47
+ AAF34787A3405B8EE83DA69E53565DBE85B969FB407F5F4FAAF8AD7DF6D66D4F
+ EE2532F59CB21AB245CF4F40FF681C5CE2B53D1A0F977A917B4C3D97A418B401
+ F5591E603F7322D8D16BA69FB3852D6C69B8B85AC020774B9820A871D6BA53C6
+ 8CB4F09654DC76EEC6AD41EADF4D97E12FB73F67A93C0AEAC4864F85FEFFEFA9
+ 895F63F277611CD16BDE7D5EBB241FB9B3640C6D64C58FDA14881E6E361CD1EB
+ BA7CAA7DDE72B9D28C2171261C4C9E298782DAE7DF1313A202EB2934703A8EB2
+ EDC711BDDEBB6D39FE90188175C7CFB543BDB5B0EEA9D93E747247CF351EEDA7
+ 068C6D37972A65BECED5E405C698B274408D42DC70FFAEE07ADABAD11FE74D77
+ C50513FA63EC2A37DCF34D00266C5D86D98172F5C640FC615C5DBE975D5BF7A2
+ 584BBC16D91F4F06F5E22B67BB07FFBD294EF9D9C958B8DB06693F97A2ED3963
+ F89ED8ACEE18A83F36959FF46D04FFB3657A5D974F7539DA0EF74C96AF3786E6
+ E2535DD9D80DE3262BE081B92DC3A74A5BA0CDB1C1C7C99F20949FB63F164357
+ FA7044AF39FCACEFF1F6EE614DE2DFDA6E8EB9EBFB6056702F8E723659E18563
+ A4FFACFDF594975D233AF79FD3D662D1CE014DE253DD8EB1C04241C55AE1ED5D
+ 43C44A18BB317CA6F531F11F248FC537B733F1EF3B279BA6A2E3F8E07B37A9F9
+ 4F4E2D45A64A71D6F226F15FBD7886C57FDE974AB4CDBF855F5D5D8555551FA4
+ 126DC314FFD993DFF15ED175A9F4F4C943C6F8156FCBF0EF372FA5126DF36FE1
+ 3365FF445F83D879C3C0CBDF193CA93EED04ED9BD3FF42A7749C286EAFCBE33F
+ 3A3C0F3F94BFC0AA8A574DD287B72F485F73A5E6DF21EFB07BF1F6782FC1A169
+ 227DF0F2BF24FCDE1DD48CC91826C84283C9DE9E3DCD0A2FFF07A8262E834C7A
+ 0B0000003000000030000000E72300000000000078DAED9D075C14C717C71F47
+ 2F02A2744405BB58B081745054C0AEA8344BD4D8A2FFA851136C880D63011B56
+ 8A0515048C4605511415EC2676051145ECBD005284F9CF2C1CB93BEE8E3BB8DB
+ E3927D9FCFEFE3BA65F63B3B6FDEBC9D1D0080B17F93A9E86929AAEA6B6B61E9
+ 61E9578A6C6BA934D452AC6FBC2C634596865D23438B05AEAEED660D0A728CFD
+ 25DEEDCF05975DFF5C9085FF7D4654B97DD9F1C09CF8B6B3062D6E3EDFC5955C
+ 43AE9515B79ABEB6AA85AF938375BCFF16CCF708F39563211145CE7DD4195FDB
+ CCCFD91EB78D2A5DDC1AFA3A0A16BECE0E765133FEC00C0562300B520129ABB9
+ 9F933D295B9AEC8D3B9A3574889C118AEFF94902DCBCFA84CB5E47EE210DF6E6
+ BE4E1D7B1D9E77410ADC5CC2F7C8C03ED541A2EC7E4E2EAE870373A5CDCE16BE
+ D793E67ECE2E12647F45173B5B2E7F04BE6CEE5BB73AE0EB3BBAD0F8DCF9B503
+ F6A58EB5EAAB9DCCF4E8F07751FA83B87D5ACDA081827DD44FA1FCCAEB736C31
+ EA7B2C48A0C87149D7C176D7B4B52AC65A22C756322E098A91C17FC7A1736F1F
+ A0F477997C75E2E54D343079B9C463AB859FB39DA8E36AE5D8C4BF3D8F2E44CB
+ 6F26A00BEFB2D0A5F7D9D594FAEA8E34F811663AA4A6AFA322E2B3173AAEF63E
+ BA08ADC075B8F8FE619DF93D8E2FA19E8908E7E69331BAA65CCC3AC17F8B28F7
+ FDDFC59D7CDB401CFE51A96BD0DE9C736872FA5691CEC7F95238CB5849603F20
+ 39A15B452E26B49C6129212819FB795DFC27E04C28FA23EF2A754DD28B1B6868
+ CA4A51EA904D1805FA0ECE816BCA23495BAFBF778C8B3903B74346655B88C24F
+ 8E273CBDCC55C6FABB4745F1A3728BF9AE02C73492BFD7F40C86E0E744E20FFB
+ BE17701F5876E320D51F481D44E1E7D77F4899A2B401665CC4F7BD09BF1B91F7
+ 8B9AAE5F706D1FD7734B7C7A0579250523771CF797DF4840292F6F89E43F0392
+ 9755F90F5B0BAEC7D4789D73EC9C83AA7AD5DFE3C8FB1D7E0FB95CD3F5D1D967
+ B8EEB9F8AF0355C7DC8F2D4281D76250FFA4A522F547722D6759A46C11AEBB44
+ 58F9F0EB55BEE709BD9EB3DF92361F9BB6A15AFF103576926B397D31F9C54D51
+ AECB22AC7CF8F5F1B1BC9AAE27632CFB7EA75EDD46C34E86D47A4C1A76721555
+ 06BB3C52B608D7E511D6DAF273C6FC93F8DE43536ACF3F14D7FD24073F29BB0E
+ FC22F90FE7FDD2DEDC47FE388ED7969F8C01A48CAAE7F1F2765DFC47A4FEBB2F
+ E73C579F9B776577ADF9C9B59C65C5E0B26BDB7F458D9FCB6EC473DD73DFE374
+ 2A6F16979D5CB31F5FCB59D6523C8ED4367E52E3D7EC418B45C9593238FA00D9
+ FEE572B4D8FC732EEFAA560E29BBB6E397A8F9436F1C1F231EA6723D3732668D
+ 3FB74964F609E73653D77096B1332B952ABBC6FC21C0C5A5AEF99BDFE975B8DF
+ DDE3BA3F796F997529921A87055D478E91734EF0E47E677059BEA7D78A96BF99
+ 09CEDF706ECA22F38135BE93E2E744C659DEFCF9FCDB4CB42B3B8DCAADBD4FFD
+ 8E3C715E4144B6C9BE5D8FD250FADB4CAE6B48198157F78A34EE5953F9B3A2D0
+ F748321F29CABC207996213713F1FDABBFC3907D248F23793111D91674DE4A5C
+ 86BB68EFCCF9CD7D85BFBF54C4511DA1EF8FBCEFF20BAFEF43673962B8A822D7
+ 2CC0D78AC88EECA3A627E2775B1511E7ACEC459DE324ED3E3A2D0CED7E7496CA
+ A56BE226E79073479F09132757FAD8CCDFB9A73873CC642E559C784872FA3169
+ EBD1DADB47A8BC983736927DE418E126E78A397FB2469CF993CAF9AB8664EEA8
+ B6632B8985FD939752EA2D464ECA67FE2ABDB673D24DFD9CC8FCE11319CE1F3E
+ C67DD6AA8E73A02E642E5506EC2F31BBB364E6A09DC91C346DEDE052317FEE24
+ C96F00E49B425DFA83C8795DC2FC74497FBFE0FA7E143163AD94BE1F7DB48B9E
+ B1C6B86D73A97C3FE2FA7EE7E76C671F393D918C8912E0CEB78F9C91889FB99D
+ AAA174BFDFF1CCF3AA905C83E42424AFAAC5F7D36C722DC909441D57A5F6FD1A
+ E7841601AE2E38375FEC14F7CB41FC1E77C9ED9FEFD7E49D3A93BC379163247F
+ 273930B9A6A65CAC1EAD1F6848AD1FD0AB7FEB0718638C31F9311CF0145A1929
+ 9BB43652369586A4CDAFAEACA0F635BC59CED7F0E6CFA4211AF8D5317FF197CD
+ CD51DE3A4B94BBB6EE7A8ACB21E5617E4417FF8B300BF4F3C82E68C2906EB5D2
+ 381F1B4A647B867757F466BD05ADFCCF422D51C080EE68505F1BB1E535D80E99
+ A6FF804CCF8FA3B67DFB7747AFE58CDF0CB39B9D931DFF73CC3F655857E48FEB
+ 501BF98CB0453EDEB6D4F6A4A1DD68E7FF82EF457CF6B504C4F67D3AF9D9F793
+ B4E4C1FF890678D9A2019EB6D4B63CF65F938BE391C9851FE436FE985CFC41A6
+ FC64FC9A8EC79DB183BAD54A6386F7406386F5A0B6A70DA77FFC62E70F4F2520
+ 52CE179AFB2F93FF30F90F93FF30F90F93FF30F90F93FF30F9CF7F2DFF91F7F8
+ C3F033FC0C3FC35F9FF90B1386A2D20709A8F4FE415498384CCEF82D28EED29C
+ 93944AEEEC43F9DB5AC90D7FFE16CC7F2F96833F06F3B79633FF19467197DCDE
+ 2387FE53D90EDBDB5262E20FC3FF6FF87E2AEFDFAF19638C31C618FB6F587373
+ 506FD3024CB04CB11AC803B3A62680BB33B45C3C0B56466F80CBFBB74036560E
+ D68DC5B361CB604FE8A2AD5D3FD93534007E9D0DEEFBB752CC889FF66D8137AB
+ 02617CE38650EFD6FF3AD9422BCCFE8883B718EB2ED603ACEF1CFB3FF90C068F
+ 7AF5ECD54161C5AFB08683317B8827B836D6035DAC8663BCC115EFABAA5BDC4E
+ B8E860071AD260E9D20194BC3D41C3C448F436C6E7EA60DFB856C957367F220C
+ 61F15C3D7A18D870B443C1602F709414B3CF20E874361E569D4F802BD78FC39B
+ 3BA7E0FDA523F014FF3F71CE24F0D4D305560DFCC69829B792ED4DCB6660592D
+ FF5703157CEC26BB0D96FF06E3EACCED05A698F120E62DC242025412BF0D4274
+ B5415908BF7EA59F13B622CFDED04300FF0D36FF6F33C1BBB6DC1D3B8062F84A
+ F0C66C2F857053BA95026F12B7C218CC2F700DBEA10128456F84436CB6D025B0
+ 57BF11B77F070C85AE1CFEF3AE7B67682F2EB72226E8D0060CD30EC276CC565A
+ 037BD9ADE370CE7F0888F473609346C340F2EC2BF9CA711D627A398135EEBF46
+ 634751FD37935DBF2D21B01FD74FACDF01D8500714E74E81DEB74FC1ED9A9E39
+ D6D7433BE0F77E1D441F37B53440356C19ACC77CA51C71883CEF0FA43E1CFB3E
+ 4F0B000775117F7245490900B7955E6A0C2C255C35B1DF4886AC99E36190B1BE
+ F8634CB72EA0B9E06708227D58D0184674201CEECF9908638C0D41AB067F5198
+ 3901BA67A6439A08CFFCFBA97D9030C11B9AD52526686982423F57E8BC6E316C
+ C4AC8F799E3DA74A77AE85E41F4680939A6AF5BE656E0AEA3BD7C0FF30D71B11
+ D83FA4C4C22C13435093544CD6C14F16E76DE6337F84C9319B2183670CE6D4A7
+ 9581B0D1BE1B345356AAB8769A3F589EDC0787C9331581FDDA9431E0D0A489F4
+ C6E7A666A035732A0CDE174EE574FCFD2A1CB2A78E814971413016C7BC272270
+ 1727EF852D78BC3554A0219B22B768D604F4D605C152CCFB8D973F7E07A0F389
+ 5042C61B11D89FE358E18F9F0BED3FBFA6DF188F15783CC1CC5FD8EC477701C2
+ E33E12811BE1BE7CF29729D05E49517639ACB111B0C2426022192F9263A83192
+ 62BBFC27A0DD1B015D3DC697BD10C797E076AD405B4949F6392B1ED794B686C0
+ 86BF93FF61DCB501D084B180707FE7663F098F037F027715E5FAF3DE6062001A
+ 49BB218E93933C77DCC7AB7CE9D649288BDB066FA74E84C7EEBDC0A77123A817
+ 3F47686A082AC7A36109662C17E4E7D78E43F1DA25F002B747196913AC12BF11
+ B0018F798D582C19B21B81C6B15DB05858FE75621FBC9F3B033E557253C26D50
+ B87A09BCC56377C6A841D04D9145AF2FE1B803FE83C0F25834EC17F2DCBFE23E
+ F064D27828E0602F9F350D3E6C5F0B9F39E2EDDB3993E1476303C98DB7824C4F
+ 07A05D4B6882C7F75F315F9E00EED28C4438B76416B8D97607F71F46C34DC2FE
+ E338280E9A07EF704C2AE2977FAC5F0A51AD2DC1441AE357277350F11D0CDD8F
+ 44C006CC972B30A6A742CE913D30A36777A89A59313606C3717EB07D5D303CC3
+ EFA565C27241DCDF6F0EEB0FCE929A97E96A0D6ABF2F864137132109F37D1132
+ 167D8E0D874D5EBDA0057EFFAF66068D4179F65408C08C4F85F1B373A8D58B60
+ 368EC975F2274F1730FC2B057663B66FC2F2DCCB4720E5D79FC1515303848E44
+ 24CE0CEE0FED36AD806342F2D8AA778DCDCBE1E0303730AF0DBB65536870E528
+ 24F0F44DF27EFA37D627CE3E1A3C0B5CC429BB892968AF0986DFC8BB4F4D6DB1
+ 77133C1835043CF4F4C4E35F3607C671E4BB4549D1B0B8657330C13142DBC315
+ 5AE031F42CBB0E69717002BFF32B8B3537A901ACD1DEE07C80630E40883E872C
+ 84257ABAA2CD31353505D5BF93E0049BEFD86E588DE33C976FE03A34E7F0AB4F
+ BD1CA0ABD8EFEFD89F2C9B81C1D655B09DE73D959FCA224321D9C3ADFA5C0E9F
+ B96643CC945DC996EFEB0DD57E8F077EC752C6C7AEB2EBB87F3DF8D6B69F1919
+ 81E2CC4930AAA6F754229C4BE5EDD800831A680A1EEF30BF11667A5CC9F6AE63
+ 5B68C9E7BD52151F7BC0E6DFB31102EA1AEB3C5CA06954281CC19C2542F83FAC
+ 0A041FCC2FECF96BDD3E0517D96CE1CB61028B637C57C56F1991613088337662
+ FFE92E89786D6D0DE0D51B1CB7AF86885DEBA9B9FA8F249EEED904B95B57C38E
+ 3EAE35CF3369E177DD035B218883EFEDDA05F023EEBF4DFABA80F9FE8D3005EF
+ FBC83E7E260E4E998AD97F45310B0B60B939803E7E36061DAC40ACB7847E2ED0
+ E4EA9F7087379F21EF1D9CFBEEA6C2DBD993C1A13E7EC358361FECAE54AF4395
+ 6E9E8017B14130429A730575EE53AED07457282CB9768CEA0F24263DBA8BC730
+ BC6FFDC49160057262F83D55AD4B0730EEDA014CB1B48031C618638CB17FADA9
+ 2A29B09A34526D84655017E968C86686D3AEB58E49418CEB15AC9775D1D5553D
+ 16E969292BC980DFAC20C6ED3116AA8D0A0FF445DF0E7993EDB24B2BBB2FD2D7
+ 5656962BFE830350595E2AFA76C49FFCBFF4C2F2EE8B0D755494E589BFFCD37D
+ 54F6FA32AE832FD9579CBEB4DB62235D7AEA2029FEF2825C5C874BB80E7E647F
+ D1F9A5DD828C69A88324F9A93ABCBAC8550769B783A4F9FFA903D51F8A882F49
+ B33F88CB5FB8AF3797BE1D1C84CA3FDE45E5F98FB954F63283AA033EA7F8C28A
+ 9E41FF1BEEA0F5F30837D59F473AABAAAB2AC984BF30D60B15672C47C597D772
+ 681D2AB9B691AF8AAF84B2CF2B2BB91256C856CAD6690B951425F345462CFE38
+ CC7F6125E60AAB95A87AFDB595DA3EB363469024EA402BFF5F5B50D9DBBF50C9
+ CD08EAFFB81D16A9A92A2BCA137FF9E72CAABF54D6A12C69F394459AEAB5EFDF
+ 82F83F453A56D3E73D7D51518604F8499CFA780FD76127D95F7A74E3A4C5DA9A
+ 6A2A92E2CFDFEB8A2ECD3045177E32E6D2B579ADD1D7334B25C34FD5A1AA1D4A
+ 8EACFF71B18E96BA8A64F94DB8746D5E1BF4354D82FC441FEEA0925B91E478F1
+ E1B01F17EB3650579584FF7C8C70401F78F469771FEC3F2B24CB4FD5E136BB0E
+ 45B80E8B701D94EBECFF517CFC7F6F1DFDFF7A382A7B7F0BD721B39ACA5E5F41
+ 257F6F23E73DB1EF6C612E35FFAF8BFF5C5D4FC57FC2C957D498179687F99BD5
+ 95FF22F6F78CA9465CBA3AB715EEBFC1B5E6175175E627FAB2CBB99ABEEEEB57
+ A7F84F27BF34C62FBAF8F3F7B8A073930C50DA447D2E5D9C6959CD7FBEA587A0
+ BB0B9AA15B738DC5D69DDF9AA0A24B6BA5C0EF8A2E4C3346E9530CB974654E75
+ FFFF961182EE2F6983EECE6F26B6EE2D6A21157EA2CFD14ED5F425A61F2AAAE6
+ 3FA1A8307D25D68A5A68A5D4FC5FF4FC07F39F5B860ACE2EAD51E4BCE2CBA1D2
+ F77F31F29F6F19AB50E6322B746F714B4AF739FEE555D6F24EA8F0FC0A1AF96B
+ CE7F2AF83B60BE56352A6B053DFCE2E53FA1A8202D18E59F5952A3C87974F88F
+ 78F15F74FFAF26D21F64CCCFF67F4E3FBFC72161FB1E04B7955AFC149D3F84E2
+ B8B7C0426C913E216B7E761B907A88AF55FFF9FC471CB36E63668ECB7F5ADBFC
+ E7DE424B1C3F97D72F7E31F29F07C1ED241EFFEBCA2F4EFEF38DCA7742EB19BF
+ 7CFB3FC3CFF08BC16F82CBBF82F54C8ABA82F9A5B25A4D4D4549B1753343232C
+ 5329CA48BD8E73EA8C31C69870D3D450576AD04053BB421A4AF2C2ADA4A404C3
+ 06BB778E0E5F7620716F582ED6D38498B5B1BD9C6D3AC803FF985103BA547223
+ ACF79522DBAFDC9C6CDAD667762383464AD19B83C973477BB6AD887577B5ED60
+ D3ADA3D5A6D581F164DFFE88D5F1B2E45353556619EAEB1A18E9EB9A11593637
+ 6BCF2927FB2E4E897B427330EB673B9BCE55FE32A09F730BBCAF38366AF5477C
+ 9E15E73516CD4CDB191BEA99579669A0A3A121959F846CD9DCA4EBF0FE7671E3
+ 7DDD3327F8BA3F263A10F9FB074EC546FDFE09F39761D622BB1E9D7A70F077C7
+ FB4A481BF05E131D1EFC6EFAF801B91565F6CEF41BE474D0A6734B6B49FEFC1D
+ 611F3BC22D07DF0371AAD2AFF96AF7B695277BBBF674B5E9D6C119FBCF5941E7
+ EDDBB9124D1F3F90ABDCF1BEBD9FB76B656E25219F511CEE6517C72E7BF2684F
+ 346BCA08347BEA48A1FC952AC02A1476CEFEC8552870E668AA4C5236FB3EFEC3
+ 9C25D257B04F1A927665B3DFB979157DF9FC919208FC35EA78C20EF4FA651E55
+ DE8DEB17D1B47103D875F824097EDC5FCD2AFD1DCD9C3C1C7DFEF401B18DDCBB
+ AEFCA78EEE4125C5DFA8F2481D483BB0DB40F2FCDE5CFC2FF21ED5A90E498911
+ E8F9D38755E57DF9FC89567E491BC3CFF0CB377F394225F908157FA995BEBE7F
+ 8682E704A0B993065222F3197C64AAA9AEA228157ECC5E7AEF002AB9B1B3562A
+ FE7B077A73E677F4FA7408253257C24797ED3B5B9849851F3FC3921B11F56AFE
+ 9CE167F819FEFF2E7FD1C5357596ACF8C9B7ACFB41ADD19D5FCD6A2DB206887C
+ 0394097FFA4A6AED06F9AE585B916F7AE41BA06CFC27946A83C273CB6B2FEA7B
+ 64A86CF82F8756ACC3480B164DF85C49AF3FA9ABFF64ADE88CFB402B91248DF5
+ 3FFF657E79F71F79EFBFF21E3FE57DFC92F7FCA136FCF5297F13D77FC87A3749
+ AF5FA2B3FF92758AD28DFFC3A51B3FABC74A49F01B4CA8F8E642CD9FDFFCEB22
+ 7AFFF6357AFFAEBA3EBC7A8C8AFEDA5EAFDE5F54549414877AF68C65CF8991F9
+ F9393FF960F956D3F27963A9F99BFAB67EA6750BD3AE13FCDCAB7D3FE2D59C49
+ 03D1ABD4FAB77E525959092C9B1975193DC2653FE6CCC27AC64F73270F7CF9FA
+ 74C8F7FABA7EA981969AB28EB6A681AEB6A6293F397669D9E3DBE5D0E7CCFA31
+ 99F137C1E53F243FA7283D856663FEA6D2E067292828A8AB29AB61A94B516AE4
+ 3ECC0A1FC618634CDEAC67E7AEBAAE3DECCDE814B9A724D8772E5F3DE259DAB5
+ 3BAFD36FE5D2297CCFDBE4DE756177EA6ED320EFCCD57BAFD36F225928EFCCB5
+ 7B4EDD6C6BFD37EB3C9D7A99E1729ECB8A9FDC9B3030FC0C3FC3FFDFE327B18B
+ C430798D9F1CE3D76D198D5FDEFFF5FC81C97F98FC8789FF0C3FC3CFF033F90F
+ 93FF30F90F93FF30F90F13FF197E869FE167F21F26FFF9F7E73F8C31C618638C
+ 31C61863FF3563E67F98F91FE6FD97E167F8197E66FE8799FF61BE7F31F90F93
+ FF30F19FE167F8197E26FF61EB55FA0DF4F8DC259479F63CA5ACB4F4AC3E5EAE
+ BAF53DFFB99796F6213129022D8C9F8D26EE1F8DFC628623BFBDC3917F8CF73B
+ D7ED4EB19DA775F037B4D637545461D5ABFCA14F2F17CBFF454C0CF4D93BEC59
+ DFE85E4888CAFBED72BB6D35BE4D808691F87FCF551AFC3D4658B7E811DA7503
+ 662BAA819D53452E1B1CC3745AE868CA32FF797EFEAFDC3587825EF48BEE5DC6
+ CBE81B37B24AA362BD91C72E77DE3A94B96C705AAF64ADA42CABFC87F87AFF5D
+ 7DF93EE377056FABF4F07D16F2891BC1EFBC928E81ED7C1458F4C7CF876733D0
+ F87D7E027D84D3DE15BE43FE0747F13DAF4FB45BB66E0B5D2DBAF90F2545A27E
+ 427C5C547E22E7E98EBE74F293F8BE2861B6D03E2A0E7FBFA85E7174F293B169
+ E2FE80AAFB1336C228AABD2D7883FB35577F784C273F1953C9D82441FE223AF9
+ B3287E6F09F2BB15D199FF10FFF911E707ECFB93D848622467CCE4B4B2F232F4
+ E1DB87AA6399EF1EE0316138277F0E9DF9CFCBF41BB90BE2677E66DF9F8C4D64
+ 8CE21CB3388DB04F3F3A55E078D6275A78FF9546FEE019E43E95E43392883F1D
+ A758F9D0FD2DCDA8ABA161DFA85EB725C0FF50D358439B6E7E451545B0FAA16D
+ 80A0BC8D2B7F7827307F28EDF063FB5120A3DF88A265ACAEE2B6D1218CE462BC
+ 6C7ED8CFD91290BF218710DB756A8DD4944186A66BA9ADE9B6C989D4A1448CFC
+ B994B0AB3756D3847A604A1A4ACA1D26B5F7C15CD922B067759C6C350AB32B43
+ 3D32059602E8B4D4D1729EE1E84BF2191C131F63D6E23E15CAC17D3DAED3642B
+ 1F0D23DC5799DF00C418638C31EB7F98EF5FCCF72F66FE9FE167F8197E66FD0F
+ B3FE8759FFC3E43F4CFEC3C47F869FE167F8E575FDCFF95B28E7D41DF420E96E
+ 8592EF66B93BFAD4FBF53F778EDEFD7060730E9AF5BFD76894DF07D47FD8274A
+ 03867F7AD7D5E965AC4587FDFEDAFA030D592C8D7A953FB8DB0FB01C3FEE7EA0
+ E7904FCF7AF4CA474254DEC3ADE076F336DB02D4D55BD58BF53FDD3B8E69D1B6
+ EBF50D98ADA806764E15593B3E09D3D2B191E9FA9F6769B77383035FBCB0E99D
+ 5F26063B5B65D64E8FD72B2AF590D9FA1FE2EBF67DBF7271F51F5588F71554E3
+ 75F22C401EDE85BCFB4B2CDA1DF60150A43D7E669EB883BC7D3E70F10C1F5388
+ B273CAD1E1E3A5A8679F7FEAE088D9138E94A2CC876568E4389E3AB8E5676BE9
+ F6A47DFD4F6C780EB2E17CEE232BD8A9BFE48CFFF9E358451D1C3C2AD8CBCA2A
+ BE0567E794516DC4590753C763F4AEFFC1F19DC4484E063BEC3384B9BC1C55D5
+ E1E8895274E8E83FFB8825FE594AB507E7B5DD7B15D0BAFE878C4D24BEF3FA38
+ 79DEBCBC557F5D1BEF23ED40DA83F7BAEEBDF2695DFF43C654322EF18B2BA40E
+ 7F269756E33F9C849F3B1FF64A7E5AD7FFE07C40203F79BEA40D789F3DD9E7EC
+ 2580DF2D9FD6F53F82FC871D6738FD87BD4DFA6F3C3EE6DC9F4F1DDCF2E95DFF
+ 73FE56EECFD35F7FE68DEFF11C71867093389A78F49F7D541D0E576F879AFAAF
+ 34F207FB5E2953A97CA692818C4D24BEB38DB403F177C2CA59AFFB9965C87304
+ 77FCB4B0DA45FBFA1F1DC38186F8B9DDE6E420631389EF097F72C719E233E4B9
+ 13769F09D5C6E0876A1A2D695FFFC352D480666DB705F0E66D646CE21767483B
+ F03E77AC528BF611A340460B22D4700EDCD9E14918C9C56A91BFA18EB637D7A9
+ A899CB741D8AA6B68D26CE23491D4AC4602F25ECAA6A4DEBC5FA1F45251DE5E6
+ ED77FAE071285B04F62C4BAB9DA35465FCDCAB2F0052044D9D9E5A2417C3F588
+ 2339015671A572488C2471A6A2AF320B8018638C3166FD0FF3FD8BF9FEC5CCFF
+ 33FC0C3FC3CFACFF61D6FF30EB7F98FC87C97F98F8CFF033FCB5D18B73D7515E
+ DA152E3D3F7B5D6EF80F6D5D8D362E9DC2A5FD1B829F7B38B9C945FE13B17A2E
+ 5ABD702C9736044F796ADFC5DAB0BEE73FAFCEFFFD74D3B26965BCFC58CFADDA
+ 989BD5A7FC61F9EC292363C28222668D0FF0E8EBE0DCD46FF08036DB437E5DB0
+ 76D10FC57CF80BC7FBF49E636CD8B0A1BA9A8A828D75CB96137C7BCFF770EBD2
+ 8FC552A09DBFAF83439B25BFF86454B2BDF979E280E865BFFA25E1ED526ADF82
+ 3128247034A5DFF176E579DF97CEF54B9B3569D04ED21E15FBC63C6862D2589F
+ DEFCE7666E5ACC2E72FF72DEE71C327F340A9C311C4DFFC10B4D19E341896C93
+ 7DE4189F76410B66052C55525254A02BFFC939791E852F9F5E8D63C5AFFE14EB
+ 045F77BE9A3EDE0B2DC7E7F0A9C317871EEDDAD0153F93A3C211BFE7CECB3E29
+ A01F9607D7BE19E3FBF36D87A5737DB6D3C5FF20291545AE9EC7757FE21F6CC6
+ A963FAA3C35BB7E076CA408F52D2D1E12DE1E8A7B103AA8E937379F81F0EE8E7
+ 6443E7F89577E60A3ABC6D2D5AB3681CD557399FFD11CCCE7B3EA903671BB0FB
+ F4DA5FC71DECDCAE99812CC6DF0749A728FE90C000DC4FFB55FA8C07F5DC79CF
+ BD772CB504FB534145FB78A095F81AC23FCCABA7BBACF287FBC74F56E39F8CF9
+ 89CFF09E7BF7E829CCEF519DDFD3AEAF2CF89F9EBE848E6C5F47F1FFCEE33FC4
+ 57787FD7D41FE19BF9FACF9C2983636DAD5B9BD099FF6426A7A2E8B5BF71F5C1
+ F91CFD97F4555287074967D083E3A729F6E9E306561D9F3FC39BB7FFDEB3EFDE
+ C69EAEFCE744D496D7A2C4CF9F3033277755FC0CAC1E3F17CE1CB993AEFCC1CD
+ AE47377CCF5C7EE317E113347E916302C6AFCF365D5AB5A5EB1B9A92224B61A2
+ 9FFB4F82F207E21F8495F45322B24DFC8BDF73AFF0BD114B4999747E07B46C66
+ A2FBFBC2F1E9950CEFE7FF3C326AD93C5F92BF7D27FB48FF24318688237F2B0F
+ 9AED7376DA588F1D78FB65E5BECC6106BAFA40B3292A2A829B4327BB51831D83
+ 06F4E8D84EBB8186929EAE96E6445FF7D198E91BBFFC79F2E87EBF346F66A4AB
+ A2ACC46ADFDADCD267B063A0936DFB7E0A0AF5672D4753F326AC15BF8D7EC1F7
+ FDA575DDDF5FE8B0A0D9A3AEF3E1CFB66A533FD62A89C01FC3CB3F77CA9073F5
+ C94F18638CB17F8F355051629968A9B1E495DFCEA491D15E4FDB056DF4B4F5E4
+ 94DFECD810E767FB3D7BA62E6C62D09AA52097FCCFB1D0F121CE8FFF67DDB2AF
+ BCF2572A3F7470EB59E60D345972CA4F541AE3D17357536D4D1D39E5A77478A0
+ E395A95696ADC429CF4C4BC3E4D860E73C7EE5C948AFA6746C315099255A1257
+ 0FF9890AA33CBA05627F5297537EA2B25D7D6DF7B56ED8C044514853D4637EA2
+ F2F881F6DB8D34D554E490BFE4607F877DB6C68D4DE4D07FBE2CE969B5A0854E
+ 034D11FCDF10F35FC6D7E4D1A417C4B705B20F767E126C6B35544B59495194F8
+ 83E394620B5D2D532C733A34B4A5992D8991FCD8130738A4F76A62D8494B4949
+ DEC6AFF27D23BA44DA9B346E2C87E3EFB7358E9D66E86A2AAACA5DFE33D8E9D9
+ AE4E2D7AAB28CA4F165DC53FD8F98293997E7365902FB3336E6412E66CBDB087
+ 899E26C8A1B5C092CB1747C618638C3131CDD5011AB83980567D395F549BE20F
+ 1DE3B640425A3C3C23C2DBF15303A0A3ACCE178B7D34743A1307CFCEC603E214
+ D937D9BFFA3DA47DBE38D6BA35C0B1BD90C05B365BBBC3209ECEF36BB2A6E666
+ 2619A7FFC8C3429452F7A27389064850F9E4183987B6F3311B6114C26F84CFB9
+ 83F59152EA9E8FB88CEF02CB3F64F01D97FF91B6F3311B6114C4AFA5A5C91AD8
+ BF8F1E56E30AB9373E14A1F987A0F2F1B143E41CFACEEFA34718C5F129BFA1D0
+ F9742CE4F2969D1A0B4FFC874227BACFAF8DF90F01EBF8AD7010979B8DF5F0E0
+ 568823F795D5F9B531A3C6A0D0A333E877C7326C24FBF319638C31C61863ECBF
+ 605A6A00E68DC0C45C0F4CF9AA11CBCCCC50C7D2CCA8618BBA88A34C630D1590
+ D894E890EEA09C3A4F211BEB193F9D5AD0F0C5E11D73F20F452FFB5617719499
+ BBD25B6191962AA848827F683750C16516A6CE63217E3AB5400FFD11B51825EE
+ 0D13A8843DA12866C74AB477FB0A4A649BECE33C87A7DCE215C31582701D54EB
+ 037FCCCE1034C1DF0BF90D73A344B6C93E21FC444592A8C390AEA09AF28B4261
+ CA6C05C44FC9BF3544095B7F4107772E15A8DD9BE6A331C39DD1082F1B4A649B
+ EC3B18B11CB3870AE2E7AC43AD7DC9B3136845F9416194AF02E2A7C8B1DA2862
+ D56414113A47A036AE988E3C5CAD91B34D5B4A649BEC8B0C9B8BF685070AE3A7
+ 7C89F407617DBA81BA226BA44363BD510E8D1B8FB06F6430D8B6A1E5209B862D
+ 88E60FD5ED14E5A75814E9CB42917ECAD535AE218A58390145ACF9995B6B6756
+ 69E3B229C8D3AD0B72E9D90EB9D8B6A3B6372E9B8A7686CD431158FB36FF268C
+ 1F4BE109894B82F82D8DD48C1E6EED7607EBE383CD5D3E9E5ED6EE4BCA92B66C
+ 7D4D5ED80A2507B543E78E47A3F3497BB8742E692F3A7722169D4B8913A8B327
+ E2D0A963FBD1E9C3DBD0F5703774734337742FDC06FDB9CE9BAA03D1E19F1484
+ F1E7617E1321FC26983D0F0BDDDFDC15A52E6D874E04B5E5524A704794919AC0
+ 39A721B62EA5EC460F227A21721FA22CAC236B2BEB304A41481DE8E13F9D148B
+ A68EFDE76742C836D927889FAD236B87A31D23152AEA308DD44141AEF8899267
+ EB53FC115575902FFE875BBA527520ED40E21A8ED9F2C54FEEBDB1338AF4A9E0
+ 3F315B4EF97D197EC1FCF142F9CF24C7A239D346A2FF4D1C42896C937D9CFC99
+ 52E6CF0CEF8ACE2C6BCF87DF0AA51F8F14CA9F9E7A08A5FCB917A51CD95321BC
+ 4DF6B18F5F4EDE8EB276384A959FE8465867AA0EA41D4E558A6CA76D1981328E
+ 6D471927F60AD5C593D545D8EFC404086497243FD50E389E3D08AFAECCEDF628
+ 7387530D72AEA6ACED0E42D925CD2F0B31FCFF0EFE473BED514E942B2D22F792
+ 24FFA3084794FF240D957DFB488B0A9E66A0ECEDB612E37FB2D70B957E7D85E8
+ B2F2B292AA3690167F696989C4F41D8BCBA4CE5F8E7273EEA1278FEE4A44CF72
+ B3B8EBC0F0FFCBFD07A1EFDF4B25A6B2EFA5F4FB3F6EF7C7D97724A2BC2799B4
+ FBFFE387B7D0A3AC9B1211E94BA534FB7F7979B94445B7FFCB373F8DFEB3A933
+ 8AF2637D8DF6832F29BF287CC4DC6CDDC1FC46F5BDFF666DE9F279FE20EDAEBD
+ DB80A5574730E8DF191A137975023D4D5560D5F7F849E68F473936167B697A75
+ FE97F4E56FDF8B25CA4FE5CF39A75169FE2BACD752D62B54907BBE2A7F96047F
+ 451D9CD09398015803A52E722F8E77B0DAF21BE26BAF603D93B1EE627EB17F7F
+ B5B2A2028B7C83C13295B18CB5D515995FDCC298D4EDFFFC8B3056
}
end
object SynSQLSynUsed: TSynSQLSyn
diff --git a/source/main.pas b/source/main.pas
index 37875549..32263987 100644
--- a/source/main.pas
+++ b/source/main.pas
@@ -248,7 +248,7 @@ type
//procedure actTableToolsExecute(Sender: TObject);
//procedure actPrintListExecute(Sender: TObject);
//procedure actCopyTableExecute(Sender: TObject);
- //procedure ShowStatusMsg(Msg: String=''; PanelNr: Integer=6);
+ procedure ShowStatusMsg(Msg: String=''; PanelNr: Integer=6);
//procedure actExecuteQueryExecute(Sender: TObject);
//procedure actCreateDatabaseExecute(Sender: TObject);
//procedure actDataCancelChangesExecute(Sender: TObject);
@@ -296,7 +296,7 @@ type
//procedure PageControlMainChange(Sender: TObject);
//procedure PageControlMainChanging(Sender: TObject; var AllowChange: Boolean);
//procedure PageControlHostChange(Sender: TObject);
- //procedure ValidateControls(Sender: TObject);
+ procedure ValidateControls(Sender: TObject);
//procedure ValidateQueryControls(Sender: TObject);
//procedure DataGridBeforePaint(Sender: TBaseVirtualTree;
// TargetCanvas: TCanvas);
@@ -665,7 +665,7 @@ type
FDataGridLastClickedColumnLeftPos: Integer;
//FDataGridSortItems: TSortItems;
FSnippetFilenames: TStringList;
- //FConnections: TDBConnectionList;
+ FConnections: TDBConnectionList;
FTreeClickHistory: TNodeArray;
FOperationTicker: Cardinal;
FOperatingGrid: TBaseVirtualTree;
@@ -735,7 +735,7 @@ type
//function RunQueryFile(Filename: String; Encoding: TEncoding; Conn: TDBConnection;
// ProgressDialog: IProgressDialog; FilesizeSum: Int64; var CurrentPosition: Int64): Boolean;
//procedure SetLogToFile(Value: Boolean);
- //procedure StoreLastSessions;
+ procedure StoreLastSessions;
//function HandleUnixTimestampColumn(Sender: TBaseVirtualTree; Column: TColumnIndex): Boolean;
//function InitTabsIniFile: TIniFile;
//procedure StoreTabs;
@@ -771,7 +771,7 @@ type
//
property AppVerRevision: Integer read FAppVerRevision;
property AppVersion: String read FAppVersion;
- //property Connections: TDBConnectionList read FConnections;
+ property Connections: TDBConnectionList read FConnections;
property Delimiter: String read FDelimiter write SetDelimiter;
//property FocusedTables: TDBObjectList read FFocusedTables;
//function GetAlternatingRowBackground(Node: PVirtualNode): TColor;
@@ -783,7 +783,7 @@ type
//procedure PopupQueryLoadRemoveAbsentFiles(Sender: TObject);
//procedure PopupQueryLoadRemoveAllFiles(Sender: TObject);
//procedure SessionConnect(Sender: TObject);
- //function InitConnection(Params: TConnectionParameters; ActivateMe: Boolean; var Connection: TDBConnection): Boolean;
+ function InitConnection(Params: TConnectionParameters; ActivateMe: Boolean; var Connection: TDBConnection): Boolean;
//procedure ConnectionsNotify(Sender: TObject; const Item: TDBConnection; Action: TCollectionNotification);
//function ActiveGrid: TVirtualStringTree;
//function GridResult(Grid: TBaseVirtualTree): TDBQuery;
@@ -864,7 +864,7 @@ uses
{ TMainForm }
-{procedure TMainForm.ShowStatusMsg(Msg: String=''; PanelNr: Integer=6);
+procedure TMainForm.ShowStatusMsg(Msg: String=''; PanelNr: Integer=6);
var
PanelRect: TRect;
begin
@@ -876,16 +876,10 @@ begin
if (PanelNr = 6) and (not IsWine) then begin
// Immediately repaint this special panel, as it holds critical update messages,
// while avoiding StatusBar.Repaint which refreshes all panels
- SendMessage(StatusBar.Handle, SB_GETRECT, PanelNr, Integer(@PanelRect));
- StatusBar.OnDrawPanel(StatusBar, StatusBar.Panels[PanelNr], PanelRect);
- InvalidateRect(StatusBar.Handle, PanelRect, False);
- // Alternatives:
- //RedrawWindow(StatusBar.Handle, @PanelRect, 0, RDW_UPDATENOW);
- //UpdateWindow(StatusBar.Handle);
- //StatusBar.Repaint;
+ StatusBar.Repaint;
end;
end;
-end;}
+end;
{procedure TMainForm.StatusBarClick(Sender: TObject);
@@ -1179,17 +1173,17 @@ begin
end;}
-{procedure TMainForm.StoreLastSessions;
+procedure TMainForm.StoreLastSessions;
var
OpenSessions, SessionPaths, SortedSessions: TStringList;
Connection: TDBConnection;
- JumpTask: TJumpTask;
+ //JumpTask: TJumpTask;
SessionPath: String;
i: Integer;
LastConnect: TDateTime;
begin
// Store names of open sessions
- OpenSessions := TStringList.Create;
+ {OpenSessions := TStringList.Create;
for Connection in Connections do
OpenSessions.Add(Connection.Parameters.SessionPath);
AppSettings.WriteString(asLastSessions, Implode(DELIM, OpenSessions));
@@ -1227,8 +1221,8 @@ begin
except
on E:Exception do
LogSQL(E.Message, lcError);
- end;
-end;}
+ end;}
+end;
{procedure TMainForm.FormAfterMonitorDpiChanged(Sender: TObject; OldDPI,
@@ -1599,9 +1593,9 @@ begin
SelectedTableForeignKeys := TForeignKeyList.Create;
SelectedTableTimestampColumns := TStringList.Create;}
- {// Set up connections list
+ // Set up connections list
FConnections := TDBConnectionList.Create;
- FConnections.OnNotify := ConnectionsNotify;}
+ //FConnections.OnNotify := ConnectionsNotify;
FTreeRefreshInProgress := False;
FGridCopying := False;
@@ -1654,19 +1648,19 @@ end;
procedure TMainForm.AfterFormCreate;
-{var
+var
LastSessions, FileNames: TStringlist;
Connection: TDBConnection;
LoadedParams, ConnectionParams: TConnectionParameters;
LastUpdatecheck, LastStatsCall, LastConnect: TDateTime;
UpdatecheckInterval, i: Integer;
LastActiveSession, Environment, RunFrom: String;
- frm : TfrmUpdateCheck;
- StatsCall: THttpDownload;
+ //frm : TfrmUpdateCheck;
+ //StatsCall: THttpDownload;
SessionPaths: TStringlist;
DlgResult: TModalResult;
- Tab: TQueryTab;
- SessionManager: TConnForm;}
+ //Tab: TQueryTab;
+ //SessionManager: TConnForm;
begin
{// Check for connection parameters on commandline or show connections form.
if AppSettings.ReadBool(asUpdatecheck) then begin
@@ -1735,17 +1729,17 @@ begin
end;
end;}
- {ConnectionParams := nil;
+ ConnectionParams := nil;
RunFrom := '';
ParseCommandLine(GetCommandLine, ConnectionParams, FileNames, RunFrom);
- // Delete scheduled task from previous
+ {// Delete scheduled task from previous
if RunFrom = 'scheduler' then begin
DeleteRestartTask;
if HasDonated(False) <> nbTrue then begin
apphelpers.ShellExec(APPDOMAIN + 'after-updatecheck?rev=' + AppVerRevision.ToString);
end;
- end;
+ end;}
if ConnectionParams <> nil then begin
// Minimal parameter for command line mode is hostname
@@ -1754,7 +1748,7 @@ begin
except on E:Exception do
ErrorDialog(E.Message);
end;
- end else if AppSettings.ReadBool(asAutoReconnect) then begin
+ end;{ else if AppSettings.ReadBool(asAutoReconnect) then begin
// Auto connection via preference setting
// Do not autoconnect if we're in commandline mode and the connection was not successful
LastSessions := Explode(DELIM, AppSettings.ReadString(asLastSessions));
@@ -2086,7 +2080,7 @@ begin
//Dialog := TConnForm.Create(Self);
//Dialog.ShowModal;
//Dialog.Free;
- ShowMessage('Showing session manager...');
+ MessageDialog('Showing session manager...', mtWarning, [mbCancel, mbAbort, mbAll, mbClose, mbIgnore]);
end;
{procedure TMainForm.actDisconnectExecute(Sender: TObject);
@@ -3793,7 +3787,7 @@ end;}
Receive connection parameters and create a connection tree node
Paremeters are either sent by connection-form or by commandline.
}
-{function TMainform.InitConnection(Params: TConnectionParameters; ActivateMe: Boolean; var Connection: TDBConnection): Boolean;
+function TMainform.InitConnection(Params: TConnectionParameters; ActivateMe: Boolean; var Connection: TDBConnection): Boolean;
var
RestoreLastActiveDatabase: Boolean;
StartupScript, LastActiveDatabase: String;
@@ -3803,9 +3797,9 @@ var
begin
Connection := Params.CreateConnection(Self);
Connection.OnLog := LogSQL;
- Connection.OnConnected := ConnectionReady;
+ {Connection.OnConnected := ConnectionReady;
Connection.OnDatabaseChanged := DatabaseChanged;
- Connection.OnObjectnamesChanged := ObjectnamesChanged;
+ Connection.OnObjectnamesChanged := ObjectnamesChanged;}
try
Connection.Active := True;
// We have a connection
@@ -3821,7 +3815,7 @@ begin
AppSettings.WriteString(asLastConnect, DateTimeToStr(Now));
end;
- if ActivateMe then begin
+ {if ActivateMe then begin
// Set focus on last uses db. If not wanted or db is gone, go to root node at least
RestoreLastActiveDatabase := AppSettings.ReadBool(asRestoreLastUsedDB);
AppSettings.SessionPath := Params.SessionPath;
@@ -3839,7 +3833,7 @@ begin
SelectNode(DBtree, SessionNode);
DBtree.Expanded[SessionNode] := True;
end;
- end;
+ end;}
// Process startup script
StartupScript := Trim(Connection.Parameters.StartupScriptFilename);
@@ -3871,10 +3865,10 @@ begin
// Apply favorite object paths
AppSettings.SessionPath := Params.SessionPath;
Connection.Favorites.Text := AppSettings.ReadString(asFavoriteObjects);
- actFavoriteObjectsOnly.Checked := False;
+ //actFavoriteObjectsOnly.Checked := False;
// Tree node filtering needs a hit once when connected
- editDatabaseTableFilterChange(Self);
+ //editDatabaseTableFilterChange(Self);
except
on E:EDbError do begin
@@ -3893,7 +3887,7 @@ begin
StoreLastSessions;
ValidateControls(Connection);
ShowStatusMsg;
-end;}
+end;
{procedure TMainForm.actDataDeleteExecute(Sender: TObject);
@@ -5986,11 +5980,11 @@ end;}
- highlighted database changes
...
}
-{procedure TMainForm.ValidateControls(Sender: TObject);
+procedure TMainForm.ValidateControls(Sender: TObject);
var
inDataTab, inDataOrQueryTab, inDataOrQueryTabNotEmpty, inGrid: Boolean;
HasConnection, GridHasChanges, EnableTimestamp: Boolean;
- Grid: TVirtualStringTree;
+ Grid: TLazVirtualStringTree;
inSynMemo, inSynMemoEditable: Boolean;
Results: TDBQuery;
RowNum: PInt64;
@@ -5999,7 +5993,7 @@ var
ResultCol: Integer;
begin
// When adding some new TAction here, be sure to apply this procedure to its OnUpdate event
-
+ {
Grid := ActiveGrid;
Conn := ActiveConnection;
HasConnection := Conn <> nil;
@@ -6064,8 +6058,8 @@ begin
ValidateQueryControls(Sender);
UpdateLineCharPanel;
- PageControlTabHighlight(PageControlMain);
-end;}
+ PageControlTabHighlight(PageControlMain);}
+end;
{procedure TMainForm.ValidateQueryControls(Sender: TObject);