|
10 | 10 | use SQLite3;
|
11 | 11 | use WP_MySQL_Lexer;
|
12 | 12 | use WP_MySQL_Parser;
|
| 13 | +use WP_MySQL_Token; |
13 | 14 | use WP_Parser_Grammar;
|
| 15 | +use WP_Parser_Node; |
14 | 16 | use WP_SQLite_PDO_User_Defined_Functions;
|
15 | 17 |
|
16 | 18 | class WP_SQLite_Driver {
|
@@ -502,17 +504,6 @@ public function get_return_value() {
|
502 | 504 | return $this->return_value;
|
503 | 505 | }
|
504 | 506 |
|
505 |
| - /** |
506 |
| - * Executes a MySQL query in SQLite. |
507 |
| - * |
508 |
| - * @param string $query The query. |
509 |
| - * |
510 |
| - * @throws Exception If the query is not supported. |
511 |
| - */ |
512 |
| - private function execute_mysql_query( $query ) { |
513 |
| - //@TODO: Implement the query translation. |
514 |
| - } |
515 |
| - |
516 | 507 | /**
|
517 | 508 | * Executes a query in SQLite.
|
518 | 509 | *
|
@@ -681,6 +672,93 @@ public function rollback() {
|
681 | 672 | return $this->last_exec_returned;
|
682 | 673 | }
|
683 | 674 |
|
| 675 | + /** |
| 676 | + * Executes a MySQL query in SQLite. |
| 677 | + * |
| 678 | + * @param string $query The query. |
| 679 | + * |
| 680 | + * @throws Exception If the query is not supported. |
| 681 | + */ |
| 682 | + private function execute_mysql_query( WP_Parser_Node $ast ) { |
| 683 | + if ( 'query' !== $ast->rule_name ) { |
| 684 | + throw new Exception( sprintf( 'Expected "query" node, got: "%s"', $ast->rule_name ) ); |
| 685 | + } |
| 686 | + |
| 687 | + $children = $ast->get_child_nodes(); |
| 688 | + if ( count( $children ) !== 1 ) { |
| 689 | + throw new Exception( sprintf( 'Expected 1 child, got: %d', count( $children ) ) ); |
| 690 | + } |
| 691 | + |
| 692 | + $ast = $children[0]->get_child_node(); |
| 693 | + switch ( $ast->rule_name ) { |
| 694 | + case 'selectStatement': |
| 695 | + $this->query_type = 'SELECT'; |
| 696 | + $query = $this->translate( $ast->get_child() ); |
| 697 | + $stmt = $this->execute_sqlite_query( $query ); |
| 698 | + $this->set_results_from_fetched_data( |
| 699 | + $stmt->fetchAll( $this->pdo_fetch_mode ) |
| 700 | + ); |
| 701 | + break; |
| 702 | + default: |
| 703 | + throw new Exception( sprintf( 'Unsupported statement type: "%s"', $ast->rule_name ) ); |
| 704 | + } |
| 705 | + } |
| 706 | + |
| 707 | + private function translate( $ast ) { |
| 708 | + if ( null === $ast ) { |
| 709 | + return null; |
| 710 | + } |
| 711 | + |
| 712 | + if ( $ast instanceof WP_MySQL_Token ) { |
| 713 | + return $this->translate_token( $ast ); |
| 714 | + } |
| 715 | + |
| 716 | + if ( ! $ast instanceof WP_Parser_Node ) { |
| 717 | + throw new Exception( 'translate_query only accepts WP_MySQL_Token and WP_Parser_Node instances' ); |
| 718 | + } |
| 719 | + |
| 720 | + $rule_name = $ast->rule_name; |
| 721 | + switch ( $rule_name ) { |
| 722 | + case 'qualifiedIdentifier': |
| 723 | + case 'dotIdentifier': |
| 724 | + return $this->translate_sequence( $ast->get_children(), '' ); |
| 725 | + case 'textStringLiteral': |
| 726 | + if ( $ast->has_child_token( WP_MySQL_Lexer::DOUBLE_QUOTED_TEXT ) ) { |
| 727 | + return WP_SQLite_Token_Factory::double_quoted_value( |
| 728 | + $ast->get_child_token( WP_MySQL_Lexer::DOUBLE_QUOTED_TEXT )->value |
| 729 | + )->value; |
| 730 | + } |
| 731 | + if ( $ast->has_child_token( WP_MySQL_Lexer::SINGLE_QUOTED_TEXT ) ) { |
| 732 | + return WP_SQLite_Token_Factory::raw( |
| 733 | + $ast->get_child_token( WP_MySQL_Lexer::SINGLE_QUOTED_TEXT )->value |
| 734 | + )->value; |
| 735 | + } |
| 736 | + // Fall through to the default case. |
| 737 | + |
| 738 | + default: |
| 739 | + return $this->translate_sequence( $ast->get_children() ); |
| 740 | + } |
| 741 | + } |
| 742 | + |
| 743 | + private function translate_token( WP_MySQL_Token $token ) { |
| 744 | + switch ( $token->id ) { |
| 745 | + case WP_MySQL_Lexer::EOF: |
| 746 | + return null; |
| 747 | + case WP_MySQL_Lexer::IDENTIFIER: |
| 748 | + return '"' . trim( $token->value, '`"' ) . '"'; |
| 749 | + default: |
| 750 | + return $token->value; |
| 751 | + } |
| 752 | + } |
| 753 | + |
| 754 | + private function translate_sequence( array $nodes, string $separator = ' ' ): string { |
| 755 | + $parts = array(); |
| 756 | + foreach ( $nodes as $node ) { |
| 757 | + $parts[] = $this->translate( $node ); |
| 758 | + } |
| 759 | + return implode( $separator, $parts ); |
| 760 | + } |
| 761 | + |
684 | 762 | /**
|
685 | 763 | * This method makes database directory and .htaccess file.
|
686 | 764 | *
|
@@ -745,6 +823,44 @@ private function flush() {
|
745 | 823 | $this->executed_sqlite_queries = array();
|
746 | 824 | }
|
747 | 825 |
|
| 826 | + /** |
| 827 | + * Method to set the results from the fetched data. |
| 828 | + * |
| 829 | + * @param array $data The data to set. |
| 830 | + */ |
| 831 | + private function set_results_from_fetched_data( $data ) { |
| 832 | + if ( null === $this->results ) { |
| 833 | + $this->results = $data; |
| 834 | + } |
| 835 | + if ( is_array( $this->results ) ) { |
| 836 | + $this->num_rows = count( $this->results ); |
| 837 | + $this->last_select_found_rows = count( $this->results ); |
| 838 | + } |
| 839 | + $this->return_value = $this->results; |
| 840 | + } |
| 841 | + |
| 842 | + /** |
| 843 | + * Method to set the results from the affected rows. |
| 844 | + * |
| 845 | + * @param int|null $override Override the affected rows. |
| 846 | + */ |
| 847 | + private function set_result_from_affected_rows( $override = null ) { |
| 848 | + /* |
| 849 | + * SELECT CHANGES() is a workaround for the fact that |
| 850 | + * $stmt->rowCount() returns "0" (zero) with the |
| 851 | + * SQLite driver at all times. |
| 852 | + * Source: https://www.php.net/manual/en/pdostatement.rowcount.php |
| 853 | + */ |
| 854 | + if ( null === $override ) { |
| 855 | + $this->affected_rows = (int) $this->execute_sqlite_query( 'select changes()' )->fetch()[0]; |
| 856 | + } else { |
| 857 | + $this->affected_rows = $override; |
| 858 | + } |
| 859 | + $this->return_value = $this->affected_rows; |
| 860 | + $this->num_rows = $this->affected_rows; |
| 861 | + $this->results = $this->affected_rows; |
| 862 | + } |
| 863 | + |
748 | 864 | /**
|
749 | 865 | * Error handler.
|
750 | 866 | *
|
|
0 commit comments