@@ -684,6 +684,87 @@ function ( string $sql, array $params ) {
684684 );
685685 }
686686
687+ /**
688+ * Translate and execute a MySQL query in SQLite.
689+ *
690+ * A single MySQL query can be translated into zero or more SQLite queries.
691+ *
692+ * @param string $query Full SQL statement string.
693+ * @param int $fetch_mode PDO fetch mode. Default is PDO::FETCH_OBJ.
694+ * @param array ...$fetch_mode_args Additional fetch mode arguments.
695+ *
696+ * @return mixed Return value, depending on the query type.
697+ *
698+ * @throws WP_SQLite_Driver_Exception When the query execution fails.
699+ *
700+ * TODO:
701+ * The API of this function is not final.
702+ * We should also add support for parametrized queries.
703+ * See: https://github.com/Automattic/sqlite-database-integration/issues/7
704+ */
705+ public function query ( string $ query , $ fetch_mode = PDO ::FETCH_OBJ , ...$ fetch_mode_args ) {
706+ $ this ->flush ();
707+ $ this ->pdo_fetch_mode = $ fetch_mode ;
708+ $ this ->last_mysql_query = $ query ;
709+
710+ try {
711+ // Parse the MySQL query.
712+ $ parser = $ this ->create_parser ( $ query );
713+ $ parser ->next_query ();
714+ $ ast = $ parser ->get_query_ast ();
715+ if ( null === $ ast ) {
716+ throw $ this ->new_driver_exception ( 'Failed to parse the MySQL query. ' );
717+ }
718+
719+ if ( $ parser ->next_query () ) {
720+ throw $ this ->new_driver_exception ( 'Multi-query is not supported. ' );
721+ }
722+
723+ /*
724+ * Determine if we need to wrap the translated queries in a transaction.
725+ *
726+ * [GRAMMAR]
727+ * query:
728+ * EOF
729+ * | (simpleStatement | beginWork) (SEMICOLON_SYMBOL EOF? | EOF)
730+ */
731+ $ child_node = $ ast ->get_first_child_node ();
732+ if (
733+ null === $ child_node
734+ || 'beginWork ' === $ child_node ->rule_name
735+ || $ child_node ->has_child_node ( 'transactionOrLockingStatement ' )
736+ ) {
737+ $ wrap_in_transaction = false ;
738+ } else {
739+ $ wrap_in_transaction = true ;
740+ }
741+
742+ if ( $ wrap_in_transaction ) {
743+ $ this ->begin_wrapper_transaction ();
744+ }
745+
746+ $ this ->execute_mysql_query ( $ ast );
747+
748+ if ( $ wrap_in_transaction ) {
749+ $ this ->commit_wrapper_transaction ();
750+ }
751+ return $ this ->last_return_value ;
752+ } catch ( Throwable $ e ) {
753+ try {
754+ $ this ->rollback_user_transaction ();
755+ $ this ->table_lock_active = false ;
756+ } catch ( Throwable $ rollback_exception ) {
757+ // Ignore rollback errors.
758+ }
759+ if ( $ e instanceof WP_SQLite_Driver_Exception ) {
760+ throw $ e ;
761+ } elseif ( $ e instanceof WP_SQLite_Information_Schema_Exception ) {
762+ throw $ this ->convert_information_schema_exception ( $ e );
763+ }
764+ throw $ this ->new_driver_exception ( $ e ->getMessage (), $ e ->getCode (), $ e );
765+ }
766+ }
767+
687768 /**
688769 * PDO API: Begin a transaction.
689770 *
@@ -840,87 +921,6 @@ public function get_insert_id() {
840921 return $ last_insert_id ;
841922 }
842923
843- /**
844- * Translate and execute a MySQL query in SQLite.
845- *
846- * A single MySQL query can be translated into zero or more SQLite queries.
847- *
848- * @param string $query Full SQL statement string.
849- * @param int $fetch_mode PDO fetch mode. Default is PDO::FETCH_OBJ.
850- * @param array ...$fetch_mode_args Additional fetch mode arguments.
851- *
852- * @return mixed Return value, depending on the query type.
853- *
854- * @throws WP_SQLite_Driver_Exception When the query execution fails.
855- *
856- * TODO:
857- * The API of this function is not final.
858- * We should also add support for parametrized queries.
859- * See: https://github.com/Automattic/sqlite-database-integration/issues/7
860- */
861- public function query ( string $ query , $ fetch_mode = PDO ::FETCH_OBJ , ...$ fetch_mode_args ) {
862- $ this ->flush ();
863- $ this ->pdo_fetch_mode = $ fetch_mode ;
864- $ this ->last_mysql_query = $ query ;
865-
866- try {
867- // Parse the MySQL query.
868- $ parser = $ this ->create_parser ( $ query );
869- $ parser ->next_query ();
870- $ ast = $ parser ->get_query_ast ();
871- if ( null === $ ast ) {
872- throw $ this ->new_driver_exception ( 'Failed to parse the MySQL query. ' );
873- }
874-
875- if ( $ parser ->next_query () ) {
876- throw $ this ->new_driver_exception ( 'Multi-query is not supported. ' );
877- }
878-
879- /*
880- * Determine if we need to wrap the translated queries in a transaction.
881- *
882- * [GRAMMAR]
883- * query:
884- * EOF
885- * | (simpleStatement | beginWork) (SEMICOLON_SYMBOL EOF? | EOF)
886- */
887- $ child_node = $ ast ->get_first_child_node ();
888- if (
889- null === $ child_node
890- || 'beginWork ' === $ child_node ->rule_name
891- || $ child_node ->has_child_node ( 'transactionOrLockingStatement ' )
892- ) {
893- $ wrap_in_transaction = false ;
894- } else {
895- $ wrap_in_transaction = true ;
896- }
897-
898- if ( $ wrap_in_transaction ) {
899- $ this ->begin_wrapper_transaction ();
900- }
901-
902- $ this ->execute_mysql_query ( $ ast );
903-
904- if ( $ wrap_in_transaction ) {
905- $ this ->commit_wrapper_transaction ();
906- }
907- return $ this ->last_return_value ;
908- } catch ( Throwable $ e ) {
909- try {
910- $ this ->rollback_user_transaction ();
911- $ this ->table_lock_active = false ;
912- } catch ( Throwable $ rollback_exception ) {
913- // Ignore rollback errors.
914- }
915- if ( $ e instanceof WP_SQLite_Driver_Exception ) {
916- throw $ e ;
917- } elseif ( $ e instanceof WP_SQLite_Information_Schema_Exception ) {
918- throw $ this ->convert_information_schema_exception ( $ e );
919- }
920- throw $ this ->new_driver_exception ( $ e ->getMessage (), $ e ->getCode (), $ e );
921- }
922- }
923-
924924 /**
925925 * Tokenize a MySQL query and initialize a parser.
926926 *
0 commit comments