SystemReferenceStringTable.cpp (10377B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Microsoft/Schema/2_0/SystemReferenceStringTable.h" 5 #include "Microsoft/Schema/2_0/PackagesTable.h" 6 #include <winget/SQLiteStatementBuilder.h> 7 8 9 namespace AppInstaller::Repository::Microsoft::Schema::V2_0 10 { 11 namespace details 12 { 13 using PrimaryTable = PackagesTable; 14 15 using namespace std::string_view_literals; 16 static constexpr std::string_view s_SystemReferenceStringTable_PrimaryName = "package"sv; 17 18 std::string_view SystemReferenceStringTableGetPrimaryColumnName() 19 { 20 return s_SystemReferenceStringTable_PrimaryName; 21 } 22 23 void SystemReferenceStringTableCreate(SQLite::Connection& connection, std::string_view tableName, std::string_view valueName) 24 { 25 using namespace SQLite::Builder; 26 27 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_create_v2_0"); 28 29 StatementBuilder createTableBuilder; 30 createTableBuilder.CreateTable(tableName).Columns({ 31 ColumnBuilder(valueName, Type::Text).NotNull(), 32 ColumnBuilder(s_SystemReferenceStringTable_PrimaryName, Type::RowId).NotNull(), 33 PrimaryKeyBuilder({ valueName, s_SystemReferenceStringTable_PrimaryName }) 34 }).WithoutRowID(); 35 36 createTableBuilder.Execute(connection); 37 38 savepoint.Commit(); 39 } 40 41 void SystemReferenceStringTableDrop(SQLite::Connection& connection, std::string_view tableName) 42 { 43 SQLite::Builder::StatementBuilder dropTableBuilder; 44 dropTableBuilder.DropTable(tableName); 45 46 dropTableBuilder.Execute(connection); 47 } 48 49 std::vector<std::string> SystemReferenceStringTableGetValuesByPrimaryId( 50 const SQLite::Connection& connection, 51 std::string_view tableName, 52 std::string_view valueName, 53 SQLite::rowid_t primaryId) 54 { 55 std::vector<std::string> result; 56 57 SQLite::Builder::StatementBuilder builder; 58 builder.Select(valueName). 59 From(tableName).Where(s_SystemReferenceStringTable_PrimaryName).Equals(primaryId); 60 61 SQLite::Statement statement = builder.Prepare(connection); 62 63 while (statement.Step()) 64 { 65 result.emplace_back(statement.GetColumn<std::string>(0)); 66 } 67 68 return result; 69 } 70 71 void SystemReferenceStringTableEnsureExists( 72 SQLite::Connection& connection, 73 std::string_view tableName, 74 std::string_view valueName, 75 const std::vector<std::string>& values, 76 SQLite::rowid_t primaryId) 77 { 78 SQLite::Savepoint savepoint = SQLite::Savepoint::Create(connection, std::string{ tableName } + "_ensure_v2_0"); 79 80 SQLite::Builder::StatementBuilder builder; 81 82 builder.InsertOrIgnore(tableName). 83 Columns({ valueName, s_SystemReferenceStringTable_PrimaryName }).Values(SQLite::Builder::Unbound, primaryId); 84 85 SQLite::Statement insertStatement = builder.Prepare(connection); 86 87 for (const std::string& value : values) 88 { 89 // Second, insert into the mapping table 90 insertStatement.Reset(); 91 insertStatement.Bind(1, value); 92 93 insertStatement.Execute(); 94 } 95 96 savepoint.Commit(); 97 } 98 99 bool SystemReferenceStringTableCheckConsistency(const SQLite::Connection& connection, std::string_view tableName, std::string_view valueName, bool log) 100 { 101 using QCol = SQLite::Builder::QualifiedColumn; 102 103 bool result = true; 104 105 { 106 // Build a select statement to find rows containing references to primaries with nonexistent rowids 107 // Such as: 108 // Select data.data, data.primary from data left outer join primary on data.primary = primary.rowid where primary.id is null 109 110 SQLite::Builder::StatementBuilder builder; 111 builder. 112 Select({ QCol(tableName, valueName), QCol(tableName, s_SystemReferenceStringTable_PrimaryName) }). 113 From(tableName). 114 LeftOuterJoin(details::PrimaryTable::TableName()).On(QCol(tableName, s_SystemReferenceStringTable_PrimaryName), QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)). 115 Where(QCol(details::PrimaryTable::TableName(), SQLite::RowIDName)).IsNull(); 116 117 SQLite::Statement select = builder.Prepare(connection); 118 119 while (select.Step()) 120 { 121 result = false; 122 123 if (!log) 124 { 125 break; 126 } 127 128 AICLI_LOG(Repo, Info, << " [INVALID] " << tableName << " [" << select.GetColumn<std::string>(0) << 129 ", " << select.GetColumn<SQLite::rowid_t>(1) << "] refers to invalid " << details::PrimaryTable::TableName()); 130 } 131 } 132 133 if (!result && !log) 134 { 135 return result; 136 } 137 138 // Build a select statement to find values that contain an embedded null character 139 // Such as: 140 // Select count(*) from table where instr(value,char(0))>0 141 SQLite::Builder::StatementBuilder builder; 142 builder. 143 Select({ valueName, s_SystemReferenceStringTable_PrimaryName }). 144 From(tableName). 145 WhereValueContainsEmbeddedNullCharacter(valueName); 146 147 SQLite::Statement select = builder.Prepare(connection); 148 149 while (select.Step()) 150 { 151 result = false; 152 153 if (!log) 154 { 155 break; 156 } 157 158 AICLI_LOG(Repo, Info, << " [INVALID] value in table [" << tableName << "] for primary [" << select.GetColumn<SQLite::rowid_t>(1) << "] contains an embedded null character and starts with [" << select.GetColumn<std::string>(0) << "]"); 159 } 160 161 return result; 162 } 163 164 bool SystemReferenceStringTableIsEmpty(SQLite::Connection& connection, std::string_view tableName) 165 { 166 SQLite::Builder::StatementBuilder countBuilder; 167 countBuilder.Select(SQLite::Builder::RowCount).From(tableName); 168 169 SQLite::Statement countStatement = countBuilder.Prepare(connection); 170 171 THROW_HR_IF(E_UNEXPECTED, !countStatement.Step()); 172 173 return countStatement.GetColumn<int>(0) == 0; 174 } 175 176 int SystemReferenceStringTableBuildSearchStatement( 177 SQLite::Builder::StatementBuilder& builder, 178 std::string_view tableName, 179 std::string_view valueName, 180 std::string_view primaryAlias, 181 std::string_view valueAlias, 182 bool useLike) 183 { 184 using QCol = SQLite::Builder::QualifiedColumn; 185 186 // Build a statement like: 187 // SELECT table.package as p, table.value as v from table 188 // where table.value = <value> 189 builder.Select(). 190 Column(s_SystemReferenceStringTable_PrimaryName).As(primaryAlias). 191 Column(valueName).As(valueAlias). 192 From(tableName). 193 Where(valueName); 194 195 int result = -1; 196 197 if (useLike) 198 { 199 builder.Like(SQLite::Builder::Unbound); 200 result = builder.GetLastBindIndex(); 201 builder.Escape(SQLite::EscapeCharForLike); 202 } 203 else 204 { 205 builder.Equals(SQLite::Builder::Unbound); 206 result = builder.GetLastBindIndex(); 207 } 208 209 return result; 210 } 211 212 std::vector<int> SystemReferenceStringTableBuildPairedSearchStatement( 213 SQLite::Builder::StatementBuilder& builder, 214 std::string_view tableName, 215 std::string_view valueName, 216 std::string_view pairedTableName, 217 std::string_view pairedValueName, 218 std::string_view primaryAlias, 219 std::string_view valueAlias, 220 bool useLike) 221 { 222 using QCol = SQLite::Builder::QualifiedColumn; 223 224 // Build a statement like: 225 // SELECT table.package as p, '' as v from table 226 // join paired on table.package = paired.package 227 // where table.value = <value1> and paired.pairedValue = <value2> 228 builder.Select(). 229 Column(QCol(tableName, s_SystemReferenceStringTable_PrimaryName)).As(primaryAlias). 230 Value(std::string_view{}).As(valueAlias). 231 From(tableName). 232 Join(pairedTableName).On(QCol(tableName, s_SystemReferenceStringTable_PrimaryName), QCol(pairedTableName, s_SystemReferenceStringTable_PrimaryName)). 233 Where(QCol(tableName, valueName)); 234 235 std::vector<int> result; 236 237 if (useLike) 238 { 239 builder.Like(SQLite::Builder::Unbound); 240 result.push_back(builder.GetLastBindIndex()); 241 builder.Escape(SQLite::EscapeCharForLike); 242 } 243 else 244 { 245 builder.Equals(SQLite::Builder::Unbound); 246 result.push_back(builder.GetLastBindIndex()); 247 } 248 249 builder.And(QCol(pairedTableName, pairedValueName)); 250 251 if (useLike) 252 { 253 builder.Like(SQLite::Builder::Unbound); 254 result.push_back(builder.GetLastBindIndex()); 255 builder.Escape(SQLite::EscapeCharForLike); 256 } 257 else 258 { 259 builder.Equals(SQLite::Builder::Unbound); 260 result.push_back(builder.GetLastBindIndex()); 261 } 262 263 return result; 264 } 265 } 266 }