| LEFT | RIGHT |
| 1 // Copyright 2008, Google Inc. | 1 // Copyright 2008, Google Inc. |
| 2 // | 2 // |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // Redistribution and use in source and binary forms, with or without |
| 4 // modification, are permitted provided that the following conditions are met: | 4 // modification, are permitted provided that the following conditions are met: |
| 5 // | 5 // |
| 6 // 1. Redistributions of source code must retain the above copyright notice, | 6 // 1. Redistributions of source code must retain the above copyright notice, |
| 7 // this list of conditions and the following disclaimer. | 7 // this list of conditions and the following disclaimer. |
| 8 // 2. Redistributions in binary form must reproduce the above copyright notice, | 8 // 2. Redistributions in binary form must reproduce the above copyright notice, |
| 9 // this list of conditions and the following disclaimer in the documentation | 9 // this list of conditions and the following disclaimer in the documentation |
| 10 // and/or other materials provided with the distribution. | 10 // and/or other materials provided with the distribution. |
| 11 // 3. Neither the name of Google Inc. nor the names of its contributors may be | 11 // 3. Neither the name of Google Inc. nor the names of its contributors may be |
| 12 // used to endorse or promote products derived from this software without | 12 // used to endorse or promote products derived from this software without |
| 13 // specific prior written permission. | 13 // specific prior written permission. |
| 14 // | 14 // |
| 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 | 25 |
| 26 #include "gears/base/common/sqlite_wrapper.h" | |
| 27 #include "gears/base/common/string16.h" | |
| 28 #include "gears/database2/common.h" | |
| 29 #include "gears/database2/connection.h" | 26 #include "gears/database2/connection.h" |
| 30 #include "gears/database2/statement.h" | 27 #include "gears/database2/statement.h" |
| 31 | 28 |
| 32 // TODO(dimitri.glazkov): implement actual database operations. For now, all | 29 // TODO(dimitri.glazkov): implement actual database operations. For now, all |
| 33 // operations pretend to succeed to facilitate in-progress testing | 30 // operations pretend to succeed to facilitate in-progress testing |
| 31 |
| 34 bool Database2Connection::OpenAndVerifyVersion( | 32 bool Database2Connection::OpenAndVerifyVersion( |
| 35 const std::string16 &database_version) { | 33 const std::string16 &database_version) { |
| 36 // a quaint rendition of opening the database | |
| 37 // only here for testing needs | |
| 38 // TODO(dimitri.glazkov): Replace with the proper method implementation | |
| 39 int rc = sqlite3_open16(STRING16(L"stubbey.sqlite"), &handle_); | |
| 40 if (rc != SQLITE_OK) { | |
| 41 return false; | |
| 42 } | |
| 43 // open database if not already open | 34 // open database if not already open |
| 44 // read expected_version (user_version value) | 35 // read expected_version (user_version value) |
| 45 // read version from Permissions.db | 36 // read version from Permissions.db |
| 46 // if database_version is not an empty value or null, | 37 // if database_version is not an empty value or null, |
| 47 if (!database_version.empty()) { | 38 if (!database_version.empty()) { |
| 48 // if database_version matches version | 39 // if database_version matches version |
| 49 // return true | 40 // return true |
| 50 // otherwise, | 41 // otherwise, |
| 51 return false; | 42 return false; |
| 52 // return true | 43 // return true |
| 53 } | 44 } |
| 54 return true; | 45 return true; |
| 55 } | 46 } |
| 56 | 47 |
| 57 bool Database2Connection::Execute(const std::string16 &statement, | 48 bool Database2Connection::Execute(const std::string16 &statement, |
| 58 int num_arguments, | 49 Database2Values *arguments, |
| 59 Database2Variant *arguments, | |
| 60 Database2RowHandlerInterface *row_handler) { | 50 Database2RowHandlerInterface *row_handler) { |
| 61 int sqlite_status; | 51 // if (bogus_version_) { |
| 62 // This method should not be invoked until database is opened. | 52 // set error code to "version mismatch" (error code 2) |
| 63 assert(handle_); | 53 // } |
| 64 assert(row_handler); | 54 // prepare |
| 65 | 55 // step, for each row, call row_handler->HandleRow(..); |
| 66 if (bogus_version_) { | 56 // if error, set error code and message, return false; |
| 67 SetAndHandleError(kDatabaseVersionMismatch, kInvalidStateError, SQLITE_OK); | 57 // return true upon success |
| 68 return false; | |
| 69 } | |
| 70 | |
| 71 // prepare statement | |
| 72 scoped_sqlite3_stmt_ptr stmt; | |
| 73 sqlite_status = sqlite3_prepare16_v2(handle_, statement.c_str(), -1, &stmt, | |
| 74 NULL); | |
| 75 if (sqlite_status != SQLITE_OK) { | |
| 76 SetAndHandleError(kOtherDatabaseError, kPrepareError, sqlite_status); | |
| 77 return false; | |
| 78 } | |
| 79 | |
| 80 if (stmt.get() == NULL) { | |
| 81 error_message_ = GET_INTERNAL_ERROR_MESSAGE(); | |
| 82 return false; | |
| 83 } | |
| 84 | |
| 85 // bind arguments | |
| 86 for(int i = 0; i < num_arguments; ++i) { | |
| 87 int sql_index = i + 1; // sql parameters are 1-based | |
| 88 switch(arguments[i].type) { | |
| 89 case JSPARAM_INT: { | |
| 90 sqlite_status = sqlite3_bind_int(stmt.get(), sql_index, | |
| 91 arguments[i].int_value); | |
| 92 break; | |
| 93 } | |
| 94 case JSPARAM_DOUBLE: { | |
| 95 sqlite_status = sqlite3_bind_double(stmt.get(), sql_index, | |
| 96 arguments[i].double_value); | |
| 97 break; | |
| 98 } | |
| 99 case JSPARAM_STRING16: { | |
| 100 sqlite_status = sqlite3_bind_text16( | |
| 101 stmt.get(), sql_index, | |
| 102 arguments[i].string_value->c_str(), -1, | |
| 103 SQLITE_STATIC); | |
| 104 break; | |
| 105 } | |
| 106 case JSPARAM_NULL: { | |
| 107 sqlite_status = sqlite3_bind_null(stmt.get(), sql_index); | |
| 108 break; | |
| 109 } | |
| 110 default: { | |
| 111 // This should never occur, because Database2Variant only handles the | |
| 112 // four types above by design. | |
| 113 assert(false); | |
| 114 return false; | |
| 115 } | |
| 116 } | |
| 117 if (sqlite_status != SQLITE_OK) { | |
| 118 SetAndHandleError(kOtherDatabaseError, kBindError, sqlite_status); | |
| 119 return false; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 // get number of columns | |
| 124 int column_count = sqlite3_column_count(stmt.get()); | |
| 125 scoped_array<std::string16> column_names(new std::string16[column_count]); | |
| 126 | |
| 127 // read column names | |
| 128 for(int i = 0; i < column_count; ++i) { | |
| 129 const void *column_name = sqlite3_column_name16(stmt.get(), i); | |
| 130 column_names[i] = std::string16(static_cast<const char16 *>(column_name)); | |
| 131 } | |
| 132 | |
| 133 // row_handler takes ownership of the column names | |
| 134 row_handler->Init(column_count, column_names.release()); | |
| 135 | |
| 136 // read values | |
| 137 while(true) { | |
| 138 sqlite_status = sqlite3_step(stmt.get()); | |
| 139 if (sqlite_status == SQLITE_DONE) { | |
| 140 break; | |
| 141 } | |
| 142 if (sqlite_status != SQLITE_ROW) { | |
| 143 // an error has occured | |
| 144 SetAndHandleError(kOtherDatabaseError, kStepError, sqlite_status); | |
| 145 return false; | |
| 146 } | |
| 147 row_handler->HandleNewRow(); | |
| 148 for (int i = 0; i < column_count; ++i) { | |
| 149 switch(sqlite3_column_type(stmt.get(), i)) { | |
| 150 case SQLITE_INTEGER: { | |
| 151 row_handler->HandleColumnInt(i, sqlite3_column_int(stmt.get(), i)); | |
| 152 break; | |
| 153 } | |
| 154 case SQLITE_FLOAT: { | |
| 155 row_handler->HandleColumnDouble(i, | |
| 156 sqlite3_column_double(stmt.get(), i)); | |
| 157 break; | |
| 158 } | |
| 159 case SQLITE_TEXT: { | |
| 160 std::string16 value( | |
| 161 static_cast<const char16*>(sqlite3_column_text16(stmt.get(), i))); | |
| 162 row_handler->HandleColumnString(i, value); | |
| 163 break; | |
| 164 } | |
| 165 case SQLITE_NULL: { | |
| 166 row_handler->HandleColumnNull(i); | |
| 167 break; | |
| 168 } | |
| 169 default: { | |
| 170 // the only remaining SQLite type would be SQLITE_BLOB, which is not | |
| 171 // supported | |
| 172 SetAndHandleError(kOtherDatabaseError, kResultSetError, SQLITE_OK); | |
| 173 return false; | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 // read last inserted rowid | |
| 180 sqlite_int64 rowid = sqlite3_last_insert_rowid(handle_); | |
| 181 if ((rowid < JS_INT_MIN) || (rowid > JS_INT_MAX)) { | |
| 182 SetAndHandleError(kOtherDatabaseError, | |
| 183 kLastRowIdOutOfRangeError, SQLITE_OK); | |
| 184 return false; | |
| 185 } | |
| 186 | |
| 187 int rows_affected = sqlite3_changes(handle_); | |
| 188 | |
| 189 row_handler->HandleStats(static_cast<int64>(rowid), rows_affected); | |
| 190 return true; | 58 return true; |
| 191 } | |
| 192 | |
| 193 void Database2Connection::SetAndHandleError(int error_code, | |
| 194 const char16 *summary, | |
| 195 int sqlite_status) { | |
| 196 // TODO(dimitri.glazkov): add poisoning in case of corruption | |
| 197 error_code_ = error_code; | |
| 198 if (sqlite_status == SQLITE_OK) { | |
| 199 // not a SQLite error | |
| 200 error_message_ = summary; | |
| 201 return; | |
| 202 } | |
| 203 BuildSqliteErrorString(summary, sqlite_status, handle_, &error_message_); | |
| 204 } | 59 } |
| 205 | 60 |
| 206 bool Database2Connection::Begin() { | 61 bool Database2Connection::Begin() { |
| 207 // execute BEGIN | 62 // execute BEGIN |
| 208 // if error, set error code and message, return false | 63 // if error, set error code and message, return false |
| 209 // read actual_version, if doesn't match expected_version_, | 64 // read actual_version, if doesn't match expected_version_, |
| 210 // set bogus_version_ flag | 65 // set bogus_version_ flag |
| 211 // return true upon success | 66 // return true upon success |
| 212 return true; | 67 return true; |
| 213 } | 68 } |
| 214 | 69 |
| 215 void Database2Connection::Rollback() { | 70 void Database2Connection::Rollback() { |
| 216 // execute ROLLBACK | 71 // execute ROLLBACK |
| 217 // don't remember or handle errors | 72 // don't remember or handle errors |
| 218 } | 73 } |
| 219 | 74 |
| 220 bool Database2Connection::Commit() { | 75 bool Database2Connection::Commit() { |
| 221 // execute COMMIT | 76 // execute COMMIT |
| 222 // if error, set error code and message, return false | 77 // if error, set error code and message, return false |
| 223 // return true upon success | 78 // return true upon success |
| 224 return true; | 79 return true; |
| 225 } | 80 } |
| 226 | |
| 227 | |
| 228 void Database2BufferingRowHandler::Init(int column_count, | |
| 229 std::string16 *column_names) { | |
| 230 assert(column_count >= 0); | |
| 231 // at this time, we only support invoking this method on a new instance | |
| 232 assert(column_count_ == 0 && rows_.size() == 0); | |
| 233 column_count_ = column_count; | |
| 234 column_names_.reset(column_names); | |
| 235 } | |
| 236 | |
| 237 void Database2BufferingRowHandler::HandleNewRow() { | |
| 238 rows_.push_back(new Database2Variant[column_count_]); | |
| 239 } | |
| 240 | |
| 241 bool Database2BufferingRowHandler::HandleColumnInt(int index, int value) { | |
| 242 assert(index >=0 && index < column_count_); | |
| 243 assert(rows_.size() > 0); | |
| 244 Database2Variant *row = rows_.back(); | |
| 245 row[index].type = JSPARAM_INT; | |
| 246 row[index].int_value = value; | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 bool Database2BufferingRowHandler::HandleColumnDouble(int index, double value) { | |
| 251 assert(index >=0 && index < column_count_); | |
| 252 assert(rows_.size() > 0); | |
| 253 Database2Variant *row = rows_.back(); | |
| 254 row[index].type = JSPARAM_DOUBLE; | |
| 255 row[index].double_value = value; | |
| 256 return true; | |
| 257 } | |
| 258 | |
| 259 bool Database2BufferingRowHandler::HandleColumnString( | |
| 260 int index, | |
| 261 const std::string16 &value) { | |
| 262 assert(index >=0 && index < column_count_); | |
| 263 assert(rows_.size() > 0); | |
| 264 Database2Variant *row = rows_.back(); | |
| 265 row[index].type = JSPARAM_STRING16; | |
| 266 row[index].string_value = new std::string16(value); | |
| 267 return true; | |
| 268 } | |
| 269 | |
| 270 bool Database2BufferingRowHandler::HandleColumnNull(int index) { | |
| 271 assert(index >=0 && index < column_count_); | |
| 272 assert(rows_.size() > 0); | |
| 273 Database2Variant *row = rows_.back(); | |
| 274 row[index].type = JSPARAM_NULL; | |
| 275 return true; | |
| 276 } | |
| 277 | |
| 278 void Database2BufferingRowHandler::HandleStats(int64 last_insert_rowid, | |
| 279 int rows_affected) { | |
| 280 last_insert_rowid_ = last_insert_rowid; | |
| 281 rows_affected_ = rows_affected; | |
| 282 } | |
| 283 | |
| 284 bool Database2BufferingRowHandler::CopyTo( | |
| 285 Database2RowHandlerInterface *target) { | |
| 286 // TODO(dimitri.glazkov): implement copying contents of this instance into | |
| 287 // target | |
| 288 return false; | |
| 289 } | |
| LEFT | RIGHT |