PluginProbe
SQLite Database Integration / 3.1.0
SQLite Database Integration v3.1.0
3.1.0 3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 All 33 releases
← All changes | wp-includes/database/sqlite/class-wp-sqlite-information-schema-builder.php +103 -19 2.2.23 → 3.1.0 View file →
@@ -5,8 +5,10 @@
5 5 *
6 6 * This class builds and maintains MySQL INFORMATION_SCHEMA tables in SQLite.
7 7 * It consumes the AST of MySQL DDL queries and records the schema information
8 8 * in SQLite tables that emulate the MySQL INFORMATION_SCHEMA.
9 + *
10 + * @access private
9 11 */
10 12 class WP_SQLite_Information_Schema_Builder {
11 13 /**
12 14 * The name of the database that is saved in the information schema tables.
@@ -447,8 +449,9 @@
447 449 ('utf8_general_ci', 'utf8', 33, 'Yes', 'Yes', 1, 'PAD SPACE'),
448 450 ('utf8_unicode_ci', 'utf8', 192, '', 'Yes', 8, 'PAD SPACE'),
449 451 ('utf8mb4_bin', 'utf8mb4', 46, '', 'Yes', 1, 'PAD SPACE'),
450 452 ('utf8mb4_unicode_ci', 'utf8mb4', 224, '', 'Yes', 8, 'PAD SPACE'),
453 + ('utf8mb4_unicode_520_ci', 'utf8mb4', 246, '', 'Yes', 8, 'PAD SPACE'),
451 454 ('utf8mb4_0900_ai_ci', 'utf8mb4', 255, 'Yes', 'Yes', 0, 'NO PAD')
452 455 )";
453 456 default:
454 457 return null;
@@ -1502,15 +1505,16 @@
1502 1505 * @param int $position The ordinal position of the column in the table.
1503 1506 * @return array Column data for the information schema.
1504 1507 */
1505 1508 private function extract_column_data( string $table_name, string $column_name, WP_Parser_Node $node, int $position ): array {
1506 - $default = $this->get_column_default( $node );
1509 + list ( $data_type, $column_type ) = $this->get_column_data_types( $node );
1510 +
1511 + $default = $this->get_column_default( $node, $data_type, $column_name );
1507 1512 $nullable = $this->get_column_nullable( $node );
1508 1513 $key = $this->get_column_key( $node );
1509 1514 $extra = $this->get_column_extra( $node );
1510 1515 $comment = $this->get_column_comment( $node );
1511 1516
1512 - list ( $data_type, $column_type ) = $this->get_column_data_types( $node );
1513 1517 list ( $charset, $collation ) = $this->get_column_charset_and_collation( $node, $data_type );
1514 1518 list ( $char_length, $octet_length ) = $this->get_column_lengths( $node, $data_type, $charset );
1515 1519 list ( $precision, $scale ) = $this->get_column_numeric_attributes( $node, $data_type );
1516 1520 $datetime_precision = $this->get_column_datetime_precision( $node, $data_type );
@@ -2039,12 +2043,14 @@
2039 2043
2040 2044 /**
2041 2045 * Extract column default value from the "columnDefinition" or "fieldDefinition" AST node.
2042 2046 *
2043 - * @param WP_Parser_Node $node The "columnDefinition" or "fieldDefinition" AST node.
2044 - * @return string The column default as stored in information schema.
2047 + * @param WP_Parser_Node $node The "columnDefinition" or "fieldDefinition" AST node.
2048 + * @param string $data_type The column data type as stored in information schema.
2049 + * @param string $column_name The column name.
2050 + * @return string|null The column default as stored in information schema.
2045 2051 */
2046 - private function get_column_default( WP_Parser_Node $node ): ?string {
2052 + private function get_column_default( WP_Parser_Node $node, string $data_type, string $column_name ): ?string {
2047 2053 $default_attr = null;
2048 2054 foreach ( $node->get_descendant_nodes( 'columnAttribute' ) as $attr ) {
2049 2055 if ( $attr->has_child_token( WP_MySQL_Lexer::DEFAULT_SYMBOL ) ) {
2050 2056 $default_attr = $attr;
@@ -2072,23 +2078,13 @@
2072 2078 // DEFAULT signedLiteral
2073 2079 $signed_literal = $default_attr->get_first_child_node( 'signedLiteral' );
2074 2080 if ( $signed_literal ) {
2075 2081 $literal = $signed_literal->get_first_child_node( 'literal' );
2076 -
2077 - // DEFAULT NULL
2078 - if ( $literal && $literal->has_child_node( 'nullLiteral' ) ) {
2079 - return null;
2082 + if ( null === $literal ) {
2083 + // A signed number, such as "-5", has no "literal" child node.
2084 + return $this->get_value( $signed_literal );
2080 2085 }
2081 -
2082 - // DEFAULT TRUE or DEFAULT FALSE
2083 - if ( $literal && $literal->has_child_node( 'boolLiteral' ) ) {
2084 - $bool_literal = $literal->get_first_child_node( 'boolLiteral' );
2085 - return $bool_literal->has_child_token( WP_MySQL_Lexer::TRUE_SYMBOL ) ? '1' : '0';
2086 - }
2087 -
2088 - // @TODO: MySQL seems to normalize default values for numeric
2089 - // columns, such as 1.0 to 1, 1e3 to 1000, etc.
2090 - return $this->get_value( $signed_literal );
2086 + return $this->get_literal_default( $literal, $data_type, $column_name );
2091 2087 }
2092 2088
2093 2089 // DEFAULT (expression) - MySQL 8.0.13+ supports exprWithParentheses
2094 2090 $expr_with_parens = $default_attr->get_first_child_node( 'exprWithParentheses' );
@@ -2096,8 +2092,96 @@
2096 2092 return $this->serialize_mysql_expression( $expr_with_parens );
2097 2093 }
2098 2094
2099 2095 throw new Exception( 'DEFAULT value of this type is not supported.' );
2096 + }
2097 +
2098 + /**
2099 + * Extract and normalize a literal default value.
2100 + *
2101 + * @param WP_Parser_Node $literal The "literal" AST node.
2102 + * @param string $data_type The column data type as stored in information schema.
2103 + * @param string $column_name The column name.
2104 + * @return string|null The default value as stored in information schema.
2105 + */
2106 + private function get_literal_default( WP_Parser_Node $literal, string $data_type, string $column_name ): ?string {
2107 + // DEFAULT NULL
2108 + if ( $literal->has_child_node( 'nullLiteral' ) ) {
2109 + return null;
2110 + }
2111 +
2112 + // DEFAULT TRUE or DEFAULT FALSE
2113 + if ( $literal->has_child_node( 'boolLiteral' ) ) {
2114 + $bool_literal = $literal->get_first_child_node( 'boolLiteral' );
2115 + $bool_value = $bool_literal->has_child_token( WP_MySQL_Lexer::TRUE_SYMBOL ) ? '1' : '0';
2116 + return 'bit' === $data_type ? "b'{$bool_value}'" : $bool_value;
2117 + }
2118 +
2119 + $default = $this->get_value( $literal );
2120 +
2121 + if ( 'bit' === $data_type ) {
2122 + /*
2123 + * @TODO: Validate and normalize defaults from their AST and the full
2124 + * column definition before storing them in the information schema.
2125 + */
2126 + $bit_default = $this->get_bit_default( $default );
2127 + if ( null === $bit_default ) {
2128 + throw WP_SQLite_Information_Schema_Exception::invalid_default_value( $column_name );
2129 + }
2130 + return $bit_default;
2131 + }
2132 +
2133 + /*
2134 + * @TODO: Non-BIT literal defaults are currently stored verbatim. To
2135 + * match MySQL, they should be normalized. For example:
2136 + * - BINARY/VARBINARY defaults stored as 0x hex literals.
2137 + * - Hex and bit literals coerced to raw bytes for text columns and to
2138 + * their decimal value for numeric columns.
2139 + * - The charset introducer dropped from text-literal defaults.
2140 + * - Numeric defaults normalized, e.g. 1.0 to 1 or 1e3 to 1000.
2141 + */
2142 + return $default;
2143 + }
2144 +
2145 + /**
2146 + * Normalize a BIT default value to a MySQL bit literal.
2147 + *
2148 + * The value may be a bit literal, a hex literal, or a decimal.
2149 + *
2150 + * @param string $default_value The BIT default value.
2151 + * @return string|null The default as a bit literal, e.g. "b'101'", or null when not a bit value.
2152 + */
2153 + private function get_bit_default( string $default_value ): ?string {
2154 + $value = strtolower( $default_value );
2155 +
2156 + // An empty string coerces to zero.
2157 + if ( '' === $value ) {
2158 + return "b'0'";
2159 + }
2160 +
2161 + // Bit literal, e.g. b'101' or 0b101.
2162 + if (
2163 + preg_match( "/\Ab'([01]*)'\z/", $value, $matches )
2164 + || preg_match( '/\A0b([01]+)\z/', $value, $matches )
2165 + ) {
2166 + $bits = ltrim( $matches[1], '0' );
2167 + return "b'" . ( '' === $bits ? '0' : $bits ) . "'";
2168 + }
2169 +
2170 + // Hex literal, e.g. x'05' or 0x05.
2171 + if (
2172 + preg_match( "/\Ax'([0-9a-f]*)'\z/", $value, $matches )
2173 + || preg_match( '/\A0x([0-9a-f]+)\z/', $value, $matches )
2174 + ) {
2175 + return "b'" . decbin( hexdec( $matches[1] ) ) . "'";
2176 + }
2177 +
2178 + // Decimal, e.g. 5, or a numeric string literal such as '0'.
2179 + if ( is_numeric( $value ) ) {
2180 + return "b'" . decbin( (int) $value ) . "'";
2181 + }
2182 +
2183 + return null;
2100 2184 }
2101 2185
2102 2186 /**
2103 2187 * Extract column nullability from the "columnDefinition" or "fieldDefinition" AST node.