├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── include ├── cachedstatement.h ├── connection.h ├── deferred.h ├── exception.h ├── localparameter.h ├── parameter.h ├── result.h ├── resultfield.h ├── resultrow.h └── statement.h ├── mysql.h └── src ├── Makefile ├── connection.cpp ├── includes.h ├── library.h ├── queryresultfield.h ├── queryresultimpl.h ├── result.cpp ├── resultfield.cpp ├── resultfieldimpl.h ├── resultimpl.h ├── resultrow.cpp ├── statement.cpp ├── statementdatetimeresultfield.h ├── statementdynamicresultfield.h ├── statementintegralresultfield.h ├── statementresultfield.h ├── statementresultimpl.h └── statementresultinfo.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX = /usr 2 | INCLUDE_DIR = ${PREFIX}/include/reactcpp 3 | LIBRARY_DIR = ${PREFIX}/lib 4 | 5 | all: 6 | $(MAKE) -C src all 7 | 8 | static: 9 | $(MAKE) -C src static 10 | 11 | shared: 12 | $(MAKE) -C src shared 13 | 14 | clean: 15 | $(MAKE) -C src clean 16 | 17 | install: 18 | mkdir -p ${INCLUDE_DIR}/mysql 19 | mkdir -p ${LIBRARY_DIR} 20 | cp -f mysql.h ${INCLUDE_DIR} 21 | cp -fr include/* ${INCLUDE_DIR}/mysql 22 | cp -f src/libreactcpp-mysql.so ${LIBRARY_DIR} 23 | cp -f src/libreactcpp-mysql.a ${LIBRARY_DIR} 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | REACT-CPP-MYSQL 2 | =============== 3 | 4 | C++ asynchronous mysql library on top of the REACT-CPP library. Uses lambdas and callbacks to return query results. 5 | 6 | REACT-CPP-MYSQL is a MySQL library using REACT-CPP to manage an asynchronous connection to MySQL. 7 | 8 | ABOUT 9 | ===== 10 | 11 | REACT-CPP-MYSQL library is created and maintained by Copernica (www.copernica.com). 12 | Do you appreciate our work and are you looking for other high quality solutions? 13 | 14 | Then check out our other solutions: 15 | 16 | * PHP-CPP (www.php-cpp.com) 17 | * PHP-JS (www.php-js.com) 18 | * Copernica Marketing Suite (www.copernica.com) 19 | * MailerQ MTA (www.mailerq.com) 20 | * Responsive Email web service (www.responsiveemail.com) 21 | 22 | Connection class 23 | ================ 24 | 25 | The React::MySQL::Connection class is used to establish a connection to a MySQL daemon. 26 | 27 | ```c++ 28 | #include 29 | #include 30 | #include 31 | 32 | /** 33 | * Main application procedure 34 | */ 35 | int main() 36 | { 37 | // create an event loop 38 | React::MainLoop loop; 39 | 40 | // create the connection to MySQL 41 | React::MySQL::Connection connection(&loop, "mysql.example.org", "example user", "example password", "example database"); 42 | 43 | // get connection updates 44 | connection.onConnected([](const char *error) { 45 | /** 46 | * This function will be called upon success/failure of the connection 47 | * 48 | * The error parameter will be set to NULL upon success, and set 49 | * to a c-string containing a description of the problem otherwise. 50 | */ 51 | if (error) std::cout << "Failed to connect: " << error << std::endl; 52 | else std::cout << "Connected" << std::endl; 53 | }); 54 | 55 | /** 56 | * At this point, the connection is still being established. It is, 57 | * however, perfectly valid to start sending queries to MySQL. 58 | * 59 | * The queries will be executed after the connection has been established. 60 | * If the connection somehow fails, all the failure callbacks will be 61 | * called with a description of the error to indicate this condition. 62 | */ 63 | connection.query("SELECT * FROM test LIMIT 10").onSuccess([](React::MySQL::Result&& result) { 64 | // this is a normal select query, affectedRows should be zero 65 | assert(result.affectedRows() == 0); 66 | 67 | // dump all rows to screen 68 | for (auto row : result) 69 | { 70 | // wrap each row in curly braces 71 | std::cout << "{" << std::endl; 72 | 73 | // and dump all fields 74 | for (auto field : row) 75 | { 76 | // field is an std::pair with as first member the name and the second member the field value 77 | std::cout << " " << field.first << " => " << field.second << std::endl; 78 | } 79 | 80 | // close the row 81 | std::cout << "}" << std::endl; 82 | } 83 | 84 | // stop the application 85 | exit(0); 86 | }).onFailure([](const char *error) { 87 | /** 88 | * In case of an error, the error parameter will contain 89 | * a pointer to a description of the error. 90 | */ 91 | std::cout << "Query error: " << error << std::endl; 92 | exit(1); 93 | }); 94 | 95 | // run the event loop 96 | loop.run(); 97 | 98 | // done 99 | return 0; 100 | } 101 | ``` 102 | 103 | Both the result set and the individual rows can be iterated (as shown in the example above) or simply used 104 | as an array. The rows can only be accessed with a numerical index, while the fields can be accessed using 105 | their field index as well as their name: 106 | 107 | ```c++ 108 | connection.query("SELECT a, b, c FROM test").onSuccess([](React::MySQL::Result&& result) { 109 | // loop over the rows by their index 110 | for (size_t i = 0; i < result.size(); ++i) 111 | { 112 | // retrieve the row 113 | auto row = result[i]; 114 | 115 | // the individual fields can be accessed by name or by index 116 | // e.g.: the field 'b', can be accessed by index 1 or 'b': 117 | auto field = row[1]; 118 | auto sameField = row["b"]; 119 | 120 | // fields can be casted to a string, or any of the numeric types 121 | // assuming that 'a' holds a unsigned bigint 122 | unsigned long a = row["a"]; 123 | } 124 | 125 | /** 126 | * It is, of course, also possible to take individual rows 127 | * from the result set in any order. 128 | * 129 | * Note that this can be extremely slow when working with 130 | * larger result sets. Sequential access is the fastest 131 | * way to step through the results. 132 | */ 133 | auto row = result[17]; 134 | }); 135 | ``` 136 | 137 | Prepared Statements 138 | =================== 139 | 140 | When executing a particular query multiple times, or when dealing with unsanitized input (e.g. user input), 141 | the fastest, and most secure, way is to use prepared statements. 142 | 143 | When using prepared statements the query is only sent to the MySQL daemon once and the input and output to 144 | and from the query is done using a binary format. This way the query will only have to be parsed once (the 145 | execution plan is cached on the server) and saves on network overhead. It also completely eliminates the 146 | possibility of SQL injection. 147 | 148 | ```c 149 | #include 150 | #include 151 | #include 152 | 153 | /** 154 | * Min application procedure 155 | */ 156 | int main() 157 | { 158 | // create an event loop 159 | React::MainLoop loop; 160 | 161 | // create the connection to MySQL and clean out the logs 162 | React::MySQL::Connection connection(&loop, "mysql.example.org", "example user", "example password", "example database"); 163 | 164 | // create the statement 165 | React::MySQL::Statement statement(&connection, "INSERT INTO logs (event_time, description) VALUES (NOW(), ?), (NOW(), ?), (NOW(), ?)") 166 | 167 | // catch statement preparation errors 168 | statement.onPrepared([](const char *error) { 169 | /** 170 | * As with the connection class and query function 171 | * the error parameter will be a NULL pointer if the 172 | * statement was correctly initialized. 173 | */ 174 | if (error) 175 | { 176 | std::cout << "Cannot initialize statement: " << error << std::endl; 177 | exit(0); 178 | } 179 | }); 180 | 181 | /** 182 | * When we get here, the statement is not yet initialized 183 | * but we can already start executing it. As with the connection 184 | * class, it will start executing once it is initialized. 185 | * 186 | * The execute method takes as many arguments as there are 187 | * placeholders in the query. The statement was initialized 188 | * with a query containing three placeholders, so we have to 189 | * provide three values. 190 | * 191 | * The values should always be of a similar type as the type 192 | * used in the table. For example, a MySQL column defined as 193 | * 'unsigned int' could receive a uint32_t, while a varchar 194 | * column could receive a const char* or std::string. 195 | * 196 | * This is not a hard requirement, if you send a number where 197 | * the column holds a varchar, conversion will take place on 198 | * the server (causing a little extra overhead). The other way 199 | * around also works, but only if the string actually holds 200 | * numeric data. 201 | */ 202 | statement.execute("first event description", "second event description", "third event description"); 203 | 204 | /** 205 | * We can also select data with prepared statements. 206 | * 207 | * Just as with other functions and classes, we simply 208 | * register a callback function that will receive the data. 209 | */ 210 | React::MySQL::Statement selectStatement(&connection, "SELECT a, b, c FROM test WHERE a BETWEEN ? and ?"); 211 | selectStatement.execute(5, 10).onSuccess([](React::MySQL::Result&& result) { 212 | // this is a normal select query, affectedRows should be zero 213 | assert(result.affectedRows() == 0); 214 | 215 | // dump all rows to screen 216 | for (auto row : result) 217 | { 218 | // wrap each row in curly braces 219 | std::cout << "{" << std::endl; 220 | 221 | // and dump all fields 222 | for (auto field : row) 223 | { 224 | // field is an std::pair with as first member the name and the second member the field value 225 | std::cout << " " << field.first << " => " << field.second << std::endl; 226 | } 227 | 228 | // close the row 229 | std::cout << "}" << std::endl; 230 | } 231 | }); 232 | } 233 | -------------------------------------------------------------------------------- /include/cachedstatement.h: -------------------------------------------------------------------------------- 1 | /** 2 | * CachedStatement.h 3 | * 4 | * This class is a cached statement that is cached 5 | * for the connection. It will not clean up server 6 | * resources when it falls out of scopes. 7 | * 8 | * It assumes that a const char pointer with the 9 | * same address points to the exact same statement 10 | * (i.e. it will not do a string comparison to see 11 | * whether it is really the same statement). it is 12 | * therefor not advised to use cached statements in 13 | * combination with dynamically allocated strings. 14 | * 15 | * @copyright 2014 Copernica BV 16 | */ 17 | 18 | /** 19 | * Set up namespace 20 | */ 21 | namespace React { namespace MySQL { 22 | 23 | /** 24 | * Cached statement class 25 | */ 26 | class CachedStatement 27 | { 28 | private: 29 | /** 30 | * The underlying statement from the connection 31 | */ 32 | Statement *_statement; 33 | public: 34 | /** 35 | * Constructor 36 | * 37 | * @param connection the connection to run the statement on 38 | * @param statement the statement to execute 39 | */ 40 | CachedStatement(Connection *connection, const char *statement) : 41 | _statement(connection->statement(statement)) 42 | {} 43 | 44 | /** 45 | * Execute the statement 46 | * 47 | * Note: the number of arguments must match the number of placeholders 48 | * in the statement string. If not, the statement will not be 49 | * executed and the callback will be invoked immediately with an error. 50 | * 51 | * The following parameter types are supported and map to the following MySQL types 52 | * 53 | * Variable type MySQL column type 54 | * ------------- ----------------- 55 | * signed char TINYINT 56 | * short int SMALLINT 57 | * int INT 58 | * long long int BIGINT 59 | * float FLOAT 60 | * double DOUBLE 61 | * std::string TEXT, CHAR or VARCHAR 62 | * std::vector BLOB, BINARY or VARBINARY 63 | * std::nullptr_t NULL 64 | * 65 | * @param mixed... variable number of arguments of different type 66 | * @param callback the callback to be informed when the statement is executed or failed 67 | */ 68 | template 69 | Deferred& execute(Arguments ...parameters) 70 | { 71 | // pass to implementation 72 | return _statement->execute(std::forward(parameters)...); 73 | } 74 | }; 75 | 76 | /** 77 | * End namespace 78 | */ 79 | }} 80 | -------------------------------------------------------------------------------- /include/connection.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Connection.h 3 | * 4 | * Class representing a connection to a MySQL or MariaDB daemon 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Dependencies 11 | */ 12 | #include 13 | 14 | /** 15 | * Set up namespace 16 | */ 17 | namespace React { namespace MySQL { 18 | 19 | // forward declaration 20 | class Statement; 21 | 22 | /** 23 | * Connection class 24 | */ 25 | class Connection 26 | { 27 | private: 28 | /** 29 | * The loop we work with 30 | */ 31 | React::Loop *_loop; 32 | 33 | /** 34 | * Connection to mysql 35 | */ 36 | MYSQL *_connection; 37 | 38 | /** 39 | * Callback to execute once the connection is established 40 | */ 41 | std::function _connectCallback; 42 | 43 | /** 44 | * Cached prepared statements 45 | */ 46 | std::unordered_map> _statements; 47 | 48 | /** 49 | * Worker for main thread 50 | */ 51 | Worker _master; 52 | 53 | /** 54 | * The worker operating on MySQL 55 | */ 56 | Worker _worker; 57 | 58 | /** 59 | * Retrieve or create a cached prepared statement 60 | * 61 | * @param query the query to use for preparing the statement 62 | */ 63 | Statement *statement(const char *query); 64 | 65 | /** 66 | * Parse the string and replace all placeholders with 67 | * the provided values. 68 | * 69 | * The callback is executed with the result. 70 | * 71 | * @param query the query to parse 72 | * @param callback the callback to give the result 73 | * @param parameters placeholder values 74 | * @param count number of placeholder values 75 | */ 76 | void prepare(const std::string& query, LocalParameter *parameters, size_t count, const std::function& callback); 77 | public: 78 | /** 79 | * Establish a connection to mysql 80 | * 81 | * @param loop the loop to bind to 82 | * @param hostname the hostname to connect to 83 | * @param username the username to login with 84 | * @param password the password to authenticate with 85 | * @param database the database to use 86 | * @param flags connection flags 87 | * @param initialize do we need to initialize (and cleanup) the mysql library 88 | */ 89 | Connection(Loop *loop, const std::string& hostname, const std::string &username, const std::string& password, const std::string& database, uint64_t flags = CLIENT_IGNORE_SIGPIPE | CLIENT_MULTI_STATEMENTS, bool initialize = true); 90 | 91 | /** 92 | * Destructor 93 | */ 94 | virtual ~Connection(); 95 | 96 | /** 97 | * Get a call when the connection succeeds or fails 98 | * 99 | * @param callback the callback that will be informed of the connection status 100 | */ 101 | void onConnected(const std::function& callback); 102 | 103 | /** 104 | * Execute a query 105 | * 106 | * @param query the query to execute 107 | */ 108 | Deferred& query(const std::string& query); 109 | 110 | /** 111 | * Execute a query with placeholders 112 | * 113 | * This works sort-of like a prepared statement, the difference being that the 114 | * query is prepared completely at the client side. This has the advantage of 115 | * allowing placeholders anywhere (including identifiers) as opposed to only 116 | * in WHERE, ON and LIMIT clauses. 117 | * 118 | * The query is sent as a regular query, which means it won't use the binary 119 | * protocol. This comes with some extra network overhead. The query is also 120 | * not cached on the server, as is done with prepared statements. 121 | * 122 | * The following placeholders are supported: 123 | * 124 | * ? escape string data and quote when necessary 125 | * ! escape string data but do not quote 126 | * 127 | * @param query the query to execute 128 | * @param mixed... placeholder values 129 | */ 130 | template 131 | Deferred& execute(const std::string& query, Arguments ...parameters) 132 | { 133 | // if there are no parameters, we don't need to parse anything 134 | if (sizeof...(parameters) == 0) return this->query(query); 135 | 136 | // create the deferred handler 137 | auto deferred = std::make_shared(); 138 | 139 | // prepare the query, then execute it 140 | prepare(query, new LocalParameter[sizeof...(parameters)]{ parameters... }, sizeof...(parameters), [this, deferred](const std::string& query) { 141 | // execute query 142 | auto &result = this->query(query); 143 | 144 | // pass on the completed event 145 | result.onComplete([deferred]() { deferred->complete(); }); 146 | 147 | // only register success and failure if necessary 148 | if (deferred->requireStatus()) 149 | { 150 | result.onSuccess([deferred](Result&& result) { deferred->success(std::move(result)); }); 151 | result.onFailure([deferred](const char *error) { deferred->failure(error); }); 152 | } 153 | }); 154 | 155 | // return the deferred handler 156 | return *deferred; 157 | } 158 | 159 | /** 160 | * Friends and family 161 | */ 162 | friend class Statement; 163 | friend class CachedStatement; 164 | }; 165 | 166 | /** 167 | * End namespace 168 | */ 169 | }} 170 | -------------------------------------------------------------------------------- /include/deferred.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Deferred.h 3 | * 4 | * Object used for registering callbacks to be executed 5 | * at a later point in time. 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | // forward declaration 16 | class Connection; 17 | class Statement; 18 | class Result; 19 | 20 | /** 21 | * Deferred class 22 | */ 23 | class Deferred 24 | { 25 | private: 26 | /** 27 | * Callback to execute on success 28 | */ 29 | std::function _successCallback; 30 | 31 | /** 32 | * Callback to execute on failure 33 | */ 34 | std::function _failureCallback; 35 | 36 | /** 37 | * Callback to execute on completion 38 | */ 39 | std::function _completeCallback; 40 | 41 | /** 42 | * Do we have to go through the trouble of checking for 43 | * success or error? If not, we can save a round-trip 44 | * to the server. 45 | */ 46 | bool requireStatus() 47 | { 48 | // status is only relevant if a success- or failure callback 49 | // has been installed, the complete callback is irrelevant. 50 | return _successCallback || _failureCallback; 51 | } 52 | 53 | /** 54 | * Signal that the command finished successfully 55 | * 56 | * @param parameters relevant parameters 57 | */ 58 | void success(Result&& result) 59 | { 60 | // execute the callbacks 61 | if (_successCallback) _successCallback(std::move(result)); 62 | if (_completeCallback) _completeCallback(); 63 | } 64 | 65 | /** 66 | * Signal that the operation resulted in failure 67 | * 68 | * @param error description of the failure reason 69 | */ 70 | void failure(const char *error) 71 | { 72 | // execute the callbacks 73 | if (_failureCallback) _failureCallback(error); 74 | if (_completeCallback) _completeCallback(); 75 | } 76 | 77 | /** 78 | * Signal that the operation completed, but without 79 | * indicating whether it resulted in success 80 | */ 81 | void complete() 82 | { 83 | // execute the callback 84 | if (_completeCallback) _completeCallback(); 85 | } 86 | public: 87 | /** 88 | * Constructor 89 | */ 90 | Deferred() {} 91 | 92 | /** 93 | * We cannot be copied 94 | */ 95 | Deferred(const Deferred& that) = delete; 96 | 97 | /** 98 | * Nor can we be moved 99 | */ 100 | Deferred(Deferred&& that) = delete; 101 | 102 | /** 103 | * Register a callback to be executed when the operation succeeds 104 | * 105 | * @param callback the callback to execute on success 106 | */ 107 | Deferred& onSuccess(const std::function& callback) 108 | { 109 | // store callback 110 | _successCallback = callback; 111 | return *this; 112 | } 113 | 114 | /** 115 | * Register a callback to be executed when the operation fails 116 | * 117 | * @param callback the callback to execute on failure 118 | */ 119 | Deferred& onFailure(const std::function& callback) 120 | { 121 | // store callback 122 | _failureCallback = callback; 123 | return *this; 124 | } 125 | 126 | /** 127 | * Register a callback to be executed when the operation is finished, 128 | * whether successful or not. 129 | * 130 | * @param callback the callback to execute when the operation completes 131 | */ 132 | Deferred& onComplete(const std::function& callback) 133 | { 134 | // store callback 135 | _completeCallback = callback; 136 | return *this; 137 | } 138 | 139 | // the connection and statement classes may call private methods 140 | friend class Connection; 141 | friend class Statement; 142 | }; 143 | 144 | /** 145 | * End namespace 146 | */ 147 | }} 148 | -------------------------------------------------------------------------------- /include/exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Exception.h 3 | * 4 | * Exception thrown by the REACT-CPP-MYSQL library 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Class definition 16 | */ 17 | class Exception : public React::Exception 18 | { 19 | public: 20 | /** 21 | * Constructor 22 | * 23 | * @param message 24 | */ 25 | Exception(const char *message) : React::Exception(message) {} 26 | 27 | /** 28 | * Destructor 29 | */ 30 | virtual ~Exception() {} 31 | }; 32 | 33 | /** 34 | * End namespace 35 | */ 36 | }} 37 | -------------------------------------------------------------------------------- /include/localparameter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * LocalParameter.h 3 | * 4 | * A parameter in a local prepared-statement-like query. 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Local input parameter 16 | */ 17 | class LocalParameter 18 | { 19 | private: 20 | /** 21 | * The parameter in string representation 22 | */ 23 | std::string _value; 24 | 25 | /** 26 | * Are we representing an integral value? 27 | */ 28 | bool _integral; 29 | 30 | /** 31 | * Buffer for value quoting 32 | */ 33 | char *_buffer; 34 | public: 35 | /** 36 | * Integral constructors 37 | * 38 | * @param value parameter value 39 | */ 40 | LocalParameter(uint8_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 41 | LocalParameter(int8_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 42 | LocalParameter(uint16_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 43 | LocalParameter(int16_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 44 | LocalParameter(uint32_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 45 | LocalParameter(int32_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 46 | LocalParameter(uint64_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 47 | LocalParameter(int64_t value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 48 | LocalParameter(float value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 49 | LocalParameter(double value) : _value(std::to_string(value)), _integral(true), _buffer(nullptr) {} 50 | 51 | /** 52 | * String constructor 53 | * 54 | * @param value parameter value 55 | */ 56 | LocalParameter(const std::string& value) : 57 | _value(value), 58 | _integral(false), 59 | _buffer(static_cast(std::malloc(size()))) 60 | {} 61 | 62 | /** 63 | * Character array constructor 64 | * 65 | * @param value parameter value 66 | */ 67 | LocalParameter(const char *value) : 68 | _value(value), 69 | _integral(false), 70 | _buffer(static_cast(std::malloc(size()))) 71 | {} 72 | 73 | /** 74 | * NULL constructor 75 | * 76 | * @param value 77 | */ 78 | LocalParameter(std::nullptr_t value) : 79 | _value("NULL"), 80 | _integral(true), 81 | _buffer(nullptr) 82 | {} 83 | 84 | /** 85 | * Destructor 86 | */ 87 | virtual ~LocalParameter() 88 | { 89 | std::free(_buffer); 90 | } 91 | 92 | /** 93 | * The maximum size we could need to store 94 | * this parameter inside a query string. 95 | */ 96 | size_t size() const 97 | { 98 | // integral types need not be quoted or escaped 99 | // so their size is as-is, no extra space needed 100 | if (_integral) return _value.size(); 101 | 102 | // otherwise, we might need quotes and each character 103 | // could be escaped to two, so to be safe we return 104 | // double the size plus that of two quote characters 105 | else return _value.size() * 2 + 2; 106 | } 107 | 108 | /** 109 | * Escape the parameter, but do not quote it 110 | * 111 | * @param connection mysql connection to use for escaping 112 | */ 113 | const std::string escape(MYSQL *connection) 114 | { 115 | // integral values don't need escaping 116 | if (_integral) return _value; 117 | 118 | // escape the value 119 | auto length = mysql_real_escape_string(connection, _buffer, _value.c_str(), _value.size()); 120 | 121 | // and wrap it in a string 122 | return std::string(_buffer, length); 123 | } 124 | 125 | /** 126 | * Escape the parameter, and quote it if necessary 127 | * 128 | * @param connection mysql connection to use for escaping 129 | */ 130 | const std::string quote(MYSQL *connection) 131 | { 132 | // integral values need escaping nor quoting 133 | if (_integral) return _value; 134 | 135 | // write the opening quote to the buffer 136 | _buffer[0] = '\''; 137 | 138 | // escape the value and retrieve the size 139 | auto length = mysql_real_escape_string(connection, _buffer + 1, _value.c_str(), _value.size()); 140 | 141 | // add the closing quote and terminating null character 142 | _buffer[length + 1] = '\''; 143 | _buffer[length + 2] = '\0'; 144 | 145 | // and wrap it in a string 146 | return std::string(_buffer, length + 2); 147 | } 148 | }; 149 | 150 | /** 151 | * End namespace 152 | */ 153 | }} 154 | -------------------------------------------------------------------------------- /include/parameter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Parameter.h 3 | * 4 | * Input parameter for MySQL prepared statement 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Input parameter class 16 | */ 17 | class Parameter : public MYSQL_BIND 18 | { 19 | private: 20 | /** 21 | * Templated base constructor 22 | */ 23 | template 24 | Parameter(enum_field_types type, T value) 25 | { 26 | // empty the struct (MySQL dictates it) 27 | memset(this, 0, sizeof(*this)); 28 | 29 | // set the buffer type 30 | buffer_type = type; 31 | 32 | // determine whether the value is unsigned 33 | is_unsigned = std::is_unsigned::value; 34 | 35 | // allocate memory for the value 36 | T *data = static_cast(std::malloc(sizeof(T))); 37 | *data = value; 38 | 39 | // store the value 40 | buffer = data; 41 | } 42 | public: 43 | /** 44 | * Integral constructors 45 | * 46 | * @param value parameter value 47 | */ 48 | Parameter(int8_t value) : Parameter(MYSQL_TYPE_TINY, value) {} 49 | Parameter(uint16_t value) : Parameter(MYSQL_TYPE_SHORT, value) {} 50 | Parameter(int16_t value) : Parameter(MYSQL_TYPE_SHORT, value) {} 51 | Parameter(uint32_t value) : Parameter(MYSQL_TYPE_LONG, value) {} 52 | Parameter(int32_t value) : Parameter(MYSQL_TYPE_LONG, value) {} 53 | Parameter(uint64_t value) : Parameter(MYSQL_TYPE_LONGLONG, value) {} 54 | Parameter(int64_t value) : Parameter(MYSQL_TYPE_LONGLONG, value) {} 55 | Parameter(float value) : Parameter(MYSQL_TYPE_FLOAT, value) {} 56 | Parameter(double value) : Parameter(MYSQL_TYPE_DOUBLE, value) {} 57 | 58 | /** 59 | * String constructor 60 | * 61 | * @param value parameter value 62 | */ 63 | Parameter(const std::string& value) 64 | { 65 | // empty the struct (MySQL dictates it) 66 | memset(this, 0, sizeof(*this)); 67 | 68 | // set the buffer type 69 | buffer_type = MYSQL_TYPE_STRING; 70 | 71 | // allocate memory for the value 72 | char *data = static_cast(std::malloc(value.size())); 73 | std::memcpy(data, value.c_str(), value.size()); 74 | 75 | // store the value and length 76 | buffer = data; 77 | buffer_length = value.size(); 78 | } 79 | 80 | /** 81 | * Binary constructor 82 | */ 83 | Parameter(const std::vector& value) 84 | { 85 | // empty the struct (MySQL dictates it) 86 | memset(this, 0, sizeof(*this)); 87 | 88 | // set the buffer type 89 | buffer_type = MYSQL_TYPE_BLOB; 90 | 91 | // allocate memory for the value 92 | char *data = static_cast(std::malloc(value.size())); 93 | std::memcpy(data, value.data(), value.size()); 94 | 95 | // store the value and length 96 | buffer = data; 97 | buffer_length = value.size(); 98 | } 99 | 100 | /** 101 | * NULL constructor 102 | */ 103 | Parameter(std::nullptr_t value) 104 | { 105 | // empty the struct (MySQL dictates it) 106 | memset(this, 0, sizeof(*this)); 107 | 108 | // set the buffer type 109 | buffer_type = MYSQL_TYPE_NULL; 110 | } 111 | 112 | /** 113 | * Destructor 114 | * 115 | * Do *NOT* make this function virtual. 116 | * 117 | * Making this virtual will create a vtable, 118 | * changing the size of the object which will 119 | * make MySQL very very unhappy. 120 | */ 121 | ~Parameter() 122 | { 123 | // if we have a buffer, free it 124 | std::free(buffer); 125 | } 126 | }; 127 | 128 | /** 129 | * End namespace 130 | */ 131 | }} 132 | -------------------------------------------------------------------------------- /include/result.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Result.h 3 | * 4 | * Class representing a MySQL result set 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | // forward declaration 15 | class ResultImpl; 16 | 17 | /** 18 | * Result class 19 | */ 20 | class Result 21 | { 22 | /** 23 | * The result from MySQL 24 | */ 25 | std::shared_ptr _result; 26 | 27 | /** 28 | * Number of affected rows 29 | */ 30 | size_t _affectedRows = 0; 31 | 32 | /** 33 | * Last insert id, only relevant 34 | * if this is the result of a query 35 | * that inserted into a table with 36 | * an auto_increment column 37 | */ 38 | uint64_t _insertID = 0; 39 | 40 | public: 41 | /** 42 | * Result iterator 43 | */ 44 | class iterator 45 | { 46 | private: 47 | /** 48 | * The result from MySQL 49 | */ 50 | std::shared_ptr _result; 51 | 52 | /** 53 | * The index we are at 54 | */ 55 | size_t _index; 56 | 57 | /** 58 | * Is this iterator pointing to a valid position? 59 | */ 60 | bool valid() const; 61 | public: 62 | /** 63 | * Empty constructor 64 | */ 65 | iterator(); 66 | 67 | /** 68 | * Constructor 69 | * 70 | * @param result mysql result set 71 | * @param index index to start from 72 | */ 73 | iterator(std::shared_ptr result, size_t index); 74 | 75 | /** 76 | * Copy constructor 77 | * 78 | * @param that iterator to copy 79 | */ 80 | iterator(const iterator& that); 81 | 82 | /** 83 | * Assign another iterator 84 | */ 85 | iterator& operator=(const iterator& that); 86 | 87 | /** 88 | * Increment operator 89 | */ 90 | iterator& operator++(); 91 | 92 | /** 93 | * Increment operator (postfix) 94 | */ 95 | iterator operator++(int); 96 | 97 | /** 98 | * Compare with other iterator 99 | * 100 | * @param that iterator to compare with 101 | */ 102 | bool operator==(const iterator& that); 103 | 104 | /** 105 | * Compare with otehr iterator 106 | * 107 | * @param that iterator to compare with 108 | */ 109 | bool operator!=(const iterator& that); 110 | 111 | /** 112 | * Dereference 113 | */ 114 | ResultRow operator*(); 115 | 116 | /** 117 | * Call method on dereferenced row 118 | */ 119 | std::unique_ptr operator->(); 120 | }; 121 | 122 | /** 123 | * Constructor 124 | */ 125 | Result(MYSQL_RES *result); 126 | 127 | /** 128 | * Constructor 129 | */ 130 | Result(std::shared_ptr&& implementation); 131 | 132 | /** 133 | * Constructor for affected rows 134 | * 135 | * @param affectedRows Number of rows affected 136 | * @param insertID ID for the inserted row, if applicable 137 | */ 138 | Result(size_t affectedRows, uint64_t insertID); 139 | 140 | /** 141 | * Invalid constructor 142 | */ 143 | Result(std::nullptr_t result); 144 | 145 | /** 146 | * Move constructor 147 | */ 148 | Result(Result&& that); 149 | 150 | /** 151 | * Results may never be copied 152 | */ 153 | Result(const Result& that) = delete; 154 | 155 | /** 156 | * Destructor 157 | */ 158 | virtual ~Result(); 159 | 160 | /** 161 | * Is this a valid result? 162 | */ 163 | bool valid() const; 164 | 165 | /** 166 | * The number of rows affected 167 | */ 168 | size_t affectedRows() const; 169 | 170 | /** 171 | * ID of the last inserted row, but only 172 | * if the insert was using an auto_increment 173 | */ 174 | uint64_t insertID() const; 175 | 176 | /** 177 | * Get the number of rows in this result 178 | */ 179 | size_t size() const; 180 | 181 | /** 182 | * Retrieve a row at the given offset 183 | * 184 | * This function will throw an exception if no 185 | * row exists at the given index (i.e.: index 186 | * is greater than size()). 187 | * 188 | * @param index row index in the result 189 | * @throws Exception 190 | */ 191 | ResultRow operator [] (size_t index); 192 | 193 | /** 194 | * Retrieve iterator for first row 195 | */ 196 | iterator begin() const; 197 | 198 | /** 199 | * Retrieve iterator past the end 200 | */ 201 | iterator end() const; 202 | }; 203 | 204 | /** 205 | * End namespace 206 | */ 207 | }} 208 | -------------------------------------------------------------------------------- /include/resultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultField.h 3 | * 4 | * A single field from a single row from 5 | * a MySQL result set 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | // forward declaration 16 | class ResultImpl; 17 | class ResultFieldImpl; 18 | 19 | /** 20 | * Result field 21 | */ 22 | class ResultField 23 | { 24 | private: 25 | /** 26 | * The result implementation 27 | */ 28 | std::shared_ptr _result; 29 | 30 | /** 31 | * The field implementation 32 | */ 33 | ResultFieldImpl *_field; 34 | public: 35 | /** 36 | * Constructor 37 | * 38 | * @param result the result implementation 39 | * @param field the field implementation 40 | */ 41 | ResultField(std::shared_ptr result, ResultFieldImpl *field); 42 | 43 | /** 44 | * Get whether this field is NULL 45 | */ 46 | bool isNULL() const; 47 | 48 | /** 49 | * Cast to a number 50 | * 51 | * Note that if the value is NULL, this will yield 0. 52 | * To check for NULL values, use the isNULL function. 53 | * 54 | * @throws std::invalid_argument if this field does not contain a number 55 | * @throws std::out_of_range if the value is too small or too big to fit in the requested type 56 | */ 57 | operator int8_t() const; 58 | operator uint16_t() const; 59 | operator int16_t() const; 60 | operator uint32_t() const; 61 | operator int32_t() const; 62 | operator uint64_t() const; 63 | operator int64_t() const; 64 | operator float() const; 65 | operator double() const; 66 | 67 | /** 68 | * Cast to a uint128_t, this assumes a binary(16) field network byte ordering 69 | * 70 | * @throws std::out_of_range if the value is not the correct size (16 bytes) 71 | */ 72 | operator uint128_t() const; 73 | 74 | /** 75 | * Cast to a string 76 | * 77 | * Note that if the value is NULL, this will yield 78 | * an empty string. To check for NULL values, use 79 | * the isNULL function. 80 | */ 81 | operator std::string() const; 82 | 83 | /** 84 | * Cast to a time structure 85 | * 86 | * Note that if the value is NULL, or if it is not 87 | * a date type, this function will return an std::tm 88 | * structure set at its epoch (1900-01-01 00:00:00). 89 | */ 90 | operator std::tm() const; 91 | }; 92 | 93 | /** 94 | * Write the field to a stream 95 | * 96 | * @param stream output stream to write to 97 | * @param field the field to write 98 | */ 99 | std::ostream& operator<<(std::ostream& stream, const ResultField& field); 100 | 101 | /** 102 | * End namespace 103 | */ 104 | }} 105 | -------------------------------------------------------------------------------- /include/resultrow.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultRow.h 3 | * 4 | * Class with result data for a single MySQL row 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | // forward declaration 15 | class ResultImpl; 16 | 17 | /** 18 | * Result row class 19 | */ 20 | class ResultRow 21 | { 22 | private: 23 | /** 24 | * The result set to which this row belongs 25 | * @var std::shared_ptr 26 | */ 27 | std::shared_ptr _result; 28 | 29 | /** 30 | * Or a vector with field objects 31 | */ 32 | const std::vector>& _fields; 33 | 34 | /** 35 | * Create an iterator class 36 | */ 37 | class iterator 38 | { 39 | private: 40 | /** 41 | * Underlying map iterator 42 | */ 43 | std::map::const_iterator _iterator; 44 | 45 | /** 46 | * The result row 47 | */ 48 | const ResultRow *_row; 49 | public: 50 | /** 51 | * Empty constructor 52 | */ 53 | iterator() : _iterator(std::map::const_iterator()), _row(NULL) {} 54 | 55 | /** 56 | * Constructor 57 | * @param parent 58 | * @param index 59 | */ 60 | iterator(std::map::const_iterator&& iterator, const ResultRow *row) : _iterator(std::move(iterator)), _row(row) {} 61 | 62 | /** 63 | * Copy constructor 64 | * 65 | * @param that object to copy 66 | */ 67 | iterator(const iterator& that) : _iterator(that._iterator), _row(that._row) {} 68 | 69 | /** 70 | * Destructor 71 | */ 72 | virtual ~iterator() {} 73 | 74 | /** 75 | * Assign another iterator 76 | * 77 | * @param that other iterator to copy 78 | */ 79 | iterator& operator=(const iterator& that) 80 | { 81 | // copy iterator 82 | _iterator = that._iterator; 83 | _row = that._row; 84 | 85 | // allow chaining 86 | return *this; 87 | } 88 | 89 | /** 90 | * Increment operator 91 | */ 92 | iterator &operator++() 93 | { 94 | _iterator++; 95 | return *this; 96 | } 97 | 98 | /** 99 | * Increment operator (postfix) 100 | */ 101 | iterator operator++(int) 102 | { 103 | // make a copy of ourselves 104 | iterator copy(*this); 105 | 106 | // increment iterator 107 | _iterator++; 108 | 109 | // return the copy 110 | return copy; 111 | } 112 | 113 | /** 114 | * Decrement operator 115 | */ 116 | iterator &operator--() 117 | { 118 | _iterator--; 119 | return *this; 120 | } 121 | 122 | /** 123 | * Decrement operator (postfix) 124 | */ 125 | iterator operator--(int) 126 | { 127 | // make a copy of ourselves 128 | iterator copy(*this); 129 | 130 | // decrement iterator 131 | _iterator--; 132 | 133 | // return the copy 134 | return copy; 135 | } 136 | 137 | /** 138 | * Compare with other operator 139 | * @param iterator 140 | */ 141 | bool operator==(const iterator& iterator) const 142 | { 143 | return _iterator == iterator._iterator; 144 | } 145 | 146 | /** 147 | * Compare with other operator 148 | */ 149 | bool operator!=(const iterator& iterator) const 150 | { 151 | return _iterator != iterator._iterator; 152 | } 153 | 154 | /** 155 | * Dereference 156 | * @return ResultField 157 | */ 158 | std::pair operator*() const 159 | { 160 | return std::make_pair<>(_iterator->first,_row->operator[](_iterator->first)); 161 | } 162 | 163 | /** 164 | * Call method on the dereferenced object 165 | */ 166 | std::unique_ptr> operator->() const 167 | { 168 | return std::unique_ptr>(new std::pair(_iterator->first,_row->operator[](_iterator->first))); 169 | } 170 | }; 171 | 172 | public: 173 | /** 174 | * Construct the row 175 | * 176 | * @param result the result object with all the rows 177 | * @param fields the field object with all the data 178 | */ 179 | ResultRow(std::shared_ptr result, const std::vector>& fields) : 180 | _result(result), _fields(fields) {} 181 | 182 | /** 183 | * Destructor 184 | */ 185 | virtual ~ResultRow() {} 186 | 187 | /** 188 | * Get the number of fields in the row 189 | * @return size_t 190 | */ 191 | size_t size() const; 192 | 193 | /** 194 | * Retrieve a field by index 195 | * 196 | * This function throws an exception if no field 197 | * exists under the given index (i.e. index 198 | * is not smaller than size()). 199 | * 200 | * @param index field index 201 | */ 202 | const ResultField operator [] (size_t index) const; 203 | 204 | /** 205 | * Retrieve a field by name 206 | * 207 | * This function throws an exception if no field 208 | * exists under the given key. 209 | * 210 | * @param key field name 211 | */ 212 | const ResultField operator [] (const std::string &key) const; 213 | 214 | /** 215 | * Get iterator for first field 216 | * @return const_iterator 217 | */ 218 | iterator begin() const; 219 | 220 | /** 221 | * Get iterator for field by the given field name 222 | * @param name 223 | */ 224 | iterator find(const std::string& key) const; 225 | 226 | /** 227 | * Get the iterator past the end 228 | */ 229 | iterator end() const; 230 | }; 231 | 232 | /** 233 | * End namespace 234 | */ 235 | }} 236 | -------------------------------------------------------------------------------- /include/statement.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Statement.h 3 | * 4 | * Class for executing prepared statements 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | // forward declaration 15 | class StatementResultInfo; 16 | 17 | /** 18 | * Prepared statement class 19 | */ 20 | class Statement 21 | { 22 | private: 23 | /** 24 | * The MySQL connection 25 | */ 26 | Connection *_connection; 27 | 28 | /** 29 | * The prepared statement 30 | */ 31 | MYSQL_STMT *_statement; 32 | 33 | /** 34 | * The query the statement is based on 35 | */ 36 | std::string _query; 37 | 38 | /** 39 | * Callback to execute when the statement was prepared 40 | */ 41 | std::function _prepareCallback; 42 | 43 | /** 44 | * The number of parameters in this statement 45 | */ 46 | size_t _parameters; 47 | 48 | /** 49 | * Information about the query result fields 50 | */ 51 | std::unique_ptr _info; 52 | 53 | /** 54 | * Initialize the statement 55 | * 56 | * @note: This function is to be executed from 57 | * worker context only 58 | * 59 | * @param reference The loop reference to stay alive 60 | */ 61 | void initialize(const std::shared_ptr &reference); 62 | 63 | /** 64 | * Execute statement with given parameters 65 | * 66 | * @note: This function is to be executed from 67 | * worker context only 68 | * 69 | * @param parameters The parameters to retry with 70 | * @param count The number of parameters 71 | * @param reference The loop reference 72 | * @param deferred The previously created deferred handler 73 | */ 74 | void execute(Parameter *parameters, size_t count, const std::shared_ptr &reference, const std::shared_ptr &deferred); 75 | public: 76 | /** 77 | * Constructor 78 | * 79 | * @param connection the connection to run the statement on 80 | * @param statement the statement to execute 81 | */ 82 | Statement(Connection *connection, std::string statement); 83 | 84 | /** 85 | * Copy constructor 86 | */ 87 | Statement(const Statement& that) = delete; 88 | 89 | /** 90 | * Move constructor 91 | */ 92 | Statement(Statement&& that); 93 | 94 | /** 95 | * Destructor 96 | */ 97 | virtual ~Statement(); 98 | 99 | /** 100 | * Get a call when the statement is prepared or fails 101 | * 102 | * @param callback the callback to execute 103 | */ 104 | void onPrepared(const std::function& callback) 105 | { 106 | _prepareCallback = callback; 107 | } 108 | 109 | /** 110 | * Execute the statement 111 | * 112 | * Note: the number of arguments (excluding the callback) must match 113 | * the number of placeholders in the statement string. If not, 114 | * the statement will not be executed and the callback will be 115 | * invoked immediately with an error. 116 | * 117 | * The following parameter types are supported and map to the following MySQL types 118 | * 119 | * Variable type MySQL column type 120 | * ------------- ----------------- 121 | * signed char TINYINT 122 | * short int SMALLINT 123 | * int INT 124 | * long long int BIGINT 125 | * float FLOAT 126 | * double DOUBLE 127 | * std::string TEXT, CHAR or VARCHAR 128 | * std::vector BLOB, BINARY or VARBINARY 129 | * std::nullptr_t NULL 130 | * 131 | * @param mixed... variable number of arguments of different type 132 | * @param callback the callback to be informed when the statement is executed or failed 133 | */ 134 | template 135 | Deferred& execute(Arguments ...params) 136 | { 137 | // create the deferred handler 138 | auto deferred = std::make_shared(); 139 | 140 | // keep the loop alive while the callback runs 141 | auto reference = std::make_shared(_connection->_loop); 142 | 143 | // allocate the parameters 144 | auto count = sizeof...(params); 145 | auto *parameters = new Parameter[sizeof...(params)]{ params... }; 146 | 147 | // execute statement in worker thread 148 | _connection->_worker.execute([this, reference, parameters, count, deferred]() { execute(parameters, count, reference, deferred); }); 149 | 150 | // return the deferred handler 151 | return *deferred; 152 | } 153 | }; 154 | 155 | /** 156 | * End namespace 157 | */ 158 | }} 159 | -------------------------------------------------------------------------------- /mysql.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MysqlCpp.h 3 | * 4 | * Main header file for the MYSQL-CPP wrapper library 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | #pragma once 10 | 11 | /** 12 | * Dependencies 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** 25 | * Other include files 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CPP = c++ 2 | RM = rm -f 3 | CPPFLAGS = -Wall -c -I. -O2 -std=c++11 -g 4 | LD = c++ 5 | LD_FLAGS = -Wall -shared -O2 6 | SHARED_LIB = libreactcpp-mysql.so 7 | STATIC_LIB = $(SHARED_LIB:%.so=%.a) 8 | SOURCES = $(wildcard *.cpp */*.cpp) 9 | SHARED_OBJECTS = $(SOURCES:%.cpp=%.o) 10 | STATIC_OBJECTS = $(SOURCES:%.cpp=%.s.o) 11 | 12 | all: shared static 13 | 14 | shared: ${SHARED_OBJECTS} ${SHARED_LIB} 15 | 16 | static: ${STATIC_OBJECTS} ${STATIC_LIB} 17 | 18 | ${SHARED_LIB}: ${SHARED_OBJECTS} 19 | ${LD} ${LD_FLAGS} -o $@ ${SHARED_OBJECTS} -lmysqlclient -lreactcpp 20 | 21 | ${STATIC_LIB}: ${STATIC_OBJECTS} 22 | ar rcs ${STATIC_LIB} ${STATIC_OBJECTS} 23 | 24 | clean: 25 | ${RM} *.obj *~* ${SHARED_OBJECTS} ${STATIC_OBJECTS} ${SHARED_LIB} ${STATIC_LIB} 26 | 27 | ${SHARED_OBJECTS}: 28 | ${CPP} ${CPPFLAGS} -fpic -o $@ ${@:%.o=%.cpp} 29 | 30 | ${STATIC_OBJECTS}: 31 | ${CPP} ${CPPFLAGS} -o $@ ${@:%.s.o=%.cpp} 32 | 33 | -------------------------------------------------------------------------------- /src/connection.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Connection.cpp 3 | * 4 | * Class representing a connection to a MySQL or MariaDB daemon 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | #include "includes.h" 10 | #include "library.h" 11 | 12 | /** 13 | * Set up namespace 14 | */ 15 | namespace React { namespace MySQL { 16 | 17 | /** 18 | * Initializes the MySQL library 19 | * 20 | * The library will be automatically de-initialized 21 | * on program termination. 22 | */ 23 | static void init() 24 | { 25 | // keep a single static library instance available 26 | static Library library; 27 | } 28 | 29 | /** 30 | * Establish a connection to mysql 31 | * 32 | * @param loop the loop to bind to 33 | * @param hostname the hostname to connect to 34 | * @param username the username to login with 35 | * @param password the password to authenticate with 36 | * @param database the database to use 37 | * @param initialize do we need to initialize (and cleanup) the mysql library 38 | */ 39 | Connection::Connection(Loop *loop, const std::string& hostname, const std::string &username, const std::string& password, const std::string& database, uint64_t flags, bool initialize) : 40 | _loop(loop), 41 | _connection(nullptr), 42 | _master(loop), 43 | _worker() 44 | { 45 | // initialize the library if necessary 46 | if (initialize) init(); 47 | 48 | // keep the loop alive while the callback runs 49 | auto reference = std::make_shared(_loop); 50 | 51 | // establish the connection in the worker thread 52 | _worker.execute([this, reference, hostname, username, password, database, flags]() { 53 | // initialize connection object 54 | if ((_connection = mysql_init(nullptr)) == nullptr) 55 | { 56 | // could not initialize connection object 57 | _master.execute([this, reference]() { if (_connectCallback) _connectCallback(mysql_error(_connection)); }); 58 | return; 59 | } 60 | 61 | // enable automatic reconnect 62 | my_bool reconnect = 1; 63 | mysql_options(_connection, MYSQL_OPT_RECONNECT, &reconnect); 64 | 65 | // connect to mysql 66 | if (mysql_real_connect(_connection, hostname.c_str(), username.c_str(), password.c_str(), database.c_str(), 0, nullptr, flags) == nullptr) 67 | { 68 | // could not connect to mysql 69 | _master.execute([this, reference]() { if (_connectCallback) _connectCallback(mysql_error(_connection)); }); 70 | return; 71 | } 72 | 73 | // we are connected, signal success to the callback 74 | _master.execute([this, reference]() { if (_connectCallback) _connectCallback(nullptr); }); 75 | }); 76 | } 77 | 78 | /** 79 | * Destructor 80 | */ 81 | Connection::~Connection() 82 | { 83 | // clean up mysql data when the worker stops 84 | _worker.execute([this]() { 85 | // close a possible connection 86 | if (_connection) mysql_close(_connection); 87 | 88 | // clean up thread-local mysql data 89 | mysql_thread_end(); 90 | }); 91 | } 92 | 93 | /** 94 | * Get a call when the connection succeeds or fails 95 | * 96 | * @param callback the callback that will be informed of the connection status 97 | */ 98 | void Connection::onConnected(const std::function& callback) 99 | { 100 | // store callback for later 101 | _connectCallback = callback; 102 | } 103 | 104 | /** 105 | * Retrieve or create a cached prepared statement 106 | * 107 | * @param query the query to use for preparing the statement 108 | */ 109 | Statement *Connection::statement(const char *query) 110 | { 111 | // find a possibly existing statement 112 | auto iter = _statements.find(query); 113 | 114 | // do we already have this statement? 115 | if (iter != _statements.end()) return iter->second.get(); 116 | 117 | // create a new statement and store it 118 | auto *statement = new Statement(this, query); 119 | _statements[query].reset(statement); 120 | 121 | // return the newfangled statement 122 | return statement; 123 | } 124 | 125 | /** 126 | * Parse the string and replace all placeholders with 127 | * the provided values. 128 | * 129 | * The callback is executed with the result. 130 | * 131 | * @param query the query to parse 132 | * @param callback the callback to give the result 133 | * @param parameters placeholder values 134 | * @param count number of placeholder values 135 | */ 136 | void Connection::prepare(const std::string& query, LocalParameter *parameters, size_t count, const std::function& callback) 137 | { 138 | // keep the loop alive while the callback runs 139 | auto reference = std::make_shared(_loop); 140 | 141 | // execute prepare in worker thread 142 | _worker.execute([this, reference, callback, query, parameters, count] () { 143 | /** 144 | * Calculate the maximum storage size for the parameters. 145 | * 146 | * Initializes to the length of the given query, and adds 147 | * the size of each parameter to the total. 148 | */ 149 | size_t size = query.size(); 150 | 151 | // add all elements 152 | for (size_t i = 0; i < count; ++i) size += parameters[i].size(); 153 | 154 | // the parsed query result 155 | std::string result; 156 | result.reserve(size); 157 | 158 | // begin parsing at the beginning of the string 159 | size_t position = query.find_first_of("?!"); 160 | 161 | // add the first part of the query (before the first placeholder) 162 | result.append(query, 0, position); 163 | 164 | // process all parameters 165 | for (size_t i = 0; i < count; ++i) 166 | { 167 | // are there more parameters to be escaped? 168 | if (position == std::string::npos) break; 169 | 170 | // retrieve parameter 171 | auto ¶meter = parameters[i]; 172 | 173 | // should we just escape or also quote 174 | switch (query[position]) 175 | { 176 | case '?': 177 | // we need to escape and quote 178 | result.append(parameter.quote(_connection)); 179 | break; 180 | case '!': 181 | // we only need to escape 182 | result.append(parameter.escape(_connection)); 183 | break; 184 | } 185 | 186 | // find the next placeholder 187 | size_t next = query.find_first_of("?!", position + 1); 188 | 189 | // add the regular query part and store new position 190 | result.append(query, position + 1, next - position - 1); 191 | position = next; 192 | } 193 | 194 | // clean up the parameters 195 | delete [] parameters; 196 | 197 | // and inform the callback 198 | _master.execute([reference, callback, result]() { callback(result); }); 199 | }); 200 | } 201 | 202 | /** 203 | * Execute a query 204 | * 205 | * @param query the query to execute 206 | */ 207 | Deferred& Connection::query(const std::string& query) 208 | { 209 | // create a new deferred handler 210 | auto deferred = std::make_shared(); 211 | 212 | // keep the loop alive while the callback runs 213 | auto reference = std::make_shared(_loop); 214 | 215 | // execute query in the worker thread 216 | _worker.execute([this, reference, query, deferred]() { 217 | // run the query, should get zero on success 218 | if (mysql_query(_connection, query.c_str())) 219 | { 220 | // query failed, report to listener 221 | if (deferred->requireStatus()) _master.execute([this, reference, deferred]() { deferred->failure(mysql_error(_connection)); }); 222 | return; 223 | } 224 | 225 | // process all result sets 226 | while (true) 227 | { 228 | // retrieve result set 229 | auto *result = mysql_store_result(_connection); 230 | 231 | // are we at all interested in the result? 232 | if (!deferred->requireStatus()) 233 | { 234 | // clean up the result 235 | if (result) mysql_free_result(result); 236 | } 237 | else 238 | { 239 | // get the number of rows affected in the query 240 | size_t affectedRows = mysql_affected_rows(_connection); 241 | 242 | // did we get a valid response? 243 | if (result) 244 | { 245 | // create the result and pass it to the listener 246 | _master.execute([this, reference, deferred, result]() { deferred->success(Result(result)); }); 247 | } 248 | else if (mysql_field_count(_connection)) 249 | { 250 | // the query *should* have returned a result, this is an error 251 | _master.execute([this, reference, deferred]() { deferred->failure(mysql_error(_connection)); }); 252 | } 253 | else 254 | { 255 | // this is a query without a result set (i.e.: update, insert or delete) 256 | _master.execute([this, reference, deferred, affectedRows]() { deferred->success(Result(affectedRows, mysql_insert_id(_connection))); }); 257 | } 258 | } 259 | 260 | // check whether there are more results 261 | switch(mysql_next_result(_connection)) 262 | { 263 | case -1: 264 | // all result sets were processed 265 | return; 266 | case 0: 267 | // ready for next result 268 | continue; 269 | default: 270 | // this is an error 271 | _master.execute([this, reference, deferred]() { deferred->failure(mysql_error(_connection)); }); 272 | return; 273 | } 274 | } 275 | }); 276 | 277 | // return the deferred handler 278 | return *deferred; 279 | } 280 | 281 | /** 282 | * End namespace 283 | */ 284 | }} 285 | -------------------------------------------------------------------------------- /src/includes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Includes.h 3 | * 4 | * Startup include file to compile the mysqlcpp library 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Dependencies 11 | */ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /** 22 | * Include other files from this library 23 | */ 24 | #include "resultfieldimpl.h" 25 | #include "queryresultfield.h" 26 | #include "resultimpl.h" 27 | #include "queryresultimpl.h" 28 | #include "statementresultfield.h" 29 | #include "../include/deferred.h" 30 | #include "../include/exception.h" 31 | #include "../include/resultfield.h" 32 | #include "../include/resultrow.h" 33 | #include "../include/result.h" 34 | #include "../include/localparameter.h" 35 | #include "../include/connection.h" 36 | #include "../include/parameter.h" 37 | #include "../include/statement.h" 38 | #include "../include/cachedstatement.h" 39 | #include "statementintegralresultfield.h" 40 | #include "statementdynamicresultfield.h" 41 | #include "statementdatetimeresultfield.h" 42 | #include "statementresultimpl.h" 43 | #include "statementresultinfo.h" 44 | -------------------------------------------------------------------------------- /src/library.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Library.h 3 | * 4 | * Provides the Library class, that makes sure the 5 | * initialized mysql library stays available until 6 | * program termination. 7 | * 8 | * @copyright 2014 Copernica BV 9 | */ 10 | 11 | /** 12 | * Set up namespace 13 | */ 14 | namespace React { namespace MySQL { 15 | 16 | /** 17 | * Library class 18 | */ 19 | class Library 20 | { 21 | public: 22 | /** 23 | * Constructor 24 | */ 25 | Library() 26 | { 27 | // initialize the library 28 | mysql_library_init(0, nullptr, nullptr); 29 | } 30 | 31 | /** 32 | * Destructor 33 | */ 34 | ~Library() 35 | { 36 | // finish the library on exit 37 | mysql_library_end(); 38 | } 39 | }; 40 | 41 | /** 42 | * End namespace 43 | */ 44 | }} 45 | -------------------------------------------------------------------------------- /src/queryresultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * QueryResultField.h 3 | * 4 | * Class representing a result field 5 | * from a regular query 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | /** 16 | * Result field class 17 | */ 18 | class QueryResultField : public ResultFieldImpl 19 | { 20 | /** 21 | * Field data 22 | */ 23 | const char *_data; 24 | 25 | /** 26 | * Length of the data 27 | */ 28 | size_t _length; 29 | public: 30 | /** 31 | * Constructor 32 | */ 33 | QueryResultField(const char *data, size_t length) : _data(data), _length(length) {} 34 | 35 | /** 36 | * Is this a NULL field? 37 | */ 38 | bool isNULL() const override 39 | { 40 | return _data == nullptr; 41 | } 42 | 43 | /** 44 | * Cast to a number 45 | */ 46 | virtual operator int8_t() const override { return isNULL() ? 0 : std::stoi(_data); } 47 | virtual operator uint16_t() const override { return isNULL() ? 0 : std::stoi(_data); } 48 | virtual operator int16_t() const override { return isNULL() ? 0 : std::stoi(_data); } 49 | virtual operator uint32_t() const override { return isNULL() ? 0 : std::stoul(_data); } 50 | virtual operator int32_t() const override { return isNULL() ? 0 : std::stoi(_data); } 51 | virtual operator uint64_t() const override { return isNULL() ? 0 : std::stoull(_data); } 52 | virtual operator int64_t() const override { return isNULL() ? 0 : std::stoll(_data); } 53 | virtual operator float() const override { return isNULL() ? 0 : std::stof(_data); } 54 | virtual operator double() const override { return isNULL() ? 0 : std::stod(_data); } 55 | virtual operator uint128_t() const override 56 | { 57 | // Throw in case we are not the correct size 58 | if (_length != sizeof(uint128_t)) throw std::out_of_range("ResultField is the incorrect size, should be 16 bytes"); 59 | 60 | // Declare our uint128_t output and copy the raw bytes from tmp into it 61 | uint128_t output; 62 | memcpy(&output, _data, sizeof(output)); 63 | 64 | // Convert from network byte ordering to host byte ordering and return 65 | return ntohl128(output); 66 | } 67 | 68 | /** 69 | * Cast to a string 70 | */ 71 | virtual operator std::string() const 72 | { 73 | return isNULL() ? "" : std::string(_data, _length); 74 | } 75 | 76 | /** 77 | * Cast to a time structure 78 | */ 79 | virtual operator std::tm() const override { 80 | return std::tm{}; 81 | } 82 | }; 83 | 84 | /** 85 | * End namespace 86 | */ 87 | }} 88 | -------------------------------------------------------------------------------- /src/queryresultimpl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * QueryResultImpl.h 3 | * 4 | * A wrapper for the mysql result that includes 5 | * name -> field wrapping 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | /** 16 | * Result wrapper class 17 | */ 18 | class QueryResultImpl : public ResultImpl 19 | { 20 | private: 21 | /** 22 | * MySQL result 23 | */ 24 | MYSQL_RES *_result; 25 | 26 | /** 27 | * Already parsed rows 28 | */ 29 | std::vector>> _rows; 30 | 31 | /** 32 | * Field info 33 | */ 34 | std::map _fields; 35 | 36 | /** 37 | * The current position in the result set 38 | */ 39 | size_t _position; 40 | 41 | public: 42 | /** 43 | * Construct result implementation 44 | * 45 | * @param result mysql result 46 | */ 47 | QueryResultImpl(MYSQL_RES *result) : 48 | ResultImpl(), 49 | _result(result), 50 | _rows(mysql_num_rows(_result)), 51 | _position(0) 52 | { 53 | // retrieve number of fields 54 | auto size = mysql_num_fields(_result); 55 | 56 | // initialize all fields 57 | for (size_t i = 0; i < size; i++) 58 | { 59 | // retrieve field info 60 | auto field = mysql_fetch_field_direct(_result, i); 61 | 62 | // store field in map 63 | _fields[std::string(field->name, field->name_length)] = i; 64 | } 65 | } 66 | 67 | /** 68 | * Clean up the result 69 | */ 70 | virtual ~QueryResultImpl() 71 | { 72 | // clean up result 73 | mysql_free_result(_result); 74 | } 75 | 76 | /** 77 | * Get the fields and their index 78 | */ 79 | const std::map& fields() const override 80 | { 81 | return _fields; 82 | } 83 | 84 | /** 85 | * Get the number of rows in this result set 86 | */ 87 | size_t size() const override 88 | { 89 | return _rows.size(); 90 | } 91 | 92 | /** 93 | * Retrieve row at the given index 94 | */ 95 | const std::vector>& fetch(size_t index) override 96 | { 97 | // check whether the index is valid 98 | if (index > size()) throw Exception("Invalid result offset"); 99 | 100 | // did we already prepare this row? 101 | if (_rows[index].size() == 0) 102 | { 103 | // are we already at the required offset? 104 | if (_position != index) 105 | { 106 | // seek to the desired offset 107 | mysql_data_seek(_result, index); 108 | } 109 | 110 | // store current position 111 | // we add one to the position since 112 | // the fetch operation advances to 113 | // the next row (which happens below) 114 | _position = index + 1; 115 | 116 | // retrieve the row 117 | auto row = mysql_fetch_row(_result); 118 | auto lengths = mysql_fetch_lengths(_result); 119 | 120 | // reserve space for the rows 121 | _rows[index].reserve(_fields.size()); 122 | 123 | // and parse them 124 | for (size_t i = 0; i < _fields.size(); ++i) 125 | { 126 | // create field implementation 127 | _rows[index].emplace_back(new QueryResultField(row[i], lengths[i])); 128 | } 129 | } 130 | 131 | // return the cached row 132 | return _rows[index]; 133 | } 134 | }; 135 | 136 | /** 137 | * End namespace 138 | */ 139 | }} 140 | -------------------------------------------------------------------------------- /src/result.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Result.cpp 3 | * 4 | * Class representing a MySQL result set 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | #include "includes.h" 10 | 11 | /** 12 | * Set up namespace 13 | */ 14 | namespace React { namespace MySQL { 15 | 16 | /** 17 | * Constructor 18 | */ 19 | Result::Result(MYSQL_RES *result) : 20 | _result(std::make_shared(result)) 21 | {} 22 | 23 | /** 24 | * Constructor 25 | */ 26 | Result::Result(std::shared_ptr&& implementation) : 27 | _result(std::move(implementation)) 28 | {} 29 | 30 | /** 31 | * Constructor for affected rows 32 | * 33 | * @param affectedRows Number of rows affected 34 | * @param insertID ID for the inserted row, if applicable 35 | */ 36 | Result::Result(size_t affectedRows, uint64_t insertID) : 37 | _affectedRows(affectedRows), 38 | _insertID(insertID) 39 | {} 40 | 41 | /** 42 | * Invalid constructor 43 | */ 44 | Result::Result(std::nullptr_t result) 45 | {} 46 | 47 | /** 48 | * Move constructor 49 | */ 50 | Result::Result(Result&& that) : 51 | _result(std::move(that._result)), 52 | _affectedRows(that._affectedRows) 53 | {} 54 | 55 | /** 56 | * Destructor 57 | */ 58 | Result::~Result() 59 | {} 60 | 61 | /** 62 | * Iterator empty constructor 63 | */ 64 | Result::iterator::iterator() : 65 | _result(nullptr), 66 | _index(0) 67 | {} 68 | 69 | /** 70 | * Constructor 71 | * 72 | * @param result mysql result set 73 | * @param index index to start from 74 | */ 75 | Result::iterator::iterator(std::shared_ptr result, size_t index) : 76 | _result(result), 77 | _index(index) 78 | {} 79 | 80 | /** 81 | * Copy constructor 82 | * 83 | * @param that iterator to copy 84 | */ 85 | Result::iterator::iterator(const Result::iterator& that) : 86 | _result(that._result), 87 | _index(that._index) 88 | {} 89 | 90 | /** 91 | * Is this iterator pointing to a valid position? 92 | */ 93 | bool Result::iterator::valid() const 94 | { 95 | // check for a valid result and if the index is within bounds 96 | return _result && _index < _result->size(); 97 | } 98 | 99 | /** 100 | * Assign another iterator 101 | */ 102 | Result::iterator& Result::iterator::operator=(const iterator& that) 103 | { 104 | // copy result and index 105 | _result = that._result; 106 | _index = that._index; 107 | 108 | // allow chaining 109 | return *this; 110 | } 111 | 112 | /** 113 | * Increment operator 114 | */ 115 | Result::iterator& Result::iterator::operator++() 116 | { 117 | ++_index; 118 | return *this; 119 | } 120 | 121 | /** 122 | * Increment operator (postfix) 123 | */ 124 | Result::iterator Result::iterator::operator++(int) 125 | { 126 | // make a copy 127 | iterator copy(*this); 128 | 129 | // increment index 130 | ++_index; 131 | 132 | // return the copy 133 | return copy; 134 | } 135 | 136 | /** 137 | * Compare with other iterator 138 | * 139 | * @param that iterator to compare with 140 | */ 141 | bool Result::iterator::operator==(const iterator& that) 142 | { 143 | // check whether the result matches 144 | if (_result != that._result) return false; 145 | 146 | // if we are both invalid, we match 147 | if (!valid() && !that.valid()) return true; 148 | 149 | // index should be identical 150 | return _index == that._index; 151 | } 152 | 153 | /** 154 | * Compare with other iterator 155 | * 156 | * @param that iterator to compare with 157 | */ 158 | bool Result::iterator::operator!=(const iterator& that) 159 | { 160 | return !operator==(that); 161 | } 162 | 163 | /** 164 | * Dereference 165 | */ 166 | ResultRow Result::iterator::operator*() 167 | { 168 | // fetch the row 169 | return ResultRow(_result, _result->fetch(_index)); 170 | } 171 | 172 | /** 173 | * Call method on dereferenced row 174 | */ 175 | std::unique_ptr Result::iterator::operator->() 176 | { 177 | // fetch the row into a new pointer 178 | return std::unique_ptr(new ResultRow(_result, _result->fetch(_index))); 179 | } 180 | 181 | /** 182 | * Is this a valid result 183 | */ 184 | bool Result::valid() const 185 | { 186 | return _affectedRows || _result; 187 | } 188 | 189 | /** 190 | * The number of affected rows 191 | */ 192 | size_t Result::affectedRows() const 193 | { 194 | return _affectedRows; 195 | } 196 | 197 | /** 198 | * ID of the last inserted row, but only 199 | * if the insert was using an auto_increment 200 | */ 201 | uint64_t Result::insertID() const 202 | { 203 | return _insertID; 204 | } 205 | 206 | /** 207 | * Get the number of rows in this result 208 | */ 209 | size_t Result::size() const 210 | { 211 | return _result ? _result->size() : 0; 212 | } 213 | 214 | /** 215 | * Retrieve a row at the given offset 216 | * 217 | * This function will throw an exception if no 218 | * row exists at the given index (i.e.: index 219 | * is greater than size()) or if the object is 220 | * in an invalid state. 221 | * 222 | * @param index row index in the result 223 | * @throws Exception 224 | */ 225 | ResultRow Result::operator [] (size_t index) 226 | { 227 | // check whether we are valid 228 | if (!_result) throw Exception("Invalid result object"); 229 | 230 | // fetch the row 231 | return ResultRow(_result, _result->fetch(index)); 232 | } 233 | 234 | /** 235 | * Retrieve iterator for first row 236 | */ 237 | Result::iterator Result::begin() const 238 | { 239 | return iterator(_result, 0); 240 | } 241 | 242 | /** 243 | * Retrieve iterator past the end 244 | */ 245 | Result::iterator Result::end() const 246 | { 247 | return iterator(_result, _result->size()); 248 | } 249 | 250 | /** 251 | * End namespace 252 | */ 253 | }} 254 | -------------------------------------------------------------------------------- /src/resultfield.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultField.cpp 3 | * 4 | * A single field from a single row from 5 | * a MySQL result set 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | #include "includes.h" 11 | 12 | /** 13 | * Set up namespace 14 | */ 15 | namespace React { namespace MySQL { 16 | 17 | /** 18 | * Constructor 19 | * 20 | * @param result the result implementation 21 | * @param field the field implementation 22 | */ 23 | ResultField::ResultField(std::shared_ptr result, ResultFieldImpl *field) : 24 | _result(result), 25 | _field(field) 26 | {} 27 | 28 | /** 29 | * Get whether the field is NULL 30 | */ 31 | bool ResultField::isNULL() const 32 | { 33 | // let the field handle this 34 | return _field == nullptr || _field->isNULL(); 35 | } 36 | 37 | /** 38 | * Cast to a number 39 | * 40 | * Note that if the value is NULL, this will yield 0. 41 | * To check for NULL values, use the isNULL function. 42 | * 43 | * @throws std::invalid_argument if this field does not contain a number 44 | * @throws std::out_of_range if the value is too small or too big to fit in the requested type 45 | */ 46 | ResultField::operator int8_t() const { return *_field; } 47 | ResultField::operator uint16_t() const { return *_field; } 48 | ResultField::operator int16_t() const { return *_field; } 49 | ResultField::operator uint32_t() const { return *_field; } 50 | ResultField::operator int32_t() const { return *_field; } 51 | ResultField::operator uint64_t() const { return *_field; } 52 | ResultField::operator int64_t() const { return *_field; } 53 | ResultField::operator float() const { return *_field; } 54 | ResultField::operator double() const { return *_field; } 55 | 56 | /** 57 | * Cast to a uint128_t, this assumes a binary(16) field network byte ordering 58 | * 59 | * @throws std::out_of_range if the value is not the correct size (16 bytes) 60 | */ 61 | ResultField::operator uint128_t() const { return *_field; } 62 | 63 | /** 64 | * Cast to a string 65 | * 66 | * Note that if the value is NULL, this will yield 67 | * an empty string. To check for NULL values, use 68 | * the isNULL function. 69 | */ 70 | ResultField::operator std::string() const 71 | { 72 | // let the field handle this 73 | return *_field; 74 | } 75 | 76 | /** 77 | * Cast to a time structure 78 | * 79 | * Note that if the value is NULL, or if it is not 80 | * a date type, this function will return an std::tm 81 | * structure set at its epoch (1900-01-01 00:00:00). 82 | */ 83 | ResultField::operator std::tm() const 84 | { 85 | // let the field handle this 86 | return *_field; 87 | } 88 | 89 | /** 90 | * Write the field to a stream 91 | * 92 | * @param stream output stream to write to 93 | * @param field the field to write 94 | */ 95 | std::ostream& operator<<(std::ostream& stream, const ResultField& field) 96 | { 97 | // is this field NULL? 98 | if (field.isNULL()) return stream << "(NULL)"; 99 | 100 | // write the string value 101 | else return stream << (std::string) field; 102 | } 103 | 104 | /** 105 | * End namespace 106 | */ 107 | }} 108 | -------------------------------------------------------------------------------- /src/resultfieldimpl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultFieldImpl.h 3 | * 4 | * Class representing a result field 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Result field class 16 | */ 17 | class ResultFieldImpl 18 | { 19 | public: 20 | /** 21 | * Destructor 22 | */ 23 | virtual ~ResultFieldImpl() {} 24 | 25 | /** 26 | * Is this a NULL field? 27 | */ 28 | virtual bool isNULL() const = 0; 29 | 30 | /** 31 | * Cast to a number 32 | */ 33 | virtual operator int8_t() const = 0; 34 | virtual operator uint16_t() const = 0; 35 | virtual operator int16_t() const = 0; 36 | virtual operator uint32_t() const = 0; 37 | virtual operator int32_t() const = 0; 38 | virtual operator uint64_t() const = 0; 39 | virtual operator int64_t() const = 0; 40 | virtual operator float() const = 0; 41 | virtual operator double() const = 0; 42 | virtual operator uint128_t() const = 0; 43 | 44 | /** 45 | * Cast to a string 46 | */ 47 | virtual operator std::string() const = 0; 48 | 49 | /** 50 | * Cast to a time structure 51 | */ 52 | virtual operator std::tm() const = 0; 53 | }; 54 | 55 | /** 56 | * End namespace 57 | */ 58 | }} 59 | -------------------------------------------------------------------------------- /src/resultimpl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultImpl.h 3 | * 4 | * Base class for result implementation 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | // forward declaration 15 | class ResultRowImpl; 16 | 17 | /** 18 | * Result implementation 19 | */ 20 | class ResultImpl 21 | { 22 | public: 23 | /** 24 | * Get the fields and their index 25 | */ 26 | virtual const std::map& fields() const = 0; 27 | 28 | /** 29 | * Get the number of rows in this result set 30 | */ 31 | virtual size_t size() const = 0; 32 | 33 | /** 34 | * Retrieve row at the given index 35 | */ 36 | virtual const std::vector>& fetch(size_t index) = 0; 37 | }; 38 | 39 | /** 40 | * End namespace 41 | */ 42 | }} 43 | -------------------------------------------------------------------------------- /src/resultrow.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * ResultRow.cpp 3 | * 4 | * Class with result data for a single MySQL row. 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | #include "includes.h" 10 | 11 | /** 12 | * Set up namespace 13 | */ 14 | namespace React { namespace MySQL { 15 | 16 | /** 17 | * Get the number of fields in the row 18 | * @return size_t 19 | */ 20 | size_t ResultRow::size() const 21 | { 22 | return _result->fields().size(); 23 | } 24 | 25 | /** 26 | * Retrieve a field by index 27 | * 28 | * This function throws an exception if no field 29 | * exists under the given index (i.e. index 30 | * is not smaller than size()). 31 | * 32 | * @param index field index 33 | */ 34 | const ResultField ResultRow::operator [] (size_t index) const 35 | { 36 | // check for out of bounds 37 | if (index >= size()) throw Exception("Index out of bounds"); 38 | 39 | // construct a result field, we also pass a result object to ensure 40 | // that the result will not be destructed for as long as the ResultField 41 | // objects is kept in scope by the user code 42 | return ResultField(_result, _fields[index].get()); 43 | } 44 | 45 | /** 46 | * Retrieve a field by name 47 | * 48 | * This function throws an exception if no field 49 | * exists under the given key. 50 | * 51 | * @param key field name 52 | */ 53 | const ResultField ResultRow::operator [] (const std::string &key) const 54 | { 55 | // check if field exist 56 | auto iter = _result->fields().find(key); 57 | if (iter == _result->fields().end()) throw Exception("Field key does not exist"); 58 | 59 | // construct a result field, we also pass a result object to ensure 60 | // that the result will not be destructed for as long as the ResultField 61 | // objects is kept in scope by the user code 62 | return ResultField(_result, _fields[iter->second].get()); 63 | } 64 | 65 | /** 66 | * Get iterator for first field 67 | * @return const_iterator 68 | */ 69 | ResultRow::iterator ResultRow::begin() const 70 | { 71 | return iterator(_result->fields().cbegin(), this); 72 | } 73 | 74 | /** 75 | * Get iterator for field by the given field name 76 | * @param name 77 | */ 78 | ResultRow::iterator ResultRow::find(const std::string& key) const 79 | { 80 | return iterator(_result->fields().find(key), this); 81 | } 82 | 83 | /** 84 | * Get the iterator past the end 85 | */ 86 | ResultRow::iterator ResultRow::end() const 87 | { 88 | return iterator(_result->fields().cend(), this); 89 | } 90 | 91 | /** 92 | * End namespace 93 | */ 94 | }} 95 | -------------------------------------------------------------------------------- /src/statement.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Statement.cpp 3 | * 4 | * Class for executing prepared statements 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | #include "includes.h" 10 | #include "mysql/errmsg.h" 11 | 12 | /** 13 | * Set up namespace 14 | */ 15 | namespace React { namespace MySQL { 16 | 17 | /** 18 | * Constructor 19 | * 20 | * @param connection the connection to run the statement on 21 | * @param statement the statement to execute 22 | */ 23 | Statement::Statement(Connection *connection, std::string statement) : 24 | _connection(connection), 25 | _statement(nullptr), 26 | _query(std::move(statement)), 27 | _parameters(0) 28 | { 29 | // keep the loop alive while the callback runs 30 | auto reference = std::make_shared(_connection->_loop); 31 | 32 | // initialize statement in worker thread 33 | _connection->_worker.execute([this, reference]() { initialize(reference); }); 34 | } 35 | 36 | /** 37 | * Move constructor 38 | */ 39 | Statement::Statement(Statement&& that) : 40 | _connection(that._connection), 41 | _statement(that._statement), 42 | _parameters(that._parameters), 43 | _info(std::move(that._info)) 44 | { 45 | // reset other statement 46 | that._connection = nullptr; 47 | that._statement = nullptr; 48 | that._parameters = 0; 49 | } 50 | 51 | /** 52 | * Destructor 53 | */ 54 | Statement::~Statement() 55 | { 56 | // clean up the statement 57 | if (_statement != nullptr) mysql_stmt_close(_statement); 58 | } 59 | 60 | /** 61 | * Initialize the statement 62 | * 63 | * @note: This function is to be executed from 64 | * worker context only 65 | * 66 | * @param reference The loop reference to stay alive 67 | */ 68 | void Statement::initialize(const std::shared_ptr &reference) 69 | { 70 | // initialize statement 71 | if ((_statement = mysql_stmt_init(_connection->_connection)) == nullptr) 72 | { 73 | _connection->_master.execute([this, reference]() { if (_prepareCallback) _prepareCallback("Unable to initialize statement"); }); 74 | return; 75 | } 76 | 77 | // prepare statement 78 | if (mysql_stmt_prepare(_statement, _query.c_str(), _query.size())) 79 | { 80 | _connection->_master.execute([this, reference]() { 81 | // inform callback of problem 82 | if (_prepareCallback) _prepareCallback(mysql_stmt_error(_statement)); 83 | 84 | // clean up the statement 85 | mysql_stmt_close(_statement); 86 | _statement = nullptr; 87 | }); 88 | return; 89 | } 90 | 91 | // store number of parameters in statement 92 | _parameters = mysql_stmt_param_count(_statement); 93 | 94 | // retrieve the list of fields for the statement result set (if any) 95 | auto *result = mysql_stmt_result_metadata(_statement); 96 | 97 | // if the statement does return fields, store information about it 98 | if (result != nullptr) _info.reset(new StatementResultInfo(_statement, result)); 99 | 100 | // all is well, inform the callback 101 | _connection->_master.execute([this, reference] () { if (_prepareCallback) _prepareCallback(nullptr); }); 102 | } 103 | 104 | /** 105 | * Execute statement with given parameters 106 | * 107 | * @note: This function is to be executed from 108 | * worker context only 109 | * 110 | * @param parameters The parameters to retry with 111 | * @param count The number of parameters 112 | * @param reference The loop reference 113 | * @param deferred The previously created deferred handler 114 | */ 115 | void Statement::execute(Parameter *parameters, size_t count, const std::shared_ptr &reference, const std::shared_ptr &deferred) 116 | { 117 | // check for a valid statement 118 | if (_statement == nullptr) 119 | { 120 | _connection->_master.execute([reference, deferred]() { deferred->failure("Cannot execute invalid statement"); }); 121 | delete [] parameters; 122 | return; 123 | } 124 | 125 | // check for correct number of arguments and bind the parameters 126 | if (count != _parameters) 127 | { 128 | _connection->_master.execute([reference, deferred]() { deferred->failure("Incorrect number of arguments"); }); 129 | delete [] parameters; 130 | return; 131 | } 132 | 133 | // bind the parameters 134 | if (mysql_stmt_bind_param(_statement, parameters)) 135 | { 136 | _connection->_master.execute([this, reference, deferred]() { deferred->failure(mysql_stmt_error(_statement)); }); 137 | delete [] parameters; 138 | return; 139 | } 140 | 141 | // execute the statement 142 | if (mysql_stmt_execute(_statement)) 143 | { 144 | // check if the connection was reset, in which case we will 145 | // have to completely re-initialize the statement :( 146 | if (mysql_stmt_errno(_statement) == CR_SERVER_LOST) 147 | { 148 | // the statement is now invalid 149 | // the client code cleans it up 150 | _statement = nullptr; 151 | 152 | // reset parameter count 153 | _parameters = 0; 154 | 155 | // clean up the info 156 | _info.reset(); 157 | 158 | // initialize the statement again 159 | initialize(reference); 160 | 161 | // and retry execution again 162 | execute(parameters, count, reference, deferred); 163 | } 164 | else 165 | { 166 | // an error occured that we can't recover from 167 | _connection->_master.execute([this, reference, deferred]() { deferred->failure(mysql_stmt_error(_statement)); }); 168 | delete [] parameters; 169 | } 170 | 171 | // an error occured, don't proceed 172 | return; 173 | } 174 | 175 | // clean up input parameters 176 | delete [] parameters; 177 | 178 | // anyone interested in the result? 179 | if (!deferred->requireStatus()) 180 | { 181 | deferred->complete(); 182 | return; 183 | } 184 | 185 | // if the query has no result set, we create the result with the affected rows 186 | if (!_info) _connection->_master.execute([this, reference, deferred]() { 187 | // the statement already knows the number of affected rows 188 | // so doing this on the master will not block at all 189 | deferred->success(Result(mysql_stmt_affected_rows(_statement), mysql_stmt_insert_id(_statement))); 190 | }); 191 | else 192 | { 193 | // retrieve the result 194 | auto rows = _info->rows(); 195 | 196 | // send the result to the callback 197 | _connection->_master.execute([reference, deferred, rows]() { deferred->success(Result(std::move(rows))); }); 198 | } 199 | } 200 | 201 | /** 202 | * End namespace 203 | */ 204 | }} 205 | -------------------------------------------------------------------------------- /src/statementdatetimeresultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementDateTimeResultField.h 3 | * 4 | * Class representing a result field in 5 | * a result set from a prepared statement 6 | * holding a date and/or time. 7 | * 8 | * @copyright 2014 Copernica BV 9 | */ 10 | 11 | /** 12 | * Set up namespace 13 | */ 14 | namespace React { namespace MySQL { 15 | 16 | /** 17 | * Result field class 18 | */ 19 | class StatementDateTimeResultField : public StatementResultField 20 | { 21 | private: 22 | /** 23 | * The date information from MySQL 24 | */ 25 | MYSQL_TIME _value; 26 | 27 | /** 28 | * Get access to the data pointer for MySQL to fill 29 | */ 30 | virtual void *getValue() 31 | { 32 | return static_cast(&_value); 33 | } 34 | public: 35 | /** 36 | * Constructor 37 | */ 38 | StatementDateTimeResultField() : StatementResultField() {} 39 | 40 | /** 41 | * Cast to a number 42 | */ 43 | virtual operator int8_t() const override { return 0; } 44 | virtual operator uint16_t() const override { return 0; } 45 | virtual operator int16_t() const override { return 0; } 46 | virtual operator uint32_t() const override { return 0; } 47 | virtual operator int32_t() const override { return 0; } 48 | virtual operator uint64_t() const override { return 0; } 49 | virtual operator int64_t() const override { return 0; } 50 | virtual operator float() const override { return 0; } 51 | virtual operator double() const override { return 0; } 52 | virtual operator uint128_t() const override { return 0; } 53 | 54 | /** 55 | * Cast to a string 56 | */ 57 | virtual operator std::string() const override { return ""; /* TODO */ } 58 | 59 | /** 60 | * Cast to a time structure 61 | */ 62 | virtual operator std::tm() const override { 63 | return std::tm { (int)_value.second, (int)_value.minute, (int)_value.hour, (int)_value.day, (int)_value.month - 1, (int)_value.year - 1900, 0, 0, -1 }; 64 | } 65 | }; 66 | 67 | /** 68 | * End namespace 69 | */ 70 | }} 71 | -------------------------------------------------------------------------------- /src/statementdynamicresultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementSignedCharResultField.h 3 | * 4 | * Class representing a result field 5 | * of varying length (i.e. a CHAR or 6 | * a BLOB field). 7 | * 8 | * @copyright 2014 Copernica BV 9 | */ 10 | 11 | /** 12 | * Set up namespace 13 | */ 14 | namespace React { namespace MySQL { 15 | 16 | /** 17 | * Result field class for dynamic fields 18 | */ 19 | class StatementDynamicResultField : public StatementResultField 20 | { 21 | private: 22 | /** 23 | * The field data 24 | */ 25 | char *_value; 26 | 27 | /** 28 | * Field size 29 | */ 30 | unsigned long _size; 31 | 32 | /** 33 | * Retrieve the value pointer 34 | */ 35 | void *getValue() override 36 | { 37 | return static_cast(_value); 38 | } 39 | protected: 40 | /** 41 | * Is this a dynamically sized field? 42 | */ 43 | virtual bool dynamic() 44 | { 45 | // we are! 46 | return true; 47 | } 48 | public: 49 | /** 50 | * Constructor 51 | */ 52 | StatementDynamicResultField() : 53 | StatementResultField(), 54 | _value(nullptr), 55 | _size(0) 56 | {} 57 | 58 | /** 59 | * Destructor 60 | */ 61 | virtual ~StatementDynamicResultField() 62 | { 63 | std::free(_value); 64 | } 65 | 66 | /** 67 | * Cast to a number 68 | */ 69 | virtual operator int8_t() const override { return isNULL() ? 0 : std::stoi(_value); } 70 | virtual operator uint16_t() const override { return isNULL() ? 0 : std::stoi(_value); } 71 | virtual operator int16_t() const override { return isNULL() ? 0 : std::stoi(_value); } 72 | virtual operator uint32_t() const override { return isNULL() ? 0 : std::stoul(_value); } 73 | virtual operator int32_t() const override { return isNULL() ? 0 : std::stoi(_value); } 74 | virtual operator uint64_t() const override { return isNULL() ? 0 : std::stoull(_value); } 75 | virtual operator int64_t() const override { return isNULL() ? 0 : std::stoll(_value); } 76 | virtual operator float() const override { return isNULL() ? 0 : std::stof(_value); } 77 | virtual operator double() const override { return isNULL() ? 0 : std::stod(_value); } 78 | virtual operator uint128_t() const override 79 | { 80 | // Throw in case we are not the correct size 81 | if (_size != sizeof(uint128_t)) throw std::out_of_range("ResultField is the incorrect size, should be 16 bytes"); 82 | 83 | // Declare our uint128_t output and copy the raw bytes from tmp into it 84 | uint128_t output; 85 | memcpy(&output, _value, sizeof(output)); 86 | 87 | // Convert from network byte ordering to host byte ordering and return 88 | return ntohl128(output); 89 | } 90 | 91 | /** 92 | * Cast to a string 93 | */ 94 | virtual operator std::string() const override 95 | { 96 | return std::string(_value, _size); 97 | } 98 | 99 | /** 100 | * Cast to a time structure 101 | */ 102 | virtual operator std::tm() const override { 103 | return std::tm {}; 104 | } 105 | 106 | // friends and family 107 | friend class StatementResultInfo; 108 | }; 109 | 110 | /** 111 | * End namespace 112 | */ 113 | }} 114 | -------------------------------------------------------------------------------- /src/statementintegralresultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementIntegralResultField.h 3 | * 4 | * Class representing a result field 5 | * holding an integral value. 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | /** 16 | * Result field class for integral types 17 | */ 18 | template 19 | class StatementIntegralResultField : public StatementResultField 20 | { 21 | private: 22 | /** 23 | * The field data 24 | */ 25 | T _value; 26 | 27 | /** 28 | * Retrieve the value pointer 29 | */ 30 | void *getValue() override 31 | { 32 | return static_cast(&_value); 33 | } 34 | public: 35 | /** 36 | * Constructor 37 | */ 38 | StatementIntegralResultField() : StatementResultField() 39 | {} 40 | 41 | /** 42 | * Cast to a number 43 | */ 44 | virtual operator int8_t() const override { return isNULL() ? 0 : _value; } 45 | virtual operator uint16_t() const override { return isNULL() ? 0 : _value; } 46 | virtual operator int16_t() const override { return isNULL() ? 0 : _value; } 47 | virtual operator uint32_t() const override { return isNULL() ? 0 : _value; } 48 | virtual operator int32_t() const override { return isNULL() ? 0 : _value; } 49 | virtual operator uint64_t() const override { return isNULL() ? 0 : _value; } 50 | virtual operator int64_t() const override { return isNULL() ? 0 : _value; } 51 | virtual operator float() const override { return isNULL() ? 0 : _value; } 52 | virtual operator double() const override { return isNULL() ? 0 : _value; } 53 | virtual operator uint128_t() const override { return isNULL() ? 0 : (uint64_t) _value; } 54 | 55 | /** 56 | * Cast to a string 57 | */ 58 | virtual operator std::string() const override 59 | { 60 | return std::to_string(_value); 61 | } 62 | 63 | /** 64 | * Cast to a time structure 65 | */ 66 | virtual operator std::tm() const override 67 | { 68 | return std::tm{}; 69 | } 70 | }; 71 | 72 | // the different integral fields we support 73 | using StatementSignedCharResultField = StatementIntegralResultField; 74 | using StatementUnsignedShortResultField = StatementIntegralResultField; 75 | using StatementSignedShortResultField = StatementIntegralResultField; 76 | using StatementUnsignedLongResultField = StatementIntegralResultField; 77 | using StatementSignedLongResultField = StatementIntegralResultField; 78 | using StatementUnsignedLongLongResultField = StatementIntegralResultField; 79 | using StatementSignedLongLongResultField = StatementIntegralResultField; 80 | using StatementFloatResultField = StatementIntegralResultField; 81 | using StatementDoubleResultField = StatementIntegralResultField; 82 | 83 | /** 84 | * End namespace 85 | */ 86 | }} 87 | -------------------------------------------------------------------------------- /src/statementresultfield.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementResultField.h 3 | * 4 | * Class representing a result field in 5 | * a result set from a prepared statement 6 | * 7 | * @copyright 2014 Copernica BV 8 | */ 9 | 10 | /** 11 | * Set up namespace 12 | */ 13 | namespace React { namespace MySQL { 14 | 15 | /** 16 | * Result field class 17 | */ 18 | class StatementResultField : public ResultFieldImpl 19 | { 20 | private: 21 | /** 22 | * Are we NULL? 23 | */ 24 | my_bool _null; 25 | 26 | /** 27 | * Get access to the data pointer for MySQL to fill 28 | */ 29 | virtual void *getValue() = 0; 30 | 31 | /** 32 | * Get access to the NULL pointer for MySQL to fill 33 | */ 34 | my_bool *getNULL() 35 | { 36 | return &_null; 37 | } 38 | protected: 39 | /** 40 | * Is this a dynamically sized field? 41 | */ 42 | virtual bool dynamic() 43 | { 44 | // most fields are not 45 | return false; 46 | } 47 | public: 48 | /** 49 | * Constructor 50 | */ 51 | StatementResultField() : _null(false) {} 52 | 53 | /** 54 | * Destructor 55 | */ 56 | virtual ~StatementResultField() {}; 57 | 58 | /** 59 | * Is this a NULL field? 60 | */ 61 | bool isNULL() const 62 | { 63 | return _null; 64 | } 65 | 66 | /** 67 | * Cast to a number 68 | */ 69 | virtual operator int8_t() const override = 0; 70 | virtual operator uint16_t() const override = 0; 71 | virtual operator int16_t() const override = 0; 72 | virtual operator uint32_t() const override = 0; 73 | virtual operator int32_t() const override = 0; 74 | virtual operator uint64_t() const override = 0; 75 | virtual operator int64_t() const override = 0; 76 | virtual operator float() const override = 0; 77 | virtual operator double() const override = 0; 78 | virtual operator uint128_t() const override = 0; 79 | 80 | /** 81 | * Cast to a string 82 | */ 83 | virtual operator std::string() const override = 0; 84 | 85 | /** 86 | * Cast to a time structure 87 | */ 88 | virtual operator std::tm() const override = 0; 89 | 90 | // friends and families 91 | friend class StatementResultInfo; 92 | }; 93 | 94 | /** 95 | * End namespace 96 | */ 97 | }} 98 | -------------------------------------------------------------------------------- /src/statementresultimpl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementResultImpl.h 3 | * 4 | * Result set from a prepared statement 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Result set from a prepared statement 16 | */ 17 | class StatementResultImpl : public ResultImpl 18 | { 19 | /** 20 | * The map from field name to index 21 | */ 22 | std::map _fields; 23 | 24 | /** 25 | * The field data 26 | */ 27 | std::vector>> _rows; 28 | public: 29 | /** 30 | * Construct result 31 | */ 32 | StatementResultImpl(const std::map& fields, std::vector>>&& rows) : 33 | _fields(fields), 34 | _rows(std::move(rows)) 35 | {} 36 | 37 | /** 38 | * Get the fields and their index 39 | */ 40 | const std::map& fields() const override 41 | { 42 | return _fields; 43 | } 44 | 45 | /** 46 | * Get the number of rows in this result set 47 | */ 48 | size_t size() const override 49 | { 50 | return _rows.size(); 51 | } 52 | 53 | /** 54 | * Retrieve row at the given index 55 | */ 56 | const std::vector>& fetch(size_t index) override 57 | { 58 | return _rows[index]; 59 | } 60 | }; 61 | 62 | /** 63 | * End namespace 64 | */ 65 | }} 66 | -------------------------------------------------------------------------------- /src/statementresultinfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * StatementResultInfo.h 3 | * 4 | * Information about the result set of a prepared statement 5 | * 6 | * @copyright 2014 Copernica BV 7 | */ 8 | 9 | /** 10 | * Set up namespace 11 | */ 12 | namespace React { namespace MySQL { 13 | 14 | /** 15 | * Result info class 16 | */ 17 | class StatementResultInfo 18 | { 19 | private: 20 | /** 21 | * MySQL bind structures for retrieving data 22 | */ 23 | std::vector _bind; 24 | 25 | /** 26 | * The statement we are the result info for 27 | */ 28 | MYSQL_STMT *_statement; 29 | 30 | /** 31 | * Field names to field index 32 | */ 33 | std::map _fields; 34 | 35 | public: 36 | /** 37 | * Constructor 38 | * 39 | * @param result field result set 40 | */ 41 | StatementResultInfo(MYSQL_STMT *statement, MYSQL_RES *result) : 42 | _statement(statement) 43 | { 44 | // get the number of fields in the statement 45 | size_t fields = mysql_num_fields(result); 46 | 47 | // prepare the vector for data 48 | _bind.reserve(fields); 49 | 50 | // process all fields 51 | while (auto *field = mysql_fetch_field(result)) 52 | { 53 | // create and initialize bound parameter 54 | MYSQL_BIND bind; 55 | std::memset(&bind, 0, sizeof(bind)); 56 | 57 | // if we are a numeric field, we should store whether we are unsigned 58 | if (IS_NUM(field->type)) bind.is_unsigned = field->flags & UNSIGNED_FLAG; 59 | 60 | // determine the field type 61 | switch (field->type) 62 | { 63 | case MYSQL_TYPE_INT24: 64 | // this is a weird, 24-bit integer that we cannot 65 | // easily represent, so we force it into a "long" 66 | // (which is actually a regular integer) 67 | bind.buffer_type = MYSQL_TYPE_LONG; 68 | break; 69 | case MYSQL_TYPE_DECIMAL: 70 | case MYSQL_TYPE_NEWDECIMAL: 71 | // for reasons unknown, MySQL likes to return this 72 | // type as a char array, so we will just use our 73 | // regular string type for it 74 | bind.buffer_type = MYSQL_TYPE_STRING; 75 | break; 76 | case MYSQL_TYPE_ENUM: 77 | case MYSQL_TYPE_SET: 78 | // mysql returns these as the string representations 79 | bind.buffer_type = MYSQL_TYPE_STRING; 80 | break; 81 | case MYSQL_TYPE_GEOMETRY: 82 | // this is a bit clunky: a geometry type 83 | // has a fixed size, depending on what 84 | // specific subtype is used (e.g. a point 85 | // or a polygon). however, since there is 86 | // no easy way to retrieve the specific 87 | // type here, we used a variable-length 88 | // binary field instead 89 | bind.buffer_type = MYSQL_TYPE_BLOB; 90 | break; 91 | case MYSQL_TYPE_BIT: 92 | // mysql returns this as as bit-packed char 93 | // array. we could check the length and divide 94 | // it by eight to know the number of characters 95 | // but for now we simply use a string type 96 | bind.buffer_type = MYSQL_TYPE_BLOB; 97 | break; 98 | default: 99 | // all other types have native implementation 100 | bind.buffer_type = field->type; 101 | break; 102 | } 103 | 104 | // store name of the field 105 | _fields[std::string(field->name, field->name_length)] = _bind.size(); 106 | 107 | // add the bound field to the list 108 | _bind.push_back(bind); 109 | } 110 | 111 | // in the end, we have to clean up the result set 112 | mysql_free_result(result); 113 | } 114 | 115 | /** 116 | * Get the fields and their index 117 | */ 118 | const std::map& fields() const 119 | { 120 | return _fields; 121 | } 122 | 123 | /** 124 | * Number of rows in the result set 125 | */ 126 | size_t size() 127 | { 128 | return _bind.size(); 129 | } 130 | 131 | /** 132 | * Retrieve the rows in the result 133 | */ 134 | std::shared_ptr rows() 135 | { 136 | // store all the rows locally 137 | if (mysql_stmt_store_result(_statement)) throw Exception(mysql_stmt_error(_statement)); 138 | 139 | // retrieve the number of rows 140 | size_t count = mysql_stmt_num_rows(_statement); 141 | 142 | // the result value with all the rows 143 | std::vector>> result(count); 144 | 145 | // fetch all rows 146 | for (auto &row : result) 147 | { 148 | // prepare the row for data 149 | row.reserve(_bind.size()); 150 | 151 | // prepare all fields 152 | for (auto &bind : _bind) 153 | { 154 | // the field we are creating 155 | StatementResultField *field = nullptr; 156 | 157 | // create the field 158 | switch (bind.buffer_type) 159 | { 160 | case MYSQL_TYPE_TINY: 161 | field = new StatementSignedCharResultField(); 162 | break; 163 | case MYSQL_TYPE_SHORT: 164 | if (bind.is_unsigned) field = new StatementUnsignedShortResultField(); 165 | else field = new StatementSignedShortResultField(); 166 | break; 167 | case MYSQL_TYPE_INT24: 168 | case MYSQL_TYPE_LONG: 169 | if (bind.is_unsigned) field = new StatementUnsignedLongResultField(); 170 | else field = new StatementSignedLongResultField(); 171 | break; 172 | case MYSQL_TYPE_LONGLONG: 173 | if (bind.is_unsigned) field = new StatementUnsignedLongLongResultField(); 174 | else field = new StatementSignedLongLongResultField(); 175 | break; 176 | case MYSQL_TYPE_FLOAT: 177 | field = new StatementFloatResultField(); 178 | break; 179 | case MYSQL_TYPE_DOUBLE: 180 | field = new StatementDoubleResultField(); 181 | break; 182 | case MYSQL_TYPE_DECIMAL: 183 | case MYSQL_TYPE_NEWDECIMAL: 184 | // yes, really, we get a char array back 185 | case MYSQL_TYPE_ENUM: 186 | case MYSQL_TYPE_SET: 187 | case MYSQL_TYPE_GEOMETRY: 188 | case MYSQL_TYPE_BIT: 189 | case MYSQL_TYPE_VARCHAR: 190 | case MYSQL_TYPE_VAR_STRING: 191 | case MYSQL_TYPE_STRING: 192 | case MYSQL_TYPE_TINY_BLOB: 193 | case MYSQL_TYPE_MEDIUM_BLOB: 194 | case MYSQL_TYPE_LONG_BLOB: 195 | case MYSQL_TYPE_BLOB: 196 | field = new StatementDynamicResultField(); 197 | break; 198 | case MYSQL_TYPE_YEAR: 199 | case MYSQL_TYPE_TIME: 200 | case MYSQL_TYPE_DATE: 201 | case MYSQL_TYPE_NEWDATE: 202 | case MYSQL_TYPE_DATETIME: 203 | case MYSQL_TYPE_TIMESTAMP: 204 | field = new StatementDateTimeResultField(); 205 | break; 206 | case MYSQL_TYPE_NULL: 207 | // field is always null, no need to do anything 208 | break; 209 | 210 | default: 211 | // TODO: temporal fields 212 | break; 213 | } 214 | 215 | // if we have no field data, we must be an unknown field 216 | // or really a NULL field. we simply add this to the list 217 | if (field == nullptr) 218 | { 219 | row.emplace_back(nullptr); 220 | continue; 221 | } 222 | 223 | // if we have a fixed-size field, we can assign the data- and null-pointer 224 | if (!field->dynamic()) 225 | { 226 | // assign the data buffer and the null pointer to the bind structure 227 | bind.buffer = field->getValue(); 228 | bind.is_null = field->getNULL(); 229 | } 230 | else 231 | { 232 | // field is dynamic, cast to get access to properties 233 | StatementDynamicResultField *dynamic = static_cast(field); 234 | 235 | // set the buffer to be a null pointer and give MySQL a pointer to store the length 236 | bind.buffer = nullptr; 237 | bind.is_null = field->getNULL(); 238 | bind.length = &dynamic->_size; 239 | bind.buffer_length = 0; 240 | } 241 | 242 | // add the field 243 | row.emplace_back(field); 244 | } 245 | 246 | // bind the output parameters to the statement (this has to be done every time) 247 | if (mysql_stmt_bind_result(_statement, _bind.data())) throw Exception(mysql_stmt_error(_statement)); 248 | 249 | // fetch the data into the buffers 250 | switch (mysql_stmt_fetch(_statement)) 251 | { 252 | case 0: 253 | // fetch successful, all data loaded 254 | break; 255 | case 1: 256 | // something went horribly wrong 257 | throw Exception(mysql_stmt_error(_statement)); 258 | case MYSQL_NO_DATA: 259 | // this is also not allowed, since we only fetch 260 | // as many rows as were indicated to be present 261 | throw Exception("Result set corrupted"); 262 | case MYSQL_DATA_TRUNCATED: 263 | // some fields need more data, fetch it 264 | // we should know the required size by now 265 | for (size_t i = 0; i < _bind.size(); ++i) 266 | { 267 | // get bind property and field 268 | auto &bind = _bind[i]; 269 | auto *field = static_cast(row[i].get()); 270 | 271 | // skip fixed-size fields and NULL fields 272 | if (!field->dynamic() || field->isNULL()) continue; 273 | 274 | // cast to a dynamic field 275 | StatementDynamicResultField *dynamic = static_cast(field); 276 | 277 | // no need to allocate if it is an empty field 278 | if (!dynamic->_size) continue; 279 | 280 | // allocate memory and retrieve the field 281 | dynamic->_value = static_cast(std::malloc(dynamic->_size)); 282 | 283 | // assign the buffer and indicate the size to MySQL 284 | bind.buffer = dynamic->_value; 285 | bind.buffer_length = dynamic->_size; 286 | 287 | // fetch the field from MySQL 288 | mysql_stmt_fetch_column(_statement, &bind, i, 0); 289 | } 290 | 291 | // done 292 | break; 293 | } 294 | } 295 | 296 | // all done, wrap the data in a result container 297 | return std::make_shared(_fields, std::move(result)); 298 | } 299 | }; 300 | 301 | /** 302 | * End namespace 303 | */ 304 | }} 305 | --------------------------------------------------------------------------------