From c5791e9f6988bb8f94c5c9baddeea00015e2ba0a Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 26 Sep 2026 10:14:05 +0700 Subject: [PATCH 1/4] fix(plugin-mongodb): create collections from New Table with their fields as a validator, read the fields back, and write values in their declared types --- CHANGELOG.md | 5 + .../MongoDBCollectionDDL.swift | 153 ++++++++++ .../MongoDBCollectionSchema.swift | 177 +++++++++++ .../MongoDBConnection.swift | 15 + .../MongoDBFilterValue.swift | 18 +- .../MongoDBPluginDriver.swift | 179 +++++++---- .../MongoDBQueryBuilder.swift | 10 +- .../MongoDBStatementGenerator.swift | 93 ++++-- .../MongoDBDriverPlugin/MongoScriptHost.swift | 10 +- .../MongoScriptResultBuilder.swift | 4 +- .../MongoScriptRuntime.swift | 14 +- .../MongoCollectionAccessor.swift | 15 +- .../Database/CreateTableEligibility.swift | 40 +++ .../Core/Plugins/ImportDataSinkAdapter.swift | 7 + ...inSplitViewController+MenuValidation.swift | 6 +- .../Schema/CreateTableDraftBuilder.swift | 7 +- TablePro/Resources/Localizable.xcstrings | 30 ++ TablePro/Views/Import/RowImportSheet.swift | 6 + ...ainContentCoordinator+SidebarActions.swift | 5 +- ...ontentCommandActions+DatabaseObjects.swift | 7 + .../DatabaseTreeOutlineCoordinator+Menu.swift | 1 + .../Menu/DatabaseTreeMenuContext.swift | 2 + .../Sidebar/Menu/DatabaseTreeMenuSpec.swift | 4 +- .../Views/Structure/CreateTableDraft.swift | 3 +- .../Views/Structure/CreateTableView.swift | 2 +- .../CreateTableEligibilityTests.swift | 79 +++++ .../Core/Menu/MainMenuBuilderTests.swift | 13 + .../MongoDB/MongoScriptPreludeTests.swift | 61 ++++ .../Schema/CreateTableDraftBuilderTests.swift | 31 ++ .../Plugins/MongoDBCollectionDDLTests.swift | 279 ++++++++++++++++++ .../MongoDBCollectionSchemaTests.swift | 118 ++++++++ .../Plugins/MongoDBQueryBuilderTests.swift | 21 ++ .../MongoDBStatementGeneratorTests.swift | 6 +- .../Plugins/MongoDBWriteBackTypeTests.swift | 176 ++++++++++- .../Sidebar/DatabaseTreeMenuSpecTests.swift | 12 + docs/databases/mongodb.mdx | 27 +- docs/features/table-operations.mdx | 2 +- docs/features/table-structure.mdx | 6 +- .../images/mongodb-create-collection-dark.png | Bin 0 -> 167230 bytes docs/images/mongodb-create-collection.png | Bin 0 -> 149730 bytes project.yml | 2 + 41 files changed, 1527 insertions(+), 119 deletions(-) create mode 100644 Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift create mode 100644 Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift create mode 100644 TablePro/Core/Database/CreateTableEligibility.swift create mode 100644 TableProTests/Core/Database/CreateTableEligibilityTests.swift create mode 100644 TableProTests/Plugins/MongoDBCollectionDDLTests.swift create mode 100644 TableProTests/Plugins/MongoDBCollectionSchemaTests.swift create mode 100644 docs/images/mongodb-create-collection-dark.png create mode 100644 docs/images/mongodb-create-collection.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c57dc014e..22c7090ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -522,6 +522,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ER diagram opening at 100% instead of fitted, then losing its zoom the first time you came back to its tab. - Diagram Fit to Window stopping a scroll bar's width short when scroll bars are always shown. - No Executing indicator or Stop button for a query tab with no result grid, in Output mode or on a query plan. +- MongoDB collections could not be created from **New Table…**. (#3131) +- A new or empty MongoDB collection showing only `_id` instead of the fields its validator declares. +- MongoDB edits that stored dates and ObjectIds as text, rounded integers past 2^53, or missed a string `_id` that looks numeric. +- **New Table…** offered on databases that cannot create a table, such as Redis and Kafka. ### Security @@ -549,6 +553,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SQL Server statements written after another without a semicolon skipped Safe Mode, on Mac, iPhone and iPad. - SQL Server connections set to Required (skip verify) not encrypted past the login. - Inline suggestions sending the query and table columns to the AI provider on Ask Each Time and Never connections. +- Stored MongoDB values and collection names that ran as shell code when a row was edited, duplicated or restored. ## [0.75.0] - 2026-09-18 diff --git a/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift b/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift new file mode 100644 index 0000000000..dcbf72325b --- /dev/null +++ b/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift @@ -0,0 +1,153 @@ +import Foundation +import TableProPluginKit + +enum MongoDBCollectionDDL { + static let idField = "_id" + static let unsupportedIndexTypes: Set = ["GIN", "GIST", "BRIN", "SPGIST"] + + /// A collection's fields exist on the server only as a `$jsonSchema` validator. Leaving + /// `validationLevel` and `validationAction` out keeps the server's own defaults, strict and + /// error, which is also what Compass and Studio 3T start from. + static func createCollectionStatement(for definition: PluginCreateTableDefinition) -> String { + let name = MongoScriptJson.jsonString(definition.tableName) + let fields = definition.columns.filter { !$0.name.isEmpty } + guard fields.contains(where: { $0.name != idField }) else { + return "db.createCollection(\(name))" + } + + let properties = fields.map { field in + " \(MongoScriptJson.jsonString(field.name)): {\"bsonType\": \(bsonTypeJson(for: field))}" + } + var schema = [" \"bsonType\": \"object\""] + let required = fields.filter { !$0.isNullable && $0.name != idField }.map(\.name) + if !required.isEmpty { + schema.append(" \"required\": [\(required.map(MongoScriptJson.jsonString).joined(separator: ", "))]") + } + schema.append(" \"properties\": {\n\(properties.joined(separator: ",\n"))\n }") + + return """ + db.createCollection(\(name), { + "validator": { + "$jsonSchema": { + \(schema.joined(separator: ",\n")) + } + } + }) + """ + } + + static func createIndexStatement(collection: String, index: PluginIndexDefinition) -> String? { + guard !index.columns.isEmpty, let keyValue = indexKeyValue(for: index.indexType) else { return nil } + let keys = index.columns + .map { "\(MongoScriptJson.jsonString($0)): \(keyValue)" } + .joined(separator: ", ") + var options: [String] = [] + if !index.name.isEmpty { + options.append("\"name\": \(MongoScriptJson.jsonString(index.name))") + } + if index.isUnique { + options.append("\"unique\": true") + } + let accessor = MongoCollectionAccessor.expression(for: collection) + guard !options.isEmpty else { return "\(accessor).createIndex({\(keys)})" } + return "\(accessor).createIndex({\(keys)}, {\(options.joined(separator: ", "))})" + } + + static func refusal(for operation: PluginSchemaOperation) -> String? { + switch operation { + case .addColumn(let column): + return columnRefusal(column) + case .addIndex(let index), .modifyIndex(_, let index): + return indexRefusal(index) + case .renameCheckConstraint, .dropIndex: + return nil + @unknown default: + return nil + } + } + + // MARK: - Columns + + private static func bsonTypeJson(for field: PluginColumnDefinition) -> String { + if field.name == idField { + return "\"objectId\"" + } + let declared = MongoDBBsonType.alias(forEditorType: field.dataType) + ?? field.dataType.trimmingCharacters(in: .whitespaces) + guard field.isNullable, declared != "null" else { + return MongoScriptJson.jsonString(declared) + } + return "[\(MongoScriptJson.jsonString(declared)), \"null\"]" + } + + private static func columnRefusal(_ column: PluginColumnDefinition) -> String? { + if column.name.hasPrefix("$") || column.name.contains(".") { + return String( + format: String(localized: "MongoDB cannot address a field named %@. A field name cannot start with $ or contain a dot."), + column.name + ) + } + guard let alias = MongoDBBsonType.alias(forEditorType: column.dataType) else { + return String( + format: String(localized: "%1$@ has type %2$@, which MongoDB does not have. Choose a type from the list."), + column.name, column.dataType + ) + } + if column.name == idField { + guard alias == "objectId" else { + return String(localized: "_id must be an objectId. MongoDB generates it for every new document.") + } + return nil + } + if column.isPrimaryKey { + return String( + format: String(localized: "%@ cannot be the primary key. MongoDB keys every document by _id."), + column.name + ) + } + return nil + } + + // MARK: - Indexes + + private static func indexKeyValue(for indexType: String?) -> String? { + switch indexType?.uppercased() ?? "" { + case "", "BTREE": return "1" + case "HASH": return "\"hashed\"" + case "FULLTEXT": return "\"text\"" + case "SPATIAL": return "\"2dsphere\"" + default: return nil + } + } + + private static func indexRefusal(_ index: PluginIndexDefinition) -> String? { + let label = index.name.isEmpty ? index.columns.joined(separator: ", ") : index.name + let usesSQLOnlyFeature = !(index.whereClause ?? "").isEmpty + || !(index.expressions ?? []).isEmpty + || !(index.includedColumns ?? []).isEmpty + || !(index.columnPrefixes ?? [:]).isEmpty + if usesSQLOnlyFeature { + return String( + format: String(localized: "Index %@ has a WHERE clause, an expression, included columns or a prefix length. MongoDB indexes take none of these."), + label + ) + } + guard let keyValue = indexKeyValue(for: index.indexType) else { + return String( + format: String(localized: "MongoDB has no %@ index."), + index.indexType ?? "" + ) + } + if index.columns == [idField] { + return String(localized: "MongoDB already indexes _id.") + } + guard keyValue == "\"hashed\"" else { return nil } + if index.isUnique { + return String(localized: "A hashed index cannot be unique.") + } + if index.columns.count > 1 { + return String(localized: "A hashed index takes one field.") + } + return nil + } +} diff --git a/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift new file mode 100644 index 0000000000..0e16e8ec96 --- /dev/null +++ b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift @@ -0,0 +1,177 @@ +import Foundation +import TableProPluginKit + +enum MongoDBBsonType { + static let aliases: [String] = [ + "double", "string", "object", "array", "binData", "objectId", "bool", "date", "null", + "regex", "javascript", "int", "timestamp", "long", "decimal", "minKey", "maxKey", "number" + ] + + /// `$jsonSchema` matches `bsonType` aliases case-sensitively, measured on 7.0: `ObjectId` and + /// `String` are refused as unknown aliases, so a spelling is only ever matched here, never + /// passed through. + static func alias(forEditorType editorType: String) -> String? { + let trimmed = editorType.trimmingCharacters(in: .whitespaces) + return aliases.first { $0.caseInsensitiveCompare(trimmed) == .orderedSame } + } + + static func alias(forJsonSchemaType type: String) -> String? { + switch type { + case "string", "object", "array", "null", "number": return type + case "boolean": return "bool" + default: return nil + } + } + + /// A kind for the writer and the filter to type values against, or nil when the alias leaves + /// the value's type open. Binary data has no subtype in a schema, and a column's UUID decoding + /// is decided once from its documents, so a declaration never picks it. + static func valueKind(forAlias alias: String) -> BsonValueKind? { + switch alias { + case "objectId": return .objectId + case "string": return .string + case "int": return .int32 + case "long": return .int64 + case "double": return .double + case "decimal": return .decimal128 + case "date": return .date + case "bool": return .boolean + case "array": return .array + case "object": return .document + default: return nil + } + } +} + +struct MongoDBDeclaredField: Equatable, Sendable { + let name: String + let bsonTypes: [String] + let isRequired: Bool + let allowedValues: [String]? + + var valueKind: BsonValueKind? { + let valueTypes = bsonTypes.filter { $0 != "null" } + guard valueTypes.count == 1, let only = valueTypes.first else { return nil } + return MongoDBBsonType.valueKind(forAlias: only) + } + + func columnTypeName(representation: MongoDBUuidRepresentation) -> String { + if let valueKind { + return BsonDocumentFlattener.typeName(for: valueKind, representation: representation) + } + let valueTypes = bsonTypes.filter { $0 != "null" } + if valueTypes == ["binData"] { + return BsonDocumentFlattener.typeName(for: .binary(subtype: 0), representation: representation) + } + return valueTypes.first ?? "null" + } +} + +/// The columns a collection presents: the fields its documents hold, then the fields its +/// validator declares that no document in hand holds yet. A new collection has no documents, and +/// sampling alone showed it as `_id` and nothing else. +enum MongoDBCollectionShape { + static func emptyCollectionColumns(declaring schema: MongoDBCollectionSchema) -> [String] { + [MongoDBCollectionDDL.idField] + schema.fields.map(\.name).filter { $0 != MongoDBCollectionDDL.idField } + } + + static func declaredColumnsMissing(from sampled: [String], schema: MongoDBCollectionSchema) -> [String] { + let present = Set(sampled) + return schema.fields.map(\.name).filter { !present.contains($0) } + } +} + +struct MongoDBCollectionSchema: Equatable, Sendable { + let fields: [MongoDBDeclaredField] + + static let empty = MongoDBCollectionSchema(fields: []) + + var isEmpty: Bool { fields.isEmpty } + + func field(named name: String) -> MongoDBDeclaredField? { + fields.first { $0.name == name } + } + + var valueKinds: [String: BsonValueKind] { + var kinds: [String: BsonValueKind] = [:] + for field in fields { + guard let kind = field.valueKind else { continue } + kinds[field.name] = kind + } + return kinds + } + + var allowedValues: [String: [String]] { + var values: [String: [String]] = [:] + for field in fields { + guard let allowed = field.allowedValues else { continue } + values[field.name] = allowed + } + return values + } + + static func listCollectionsCommand(for collection: String) -> String { + "{\"listCollections\": 1, \"filter\": {\"name\": \(MongoScriptJson.jsonString(collection))}}" + } + + /// Reads a `listCollections` reply's first collection. The reply is walked as text because the + /// order of `properties` is the order the fields were declared in, and decoding into a + /// dictionary loses it. + static func parse(listCollectionsReply reply: String) -> MongoDBCollectionSchema { + guard let cursor = MongoScriptJson.member(of: reply, key: "cursor"), + let batch = MongoScriptJson.member(of: cursor, key: "firstBatch"), + let collection = MongoScriptJson.topLevelElements(batch).first, + let options = MongoScriptJson.member(of: collection, key: "options"), + let validator = MongoScriptJson.member(of: options, key: "validator"), + let jsonSchema = MongoScriptJson.member(of: validator, key: "$jsonSchema") else { + return .empty + } + return parse(jsonSchema: jsonSchema) + } + + static func parse(jsonSchema: String) -> MongoDBCollectionSchema { + guard let schema = decodeObject(jsonSchema), + let properties = schema["properties"] as? [String: Any], + let propertiesText = MongoScriptJson.member(of: jsonSchema, key: "properties") else { + return .empty + } + let required = Set(schema["required"] as? [String] ?? []) + let orderedNames = orderedKeys(of: propertiesText, in: properties) + + let fields = orderedNames.compactMap { name -> MongoDBDeclaredField? in + guard let spec = properties[name] as? [String: Any] else { return nil } + return MongoDBDeclaredField( + name: name, + bsonTypes: declaredTypes(in: spec), + isRequired: required.contains(name), + allowedValues: stringEnum(spec["enum"]) + ) + } + return MongoDBCollectionSchema(fields: fields) + } + + private static func orderedKeys(of objectText: String, in decoded: [String: Any]) -> [String] { + let textual = MongoScriptJson.members(of: objectText).map(\.key).filter { decoded[$0] != nil } + let seen = Set(textual) + return textual + decoded.keys.filter { !seen.contains($0) }.sorted() + } + + private static func declaredTypes(in spec: [String: Any]) -> [String] { + if let single = spec["bsonType"] as? String { return [single] } + if let many = spec["bsonType"] as? [String] { return many } + if let single = spec["type"] as? String { return [MongoDBBsonType.alias(forJsonSchemaType: single)].compactMap { $0 } } + if let many = spec["type"] as? [String] { return many.compactMap(MongoDBBsonType.alias(forJsonSchemaType:)) } + return [] + } + + private static func stringEnum(_ value: Any?) -> [String]? { + guard let array = value as? [Any], !array.isEmpty, + array.allSatisfy({ $0 is String }) else { return nil } + return array.compactMap { $0 as? String } + } + + private static func decodeObject(_ text: String) -> [String: Any]? { + guard let data = text.data(using: .utf8) else { return nil } + return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] + } +} diff --git a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift index 57ff88a37d..7ae49284c9 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift @@ -552,6 +552,21 @@ final class MongoDBConnection: @unchecked Sendable { #endif } + /// The reply as canonical Extended JSON text, members in the order the server sent them. + func runCommandJson(_ command: String, database: String? = nil) async throws -> String { + #if canImport(CLibMongoc) + resetCancellation() + return try await pluginDispatchAsync(on: queue) { [self] in + guard !isShuttingDown, let client = self.client else { + throw MongoDBError.notConnected + } + return QueueTransfer(value: try scriptRunCommand(client: client, command: command, database: database)) + }.value + #else + throw MongoDBError.libmongocUnavailable + #endif + } + // MARK: - Collection Operations func find( diff --git a/Plugins/MongoDBDriverPlugin/MongoDBFilterValue.swift b/Plugins/MongoDBDriverPlugin/MongoDBFilterValue.swift index 9ad0f72307..821d74b632 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBFilterValue.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBFilterValue.swift @@ -63,7 +63,17 @@ enum MongoDBFilterValue { return "{\"$date\": {\"$numberLong\": \"\(millis)\"}}" } - static func epochMilliseconds(_ value: String) -> Int64? { + /// A date the grid writes. The date picker fills an empty cell with the user's own wall clock + /// and no zone, so zone-less text is read in the local zone here, where a filter reads it as + /// UTC. A bare number is a year or a count far more often than epoch milliseconds, so it is + /// not read as a date. + static func writableDateJson(_ value: String) -> String? { + guard Int64(value.trimmingCharacters(in: .whitespaces)) == nil, + let millis = epochMilliseconds(value, plainTimeZone: .current) else { return nil } + return "{\"$date\": {\"$numberLong\": \"\(millis)\"}}" + } + + static func epochMilliseconds(_ value: String, plainTimeZone: TimeZone = .gmt) -> Int64? { let trimmed = value.trimmingCharacters(in: .whitespaces) guard !trimmed.isEmpty else { return nil } @@ -77,7 +87,7 @@ enum MongoDBFilterValue { } } for format in plainFormats { - if let date = plainFormatter(format).date(from: trimmed) { + if let date = plainFormatter(format, timeZone: plainTimeZone).date(from: trimmed) { return Int64((date.timeIntervalSince1970 * 1000).rounded()) } } @@ -98,10 +108,10 @@ enum MongoDBFilterValue { "yyyy-MM-dd", ] - private static func plainFormatter(_ format: String) -> DateFormatter { + private static func plainFormatter(_ format: String, timeZone: TimeZone) -> DateFormatter { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(identifier: "UTC") + formatter.timeZone = timeZone formatter.dateFormat = format return formatter } diff --git a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift index 02895eee69..9a99367c1d 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift @@ -18,6 +18,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { private let rawFilterNormalizer = MongoDBRawFilterNormalizer() private var columnKindsByCollection: [String: [String: BsonValueKind]] = [:] private var fieldPathKindsByCollection: [String: [String: BsonValueKind]] = [:] + private var declaredSchemasByCollection: [String: MongoDBCollectionSchema] = [:] private static let logger = Logger(subsystem: "com.TablePro", category: "MongoDBPluginDriver") @@ -203,14 +204,19 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { valueCeiling: ceiling ) if let switched = outcome.databaseSwitch { currentDb = switched } + let declared = await declaredColumns(for: outcome) return capToRowCap( MongoScriptResultBuilder.result( for: outcome, startTime: startTime, + emptyColumns: MongoDBCollectionShape.emptyCollectionColumns(declaring: declared).map { + (name: $0, typeName: declaredTypeName(of: $0, in: declared)) + }, documents: { documents, collection, isTruncated in self.buildPluginResult( from: documents, startTime: startTime, - isTruncated: isTruncated, collection: collection + isTruncated: isTruncated, collection: collection, + declared: declared ) } ), @@ -224,6 +230,35 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { } } + /// The fields the collection declares, for a find that returned whole documents; empty for a + /// projection, a pipeline or anything else. Read on the session driver because that is the one + /// whose writer and filters use it: `fetchColumns` runs on a pooled driver whose caches this + /// one never sees. Read again on a first page, so a refresh picks up a changed validator, and + /// reused while paging. + private func declaredColumns(for outcome: MongoScriptStatementResult) async -> MongoDBCollectionSchema { + guard outcome.producedDocuments, + let find = outcome.find, find.database == currentDb, + let collection = outcome.collection, !collection.isEmpty, + let conn = mongoConnection else { + return .empty + } + let key = columnKindKey(collection) + let cached = columnKindLock.withLock { declaredSchemasByCollection[key] } + let schema: MongoDBCollectionSchema + if find.skip > 0, let cached { + schema = cached + } else { + schema = await declaredSchema(of: collection, conn: conn) + columnKindLock.withLock { declaredSchemasByCollection[key] = schema } + } + return find.returnsWholeDocuments ? schema : .empty + } + + private func declaredTypeName(of column: String, in schema: MongoDBCollectionSchema) -> String { + schema.field(named: column)?.columnTypeName(representation: uuidRepresentation) + ?? BsonDocumentFlattener.typeName(for: .objectId, representation: uuidRepresentation) + } + private func capToRowCap(_ result: PluginQueryResult, rowCap: Int?) -> PluginQueryResult { guard let rowCap, MongoDBFindLimitPolicy.isTruncated(rowCount: result.rows.count, rowCap: rowCap) else { return result @@ -293,66 +328,56 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { filter: "{}", sort: nil, projection: nil, skip: 0, limit: MongoStreamProjection.sampleSize ).docs - let enumMap = (try? await fetchJsonSchemaEnums(conn: conn, table: table)) ?? [:] + let schema = await declaredSchema(of: table, conn: conn) if docs.isEmpty { - return [ - PluginColumnInfo( - name: "_id", dataType: "ObjectId", isNullable: false, isPrimaryKey: true, - defaultValue: nil, extra: nil, charset: nil, collation: nil, comment: nil - ) - ] + return MongoDBCollectionShape.emptyCollectionColumns(declaring: schema) + .map { declaredColumnInfo($0, schema: schema) } } - let columns = BsonDocumentFlattener.unionColumns(from: docs) + let sampled = BsonDocumentFlattener.unionColumns(from: docs) let kinds = BsonDocumentFlattener.columnKinds( - for: columns, documents: docs, representation: uuidRepresentation + for: sampled, documents: docs, representation: uuidRepresentation ) - rememberColumnKinds(kinds, for: columns, collection: table) - - return columns.enumerated().map { index, name in - let typeName = BsonDocumentFlattener.typeName( - for: kinds[index], representation: uuidRepresentation - ) - return PluginColumnInfo( - name: name, dataType: typeName, isNullable: name != "_id", isPrimaryKey: name == "_id", + rememberColumnKinds(kinds, for: sampled, collection: table) + + let sampledColumns = sampled.enumerated().map { index, name in + PluginColumnInfo( + name: name, + dataType: BsonDocumentFlattener.typeName(for: kinds[index], representation: uuidRepresentation), + isNullable: name != MongoDBCollectionDDL.idField && schema.field(named: name)?.isRequired != true, + isPrimaryKey: name == MongoDBCollectionDDL.idField, defaultValue: nil, extra: nil, charset: nil, collation: nil, comment: nil, - allowedValues: enumMap[name] + allowedValues: schema.allowedValues[name] ) } + let unseen = MongoDBCollectionShape.declaredColumnsMissing(from: sampled, schema: schema) + return sampledColumns + unseen.map { declaredColumnInfo($0, schema: schema) } } - private func fetchJsonSchemaEnums(conn: MongoDBConnection, table: String) async throws -> [String: [String]] { - let escaped = escapeJsonString(table) - let result = try await conn.runCommand( - "{\"listCollections\": 1, \"filter\": {\"name\": \"\(escaped)\"}}", - database: currentDb + private func declaredColumnInfo(_ name: String, schema: MongoDBCollectionSchema) -> PluginColumnInfo { + let field = schema.field(named: name) + let isKey = name == MongoDBCollectionDDL.idField + return PluginColumnInfo( + name: name, + dataType: field?.columnTypeName(representation: uuidRepresentation) + ?? BsonDocumentFlattener.typeName(for: .objectId, representation: uuidRepresentation), + isNullable: !isKey && field?.isRequired != true, + isPrimaryKey: isKey, + defaultValue: nil, extra: nil, charset: nil, collation: nil, comment: nil, + allowedValues: schema.allowedValues[name] ) - guard let firstDoc = result.first, - let cursor = firstDoc["cursor"] as? [String: Any], - let firstBatch = cursor["firstBatch"] as? [[String: Any]], - let collInfo = firstBatch.first, - let options = collInfo["options"] as? [String: Any], - let validator = options["validator"] as? [String: Any], - let jsonSchema = validator["$jsonSchema"] as? [String: Any], - let properties = jsonSchema["properties"] as? [String: Any] - else { return [:] } - - var map: [String: [String]] = [:] - for (colName, spec) in properties { - guard let specDict = spec as? [String: Any] else { continue } - if let enumValues = extractStringEnum(specDict["enum"]) { - map[colName] = enumValues - } - } - return map } - private func extractStringEnum(_ value: Any?) -> [String]? { - guard let array = value as? [Any], !array.isEmpty else { return nil } - guard array.allSatisfy({ $0 is String }) else { return nil } - let strings = array.compactMap { $0 as? String } - return strings.isEmpty ? nil : strings + /// A user whose role can `find` but not `listCollections` still browses, with no declared + /// fields, so a failed read is an empty schema rather than an error. + private func declaredSchema(of collection: String, conn: MongoDBConnection) async -> MongoDBCollectionSchema { + guard let reply = try? await conn.runCommandJson( + MongoDBCollectionSchema.listCollectionsCommand(for: collection), database: currentDb + ) else { + return .empty + } + return MongoDBCollectionSchema.parse(listCollectionsReply: reply) } func fetchAllColumns(schema: String?) async throws -> [String: [PluginColumnInfo]] { @@ -672,6 +697,22 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { ["db.getCollection(\"\(escapeJsonString(table))\").deleteMany({})"] } + // MARK: - Collection Creation + + func generateCreateTableSQL(definition: PluginCreateTableDefinition) -> String? { + MongoDBCollectionDDL.createCollectionStatement(for: definition) + } + + func generateAddIndexSQL(table: String, index: PluginIndexDefinition) -> String? { + MongoDBCollectionDDL.createIndexStatement(collection: table, index: index) + } + + func schemaOperationRefusal(_ operation: PluginSchemaOperation) -> String? { + MongoDBCollectionDDL.refusal(for: operation) + } + + var unsupportedIndexTypes: Set { MongoDBCollectionDDL.unsupportedIndexTypes } + func dropDatabase(name: String) async throws { guard let conn = mongoConnection else { throw MongoDBPluginError.notConnected @@ -747,7 +788,8 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { insertedRowIndices: Set ) -> [(statement: String, parameters: [PluginCellValue])]? { let generator = MongoDBStatementGenerator( - collectionName: table, columns: columns, columnKinds: columnKinds(for: table) + collectionName: table, columns: columns, columnKinds: columnKinds(for: table), + declaredKinds: declaredKinds(for: table) ) return generator.generateStatements( from: changes, insertedRowData: insertedRowData, @@ -763,7 +805,8 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { rows: [[PluginCellValue]] ) -> [(statement: String, parameters: [PluginCellValue])]? { let generator = MongoDBStatementGenerator( - collectionName: table, columns: columns, columnKinds: columnKinds(for: table) + collectionName: table, columns: columns, columnKinds: columnKinds(for: table), + declaredKinds: declaredKinds(for: table) ) return generator.generateRestore(rows: rows) } @@ -857,7 +900,8 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { from documents: [[String: Any]], startTime: Date, isTruncated: Bool = false, - collection: String = "" + collection: String = "", + declared: MongoDBCollectionSchema = .empty ) -> PluginQueryResult { if documents.isEmpty { return PluginQueryResult( @@ -867,13 +911,17 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { ) } - let columns = BsonDocumentFlattener.unionColumns(from: documents) - let kinds = BsonDocumentFlattener.columnKinds( - for: columns, documents: documents, representation: uuidRepresentation + let sampled = BsonDocumentFlattener.unionColumns(from: documents) + let sampledKinds = BsonDocumentFlattener.columnKinds( + for: sampled, documents: documents, representation: uuidRepresentation ) - rememberColumnKinds(kinds, for: columns, collection: collection) + rememberColumnKinds(sampledKinds, for: sampled, collection: collection) rememberFieldPathKinds(from: documents, collection: collection) - let typeNames = kinds.map { BsonDocumentFlattener.typeName(for: $0, representation: uuidRepresentation) } + let unseen = MongoDBCollectionShape.declaredColumnsMissing(from: sampled, schema: declared) + let columns = sampled + unseen + let kinds = sampledKinds + unseen.map { declared.field(named: $0)?.valueKind ?? .null } + let typeNames = sampledKinds.map { BsonDocumentFlattener.typeName(for: $0, representation: uuidRepresentation) } + + unseen.map { declaredTypeName(of: $0, in: declared) } let rows = BsonDocumentFlattener.flatten( documents: documents, columns: columns, kinds: kinds, representation: uuidRepresentation ) @@ -891,18 +939,18 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { private func escapeJsonString(_ value: String) -> String { var result = "" result.reserveCapacity((value as NSString).length) - for char in value { - switch char { - case "\"": result += "\\\"" + for scalar in value.unicodeScalars { + switch scalar { case "\\": result += "\\\\" + case "\"": result += "\\\"" case "\n": result += "\\n" case "\r": result += "\\r" case "\t": result += "\\t" default: - if let ascii = char.asciiValue, ascii < 0x20 { - result += String(format: "\\u%04x", ascii) + if scalar.value < 0x20 { + result += String(format: "\\u%04x", scalar.value) } else { - result.append(char) + result.unicodeScalars.append(scalar) } } } @@ -934,6 +982,11 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { return columnKindLock.withLock { columnKindsByCollection[key] ?? [:] } } + private func declaredKinds(for collection: String) -> [String: BsonValueKind] { + let key = columnKindKey(collection) + return columnKindLock.withLock { declaredSchemasByCollection[key]?.valueKinds ?? [:] } + } + /// Recorded from the documents a browse already fetched, on the session driver that will /// build the filter. Sampling through `sampleFieldPaths` cannot do it: that call is routed /// through `MetadataConnectionPool`, so it lands on a different driver instance whose cache @@ -953,7 +1006,9 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { private func filterKinds(for collection: String) -> [String: BsonValueKind] { let key = columnKindKey(collection) return columnKindLock.withLock { - (fieldPathKindsByCollection[key] ?? [:]).merging(columnKindsByCollection[key] ?? [:]) { _, top in top } + (declaredSchemasByCollection[key]?.valueKinds ?? [:]) + .merging(fieldPathKindsByCollection[key] ?? [:]) { _, sampled in sampled } + .merging(columnKindsByCollection[key] ?? [:]) { _, top in top } } } diff --git a/Plugins/MongoDBDriverPlugin/MongoDBQueryBuilder.swift b/Plugins/MongoDBDriverPlugin/MongoDBQueryBuilder.swift index 753b3f9256..0d5b1bad3a 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBQueryBuilder.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBQueryBuilder.swift @@ -380,18 +380,18 @@ struct MongoDBQueryBuilder { static func escapeJsonString(_ value: String) -> String { var result = "" result.reserveCapacity((value as NSString).length) - for char in value { - switch char { + for scalar in value.unicodeScalars { + switch scalar { case "\\": result += "\\\\" case "\"": result += "\\\"" case "\n": result += "\\n" case "\r": result += "\\r" case "\t": result += "\\t" default: - if let ascii = char.asciiValue, ascii < 0x20 { - result += String(format: "\\u%04X", ascii) + if scalar.value < 0x20 { + result += String(format: "\\u%04X", scalar.value) } else { - result.append(char) + result.unicodeScalars.append(scalar) } } } diff --git a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift index 6c0a4bb328..9d31620c9a 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift @@ -17,6 +17,9 @@ struct MongoDBStatementGenerator { let collectionName: String let columns: [String] var columnKinds: [String: BsonValueKind] = [:] + /// Kinds the collection's validator declares. The server rejects any other type for these + /// fields, so they outrank what the sampled documents happen to hold. + var declaredKinds: [String: BsonValueKind] = [:] private var collectionAccessor: String { MongoCollectionAccessor.expression(for: collectionName) @@ -233,19 +236,34 @@ struct MongoDBStatementGenerator { "{\"_id\": \(idValueJson(idValue))}" } + /// An `_id` is matched by value and type together, so a string `_id` of `1001` written as a + /// number matches no document. The column's own kind decides when it is known; the text's + /// shape is only the fallback. private func idValueJson(_ idValue: String) -> String { if let binary = MongoDBUuidCodec.extendedJsonFromWrapper(idValue) { return binary } + switch kind(of: "_id") { + case .string: + return "\"\(escapeJsonString(idValue))\"" + case .objectId, .int32, .int64, .double, .decimal128: + if let typed = typedJson(idValue, kind: kind(of: "_id")) { return typed } + default: + break + } if isObjectIdString(idValue) { return "{\"$oid\": \"\(idValue)\"}" } if Int64(idValue) != nil { - return idValue + return integerJson(idValue) } return "\"\(escapeJsonString(idValue))\"" } + private func kind(of field: String) -> BsonValueKind? { + declaredKinds[field] ?? columnKinds[field] + } + /// Check if a string looks like a MongoDB ObjectId (24 hex characters) private func isObjectIdString(_ value: String) -> Bool { let nsValue = value as NSString @@ -262,69 +280,96 @@ struct MongoDBStatementGenerator { ) return nil } - entries.append("\"\(escapeJsonString(key))\": \(jsonValue(for: value, kind: columnKinds[key]))") + entries.append("\"\(escapeJsonString(key))\": \(jsonValue(for: value, field: key))") } return "{\(entries.joined(separator: ", "))}" } - /// Convert a string value to its JSON representation (auto-detect type) - private func jsonValue(for value: String, kind: BsonValueKind? = nil) -> String { - if value == "true" || value == "false" { - return value + /// A cell's text as the value it stands for, in the field's type when that type is known. + /// + /// The statement is JavaScript the shell evaluates, so text is only ever pasted in when it is + /// strict JSON. A stored string that merely starts with `[` and ends with `]` would otherwise run + /// as code the moment its row is duplicated or its delete is undone. + private func jsonValue(for value: String, field: String) -> String { + if declaredKinds[field] == .string { + return "\"\(escapeJsonString(value))\"" } - if value == "null" { - return "null" + if value == "true" || value == "false" || value == "null" { + return value } - if kind == .decimal128, NumberText.isJSONNumberLiteral(value) { - return "{\"$numberDecimal\": \"\(escapeJsonString(value))\"}" + if let typed = typedJson(value, kind: kind(of: field)) { + return typed } if MongoDBJsonNumber.isValid(value) { - return typedNumberJson(value, kind: kind) + return Int64(value) != nil ? integerJson(value) : value } if let binary = MongoDBUuidCodec.extendedJsonFromWrapper(value) { return binary } - // JSON object or array - if (value.hasPrefix("{") && value.hasSuffix("}")) || - (value.hasPrefix("[") && value.hasSuffix("]")) { + if isStrictJsonContainer(value) { return value } return "\"\(escapeJsonString(value))\"" } /// A bare JSON number is stored as int32 or double, which silently retypes a column that - /// holds int64 or decimal128. Extended JSON is the only way to keep the original type. - private func typedNumberJson(_ value: String, kind: BsonValueKind?) -> String { + /// holds int64 or decimal128, and a date or an ObjectId written as its text is a string. + /// Extended JSON is the only way to keep the original type. + private func typedJson(_ value: String, kind: BsonValueKind?) -> String? { switch kind { + case .date: + return MongoDBFilterValue.writableDateJson(value) + case .objectId: + return MongoDBFilterValue.objectIdJson(value) case .decimal128: + guard NumberText.isJSONNumberLiteral(value) else { return nil } return "{\"$numberDecimal\": \"\(escapeJsonString(value))\"}" case .int64: - guard Int64(value) != nil else { return value } + guard Int64(value) != nil else { return nil } return "{\"$numberLong\": \"\(escapeJsonString(value))\"}" case .double: - guard let parsed = Double(value), parsed.isFinite else { return value } + guard MongoDBJsonNumber.isValid(value), let parsed = Double(value), parsed.isFinite else { return nil } return "{\"$numberDouble\": \"\(escapeJsonString(value))\"}" - default: + case .int32: + guard Int32(value) != nil else { return nil } return value + default: + return nil } } + /// JavaScript numbers are doubles, so an integer past 2^53 written bare reaches the server + /// already rounded. + private func integerJson(_ value: String) -> String { + guard let parsed = Int64(value), parsed.magnitude > Self.largestExactDouble else { return value } + return "{\"$numberLong\": \"\(value)\"}" + } + + private static let largestExactDouble: UInt64 = 1 << 53 + + private func isStrictJsonContainer(_ value: String) -> Bool { + guard (value.hasPrefix("{") && value.hasSuffix("}")) || (value.hasPrefix("[") && value.hasSuffix("]")), + let data = value.data(using: .utf8), + let parsed = try? JSONSerialization.jsonObject(with: data) else { return false } + return parsed is [String: Any] || parsed is [Any] + } + /// Escape special characters for JSON strings (handles Unicode control chars U+0000-U+001F) private func escapeJsonString(_ value: String) -> String { var result = "" result.reserveCapacity((value as NSString).length) - for char in value { - switch char { + for scalar in value.unicodeScalars { + switch scalar { case "\\": result += "\\\\" case "\"": result += "\\\"" case "\n": result += "\\n" case "\r": result += "\\r" case "\t": result += "\\t" default: - if let ascii = char.asciiValue, ascii < 0x20 { - result += String(format: "\\u%04X", ascii) + if scalar.value < 0x20 { + result += String(format: "\\u%04X", scalar.value) } else { - result.append(char) + result.unicodeScalars.append(scalar) } } } diff --git a/Plugins/MongoDBDriverPlugin/MongoScriptHost.swift b/Plugins/MongoDBDriverPlugin/MongoScriptHost.swift index f30e8f68c6..637f5bc8c1 100644 --- a/Plugins/MongoDBDriverPlugin/MongoScriptHost.swift +++ b/Plugins/MongoDBDriverPlugin/MongoScriptHost.swift @@ -183,9 +183,15 @@ final class MongoScriptHost { return try cursor.remaining { try load($0, ceiling: valueCeiling) } } - func cursorDescription(handle: Int) -> (collection: String, isFind: Bool)? { + func cursorDescription(handle: Int) -> (collection: String, find: MongoScriptFindShape?)? { guard let cursor = try? cursors.cursor(for: handle) else { return nil } - return (cursor.collection, cursor.isFind) + guard cursor.isFind else { return (cursor.collection, nil) } + let shape = MongoScriptFindShape( + database: cursor.database, + returnsWholeDocuments: cursor.options.projection == nil, + skip: cursor.options.skip ?? 0 + ) + return (cursor.collection, shape) } /// The query a cursor stands for, without running it. diff --git a/Plugins/MongoDBDriverPlugin/MongoScriptResultBuilder.swift b/Plugins/MongoDBDriverPlugin/MongoScriptResultBuilder.swift index 367f15c520..274b1960e8 100644 --- a/Plugins/MongoDBDriverPlugin/MongoScriptResultBuilder.swift +++ b/Plugins/MongoDBDriverPlugin/MongoScriptResultBuilder.swift @@ -10,13 +10,15 @@ enum MongoScriptResultBuilder { static func result( for outcome: MongoScriptStatementResult, startTime: Date, + emptyColumns: [(name: String, typeName: String)] = [(name: "_id", typeName: "ObjectId")], documents build: ([[String: Any]], String, Bool) -> PluginQueryResult ) -> PluginQueryResult { if outcome.producedDocuments, outcome.documents.json.isEmpty { // Zero columns reads as write-success in the result pane, so a query that matched // nothing has to keep its row-producing shape. return PluginQueryResult( - columns: ["_id"], columnTypeNames: ["ObjectId"], rows: [], rowsAffected: 0, + columns: emptyColumns.map(\.name), columnTypeNames: emptyColumns.map(\.typeName), + rows: [], rowsAffected: 0, executionTime: Date().timeIntervalSince(startTime) ) } diff --git a/Plugins/MongoDBDriverPlugin/MongoScriptRuntime.swift b/Plugins/MongoDBDriverPlugin/MongoScriptRuntime.swift index dcfb993517..95c327bf16 100644 --- a/Plugins/MongoDBDriverPlugin/MongoScriptRuntime.swift +++ b/Plugins/MongoDBDriverPlugin/MongoScriptRuntime.swift @@ -3,10 +3,20 @@ import JavaScriptCore import os import TableProPluginKit +/// The find a statement's documents came from. A find with no projection returns whole +/// documents, so its columns can include what the collection declares; a projection or a pipeline +/// chose its own shape. +struct MongoScriptFindShape: Equatable, Sendable { + let database: String + let returnsWholeDocuments: Bool + let skip: Int +} + /// What one statement of a script evaluated to. struct MongoScriptStatementResult: Sendable { var documents = MongoScriptDocumentBatch.empty var collection: String? + var find: MongoScriptFindShape? var scalarRows: [String]? var printedLines: [String] = [] var databaseSwitch: String? @@ -298,7 +308,9 @@ final class MongoScriptRuntime: @unchecked Sendable { case "cursor": guard let handle = payload["handle"] as? Int else { return } result.documents = try engine.host.drain(handle: handle) - result.collection = engine.host.cursorDescription(handle: handle)?.collection + let description = engine.host.cursorDescription(handle: handle) + result.collection = description?.collection + result.find = description?.find result.producedDocuments = true case "array": guard let json = payload["json"] as? String else { return } diff --git a/Plugins/TableProPluginKit/MongoCollectionAccessor.swift b/Plugins/TableProPluginKit/MongoCollectionAccessor.swift index 45d6cad621..c4383d6ac8 100644 --- a/Plugins/TableProPluginKit/MongoCollectionAccessor.swift +++ b/Plugins/TableProPluginKit/MongoCollectionAccessor.swift @@ -28,9 +28,20 @@ public enum MongoCollectionAccessor { name.hasPrefix("__") || databaseMemberNames.contains(name) } + /// Checked byte by byte, because a `Character` is a whole grapheme cluster: a Unicode Prepend + /// letter such as U+0D4E joined to a `(` or `;` answers `isLetter` for the pair, and the name + /// then reached the statement bare, as code. Everything else goes through `getCollection`. private static func isPlainIdentifier(_ name: String) -> Bool { - guard let first = name.first, !first.isNumber else { return false } - return name.allSatisfy { $0.isLetter || $0.isNumber || $0 == "_" } + guard let first = name.utf8.first, !isASCIIDigit(first) else { return false } + return name.utf8.allSatisfy { isASCIILetter($0) || isASCIIDigit($0) || $0 == UInt8(ascii: "_") } + } + + private static func isASCIILetter(_ byte: UInt8) -> Bool { + (UInt8(ascii: "a") ... UInt8(ascii: "z")).contains(byte) || (UInt8(ascii: "A") ... UInt8(ascii: "Z")).contains(byte) + } + + private static func isASCIIDigit(_ byte: UInt8) -> Bool { + (UInt8(ascii: "0") ... UInt8(ascii: "9")).contains(byte) } /// Every method mongosh puts on `db`, plus what `Object.prototype` gives any JavaScript value. diff --git a/TablePro/Core/Database/CreateTableEligibility.swift b/TablePro/Core/Database/CreateTableEligibility.swift new file mode 100644 index 0000000000..faffcad27e --- /dev/null +++ b/TablePro/Core/Database/CreateTableEligibility.swift @@ -0,0 +1,40 @@ +// +// CreateTableEligibility.swift +// TablePro +// + +import Foundation +import TableProPluginKit + +/// Whether New Table can do anything on a connection. The Create Table editor turns a draft into +/// statements through the driver's form or its `generateCreateTableSQL`, and an engine with neither +/// used to be offered the editor anyway and refuse only after the grid was filled in. So the driver +/// is asked here, with a one-column probe, the way `DatabaseObjectToolEligibility` asks its hooks. +enum CreateTableEligibility { + static let probeDefinition = PluginCreateTableDefinition( + tableName: "t", + columns: [ + PluginColumnDefinition( + name: "c", + dataType: "TEXT", + isNullable: true, + defaultValue: nil, + isPrimaryKey: false, + autoIncrement: false, + comment: nil, + unsigned: false, + onUpdate: nil, + charset: nil, + collation: nil + ) + ] + ) + + @MainActor + static func canCreateTable(with driver: DatabaseDriver?) -> Bool { + guard let driver else { return false } + if driver.createTableFormSpec(schema: nil) != nil { return true } + guard let adapter = driver as? PluginDriverAdapter else { return false } + return adapter.generateCreateTableSQL(definition: probeDefinition) != nil + } +} diff --git a/TablePro/Core/Plugins/ImportDataSinkAdapter.swift b/TablePro/Core/Plugins/ImportDataSinkAdapter.swift index cf5b282f9f..1626b7b891 100644 --- a/TablePro/Core/Plugins/ImportDataSinkAdapter.swift +++ b/TablePro/Core/Plugins/ImportDataSinkAdapter.swift @@ -26,6 +26,13 @@ final class ImportDataSinkAdapter: PluginImportDataSink, @unchecked Sendable { private static let logger = Logger(subsystem: "com.TablePro", category: "ImportDataSinkAdapter") + /// Rows go in as SQL `INSERT`s, so an engine with no SQL dialect can take none of them. + static func canWriteRows(into databaseType: DatabaseType) -> Bool { + (try? SQLStatementGenerator( + tableName: "t", columns: [], primaryKeyColumns: [], databaseType: databaseType + )) != nil + } + init( driver: DatabaseDriver, databaseType: DatabaseType, diff --git a/TablePro/Core/Services/Infrastructure/MainSplitViewController+MenuValidation.swift b/TablePro/Core/Services/Infrastructure/MainSplitViewController+MenuValidation.swift index 3e480b9cb0..77d22c2f90 100644 --- a/TablePro/Core/Services/Infrastructure/MainSplitViewController+MenuValidation.swift +++ b/TablePro/Core/Services/Infrastructure/MainSplitViewController+MenuValidation.swift @@ -117,6 +117,7 @@ struct MenuValidationContext: Equatable { var canRefreshMaterializedView = false var canEditObjectComment = false var canCreateDatabase = false + var canCreateTable = false var canCopyObjects = false var canDuplicateDatabase = false var hasMaintenanceOperations = false @@ -278,7 +279,9 @@ extension MainSplitViewController: NSMenuItemValidation { case #selector(delete(_:)): return context.isConnected && (context.canDeleteSelectedRows || context.canDropSelectedTables) - case #selector(createNewTable(_:)), #selector(createNewView(_:)): + case #selector(createNewTable(_:)): + return context.isConnected && !context.isReadOnly && context.canCreateTable + case #selector(createNewView(_:)): return context.isConnected && !context.isReadOnly case #selector(createNewDatabase(_:)): return context.canCreateDatabase @@ -623,6 +626,7 @@ extension MainSplitViewController: NSMenuItemValidation { canRefreshMaterializedView: actions.canRefreshMaterializedView, canEditObjectComment: actions.canEditObjectComment, canCreateDatabase: actions.canCreateDatabase, + canCreateTable: actions.canCreateTable, canCopyObjects: actions.canCopyObjects, canDuplicateDatabase: actions.canDuplicateDatabase, hasMaintenanceOperations: !actions.maintenanceOperations.isEmpty, diff --git a/TablePro/Models/Schema/CreateTableDraftBuilder.swift b/TablePro/Models/Schema/CreateTableDraftBuilder.swift index c663305445..9149fb8acd 100644 --- a/TablePro/Models/Schema/CreateTableDraftBuilder.swift +++ b/TablePro/Models/Schema/CreateTableDraftBuilder.swift @@ -55,7 +55,8 @@ enum CreateTableDraftBuilder { indexes: [EditableIndexDefinition], foreignKeys: [EditableForeignKeyDefinition], dialect: ForeignKeyDialect, - includesEngineOptions: Bool + includesEngineOptions: Bool, + suppliesItsOwnKey: Bool = false ) -> CreateTablePlan { var issues: [SchemaDraftIssue] = [] @@ -72,14 +73,14 @@ enum CreateTableDraftBuilder { tab: .columns, row: nil, message: String(localized: "The table needs a name.") )) } - if resolvedColumns.isEmpty { + if resolvedColumns.isEmpty, !suppliesItsOwnKey { issues.append(SchemaDraftIssue( tab: .columns, row: nil, message: String(localized: "The table needs at least one column with a name and a type.") )) } - guard !trimmedName.isEmpty, !resolvedColumns.isEmpty else { + guard !trimmedName.isEmpty, !resolvedColumns.isEmpty || suppliesItsOwnKey else { return CreateTablePlan(definition: nil, indexes: [], issues: issues) } diff --git a/TablePro/Resources/Localizable.xcstrings b/TablePro/Resources/Localizable.xcstrings index 27de0972c3..dcbef14383 100644 --- a/TablePro/Resources/Localizable.xcstrings +++ b/TablePro/Resources/Localizable.xcstrings @@ -184127,6 +184127,36 @@ }, "The write was applied, but the servers did not confirm it as the write concern asks: %@" : { + }, + "MongoDB cannot address a field named %@. A field name cannot start with $ or contain a dot." : { + + }, + "%1$@ has type %2$@, which MongoDB does not have. Choose a type from the list." : { + + }, + "_id must be an objectId. MongoDB generates it for every new document." : { + + }, + "%@ cannot be the primary key. MongoDB keys every document by _id." : { + + }, + "Index %@ has a WHERE clause, an expression, included columns or a prefix length. MongoDB indexes take none of these." : { + + }, + "MongoDB has no %@ index." : { + + }, + "MongoDB already indexes _id." : { + + }, + "A hashed index cannot be unique." : { + + }, + "A hashed index takes one field." : { + + }, + "%@ cannot take rows from this file, so no table was created." : { + } }, "version" : "1.1" diff --git a/TablePro/Views/Import/RowImportSheet.swift b/TablePro/Views/Import/RowImportSheet.swift index 785af9d252..3aa7eef7db 100644 --- a/TablePro/Views/Import/RowImportSheet.swift +++ b/TablePro/Views/Import/RowImportSheet.swift @@ -920,6 +920,12 @@ struct RowImportSheet: View { @MainActor private func prepareTable(_ definition: PluginCreateTableDefinition, scope: DatabaseScope) async throws { + guard ImportDataSinkAdapter.canWriteRows(into: connection.type) else { + throw PluginImportError.importFailed(String( + format: String(localized: "%@ cannot take rows from this file, so no table was created."), + connection.type.rawValue + )) + } let tableName = definition.tableName let statements = try await DatabaseManager.shared.createTableStatements( definition: definition, diff --git a/TablePro/Views/Main/Extensions/MainContentCoordinator+SidebarActions.swift b/TablePro/Views/Main/Extensions/MainContentCoordinator+SidebarActions.swift index cfcee863db..4139116b15 100644 --- a/TablePro/Views/Main/Extensions/MainContentCoordinator+SidebarActions.swift +++ b/TablePro/Views/Main/Extensions/MainContentCoordinator+SidebarActions.swift @@ -89,7 +89,10 @@ extension MainContentCoordinator { // MARK: - Table Operations func createNewTable() { - guard !safeModeLevel.blocksAllWrites else { return } + guard !safeModeLevel.blocksAllWrites, + CreateTableEligibility.canCreateTable(with: DatabaseManager.shared.driver(for: connection.id)) else { + return + } if tabManager.tabs.isEmpty { tabManager.addCreateTableTab(databaseName: browseDatabaseName) diff --git a/TablePro/Views/Main/MainContentCommandActions+DatabaseObjects.swift b/TablePro/Views/Main/MainContentCommandActions+DatabaseObjects.swift index 83b8f6978c..1c6dbfc852 100644 --- a/TablePro/Views/Main/MainContentCommandActions+DatabaseObjects.swift +++ b/TablePro/Views/Main/MainContentCommandActions+DatabaseObjects.swift @@ -105,6 +105,13 @@ extension MainContentCommandActions { isConnected && supportsContainerSwitching && !isReadOnly } + var canCreateTable: Bool { + guard isConnected, !isReadOnly, let coordinator else { return false } + return CreateTableEligibility.canCreateTable( + with: DatabaseManager.shared.driver(for: coordinator.connection.id) + ) + } + func createDatabase() { coordinator?.activeSheet = .createDatabase } diff --git a/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator+Menu.swift b/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator+Menu.swift index 2374e5bedd..80da297b55 100644 --- a/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator+Menu.swift +++ b/TablePro/Views/Sidebar/DatabaseTreeOutlineCoordinator+Menu.swift @@ -105,6 +105,7 @@ extension DatabaseTreeOutlineCoordinator: NSMenuDelegate { ), canBackUp: backupIsAvailable(), canCreateType: DatabaseManager.shared.driver(for: connectionId)?.createTypeTemplate(schema: nil) != nil, + canCreateTable: CreateTableEligibility.canCreateTable(with: DatabaseManager.shared.driver(for: connectionId)), objectToolSupport: .of(DatabaseManager.shared.driver(for: connectionId)) ) } diff --git a/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuContext.swift b/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuContext.swift index aee95e269f..6eea1e81c1 100644 --- a/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuContext.swift +++ b/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuContext.swift @@ -60,6 +60,8 @@ internal struct DatabaseTreeMenuContext { internal var canBackUp: Bool = false /// Whether the driver can offer a CREATE TYPE template. Read-only mode still hides the item. internal var canCreateType: Bool = false + /// Whether the driver can create a table, from `CreateTableEligibility`. + internal var canCreateTable: Bool = false /// Which materialized-view and comment commands the driver has statements for. internal var objectToolSupport: DatabaseObjectToolEligibility.Support = .none } diff --git a/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuSpec.swift b/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuSpec.swift index e66d26c008..befa48dfa2 100644 --- a/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuSpec.swift +++ b/TablePro/Views/Sidebar/Menu/DatabaseTreeMenuSpec.swift @@ -601,7 +601,9 @@ internal enum DatabaseTreeMenuSpec { private static func backgroundSections(_ context: DatabaseTreeMenuContext) -> [DatabaseTreeMenuSection] { var creation: [DatabaseTreeMenuItem] = [] if !context.isReadOnly { - creation.append(.command(String(localized: "New Table…"), .createTable)) + if context.canCreateTable { + creation.append(.command(String(localized: "New Table…"), .createTable)) + } creation.append(.command(String(localized: "New View…"), .createView)) } creation += newSchemaItems(database: context.activeDatabase, context: context) diff --git a/TablePro/Views/Structure/CreateTableDraft.swift b/TablePro/Views/Structure/CreateTableDraft.swift index daaed49645..1d40f41b75 100644 --- a/TablePro/Views/Structure/CreateTableDraft.swift +++ b/TablePro/Views/Structure/CreateTableDraft.swift @@ -83,7 +83,8 @@ internal final class CreateTableDraft: ObservableObject { indexes: changeManager.workingIndexes, foreignKeys: changeManager.workingForeignKeys, dialect: ForeignKeyDialect.forType(databaseType), - includesEngineOptions: Self.offersEngineOptions(for: databaseType) + includesEngineOptions: Self.offersEngineOptions(for: databaseType), + suppliesItsOwnKey: PluginManager.shared.defaultPrimaryKeyColumn(for: databaseType) != nil ) } diff --git a/TablePro/Views/Structure/CreateTableView.swift b/TablePro/Views/Structure/CreateTableView.swift index 03e973b502..12c7f43afa 100644 --- a/TablePro/Views/Structure/CreateTableView.swift +++ b/TablePro/Views/Structure/CreateTableView.swift @@ -402,7 +402,7 @@ struct CreateTableView: View { if composed.statements.isEmpty { sqlPreviewPlaceholder(composed.issues.first?.qualifiedMessage) } else { - DDLTextView(ddl: composed.preview, fontSize: .constant(13)) + DDLTextView(ddl: composed.preview, fontSize: .constant(13), databaseType: connection.type) } } else if let failure = draft.compositionFailure { sqlPreviewPlaceholder(failure) diff --git a/TableProTests/Core/Database/CreateTableEligibilityTests.swift b/TableProTests/Core/Database/CreateTableEligibilityTests.swift new file mode 100644 index 0000000000..5a3af5bb35 --- /dev/null +++ b/TableProTests/Core/Database/CreateTableEligibilityTests.swift @@ -0,0 +1,79 @@ +// +// CreateTableEligibilityTests.swift +// TableProTests +// + +import Foundation +@testable import TablePro +import TableProPluginKit +import Testing + +private class EligibilityBaseDriver { + var supportsTransactions: Bool { false } + var serverVersion: String? { nil } + + func connect() async throws {} + func disconnect() {} + + func fetchTables(schema: String?) async throws -> [PluginTableInfo] { [] } + func fetchColumns(table: String, schema: String?) async throws -> [PluginColumnInfo] { [] } + func fetchIndexes(table: String, schema: String?) async throws -> [PluginIndexInfo] { [] } + func fetchForeignKeys(table: String, schema: String?) async throws -> [PluginForeignKeyInfo] { [] } + func fetchTableDDL(table: String, schema: String?) async throws -> String { "" } + func fetchViewDefinition(view: String, schema: String?) async throws -> String { "" } + func fetchTableMetadata(table: String, schema: String?) async throws -> PluginTableMetadata { + PluginTableMetadata(tableName: table) + } + + func fetchDatabases() async throws -> [String] { [] } + func fetchDatabaseMetadata(_ database: String) async throws -> PluginDatabaseMetadata { + PluginDatabaseMetadata(name: database) + } + + func execute(query: String) async throws -> PluginQueryResult { + PluginQueryResult(columns: [], columnTypeNames: [], rows: [], rowsAffected: 0, executionTime: 0) + } + + func switchDatabase(to database: String) async throws {} +} + +private final class NoCreateDriver: EligibilityBaseDriver, PluginDatabaseDriver, @unchecked Sendable {} + +private final class SQLCreateDriver: EligibilityBaseDriver, PluginDatabaseDriver, @unchecked Sendable { + func generateCreateTableSQL(definition: PluginCreateTableDefinition) -> String? { + "CREATE TABLE \(definition.tableName) (\(definition.columns.map { "\($0.name) \($0.dataType)" }.joined(separator: ", ")))" + } +} + +private final class FormCreateDriver: EligibilityBaseDriver, PluginDatabaseDriver, @unchecked Sendable { + func createTableFormSpec(schema: String?) -> PluginCreateTableFormSpec? { + PluginCreateTableFormSpec(sections: []) + } +} + +@MainActor +struct CreateTableEligibilityTests { + private func adapter(_ driver: any PluginDatabaseDriver) -> PluginDriverAdapter { + PluginDriverAdapter(connection: TestFixtures.makeConnection(type: .postgresql), pluginDriver: driver) + } + + @Test("A driver with no create hook cannot create a table") + func driverWithoutAHookCannotCreate() { + #expect(!CreateTableEligibility.canCreateTable(with: adapter(NoCreateDriver()))) + } + + @Test("A driver that writes CREATE TABLE can create a table") + func sqlDriverCanCreate() { + #expect(CreateTableEligibility.canCreateTable(with: adapter(SQLCreateDriver()))) + } + + @Test("A driver with its own Create Table form can create a table") + func formDriverCanCreate() { + #expect(CreateTableEligibility.canCreateTable(with: adapter(FormCreateDriver()))) + } + + @Test("No driver means no New Table") + func noDriverCannotCreate() { + #expect(!CreateTableEligibility.canCreateTable(with: nil)) + } +} diff --git a/TableProTests/Core/Menu/MainMenuBuilderTests.swift b/TableProTests/Core/Menu/MainMenuBuilderTests.swift index 16bebd2b03..a4d37c68b3 100644 --- a/TableProTests/Core/Menu/MainMenuBuilderTests.swift +++ b/TableProTests/Core/Menu/MainMenuBuilderTests.swift @@ -564,12 +564,24 @@ struct MainMenuValidationTests { context.isCurrentTabSchemaResolved = true context.hasTableSelection = true context.canTruncateSelectedTables = true + context.canCreateTable = true context.isReadOnly = true #expect(!enabled(#selector(MainSplitViewController.addRow(_:)), context)) #expect(!enabled(#selector(MainSplitViewController.truncateTable(_:)), context)) #expect(!enabled(#selector(MainSplitViewController.createNewTable(_:)), context)) } + @Test("New Table is disabled for an engine that cannot create a table") + func newTableFollowsTheDriver() { + var context = MenuValidationContext() + context.isConnected = true + #expect(!enabled(#selector(MainSplitViewController.createNewTable(_:)), context)) + #expect(enabled(#selector(MainSplitViewController.createNewView(_:)), context)) + + context.canCreateTable = true + #expect(enabled(#selector(MainSplitViewController.createNewTable(_:)), context)) + } + /// A view is a valid selection and a hopeless truncate. The menu bar used to ask only whether /// anything was selected, so it offered Truncate Table for one and the server refused the save. @Test("Truncate Table is disabled for a selection holding nothing truncatable") @@ -672,6 +684,7 @@ struct MainMenuValidationTests { context.canShowTableStructure = true context.canEditViewDefinition = true context.hasMaintenanceOperations = true + context.canCreateTable = true return context } diff --git a/TableProTests/Core/MongoDB/MongoScriptPreludeTests.swift b/TableProTests/Core/MongoDB/MongoScriptPreludeTests.swift index b42e6cede7..7026a57126 100644 --- a/TableProTests/Core/MongoDB/MongoScriptPreludeTests.swift +++ b/TableProTests/Core/MongoDB/MongoScriptPreludeTests.swift @@ -5,6 +5,7 @@ import Foundation import JavaScriptCore +import TableProPluginKit import Testing /// Drives the real prelude in a real `JSContext` against a recording host. @@ -63,6 +64,66 @@ struct MongoScriptPreludeTests { ) } + @Test("A Create Table statement reaches the driver as one create command carrying its validator") + func createCollectionCarriesTheValidator() throws { + let host = RecordingHost() + host.replies = ["{\"ok\": 1}"] + let context = try makeContext(host) + let columns = [ + PluginColumnDefinition( + name: "title", dataType: "string", isNullable: false, defaultValue: nil, isPrimaryKey: false, + autoIncrement: false, comment: nil, unsigned: false, onUpdate: nil, charset: nil, collation: nil + ), + PluginColumnDefinition( + name: "tags", dataType: "array", isNullable: true, defaultValue: nil, isPrimaryKey: false, + autoIncrement: false, comment: nil, unsigned: false, onUpdate: nil, charset: nil, collation: nil + ) + ] + let statement = MongoDBCollectionDDL.createCollectionStatement( + for: PluginCreateTableDefinition(tableName: "articles", columns: columns) + ) + + context.evaluateScript(statement) + #expect(context.exception == nil) + + let request = try #require(host.requests(op: "command").first) + let commandText = try #require(request["command"] as? String) + let commandData = try #require(commandText.data(using: .utf8)) + let command = try #require(try JSONSerialization.jsonObject(with: commandData) as? [String: Any]) + #expect(command["create"] as? String == "articles") + let validator = try #require(command["validator"] as? [String: Any]) + let schema = try #require(validator["$jsonSchema"] as? [String: Any]) + #expect(schema["required"] as? [String] == ["title"]) + let properties = try #require(schema["properties"] as? [String: Any]) + #expect((properties["tags"] as? [String: Any])?["bsonType"] as? [String] == ["array", "null"]) + let propertiesAt = try #require(commandText.range(of: "\"properties\"")) + let tail = commandText[propertiesAt.upperBound...] + let titleAt = try #require(tail.range(of: "\"title\"")) + let tagsAt = try #require(tail.range(of: "\"tags\"")) + #expect(titleAt.lowerBound < tagsAt.lowerBound) + } + + @Test("A Create Table index reaches the driver with its keys in order and its options") + func createIndexKeepsKeysAndOptions() throws { + let host = RecordingHost() + let context = try makeContext(host) + let statement = try #require(MongoDBCollectionDDL.createIndexStatement( + collection: "articles", + index: PluginIndexDefinition(name: "lang_date", columns: ["lang", "date"], isUnique: true) + )) + + context.evaluateScript(statement) + #expect(context.exception == nil) + + let request = try #require(host.requests(op: "createIndex").first) + #expect(request["keys"] as? String == "{\"lang\":{\"$numberInt\":\"1\"},\"date\":{\"$numberInt\":\"1\"}}") + let optionsText = try #require(request["options"] as? String) + let optionsData = try #require(optionsText.data(using: .utf8)) + let options = try #require(try JSONSerialization.jsonObject(with: optionsData) as? [String: Any]) + #expect(options["name"] as? String == "lang_date") + #expect(options["unique"] as? Bool == true) + } + @Test("The prelude loads without a syntax error") func preludeLoads() throws { let host = RecordingHost() diff --git a/TableProTests/Models/Schema/CreateTableDraftBuilderTests.swift b/TableProTests/Models/Schema/CreateTableDraftBuilderTests.swift index f2847a200f..40f9b67d1b 100644 --- a/TableProTests/Models/Schema/CreateTableDraftBuilderTests.swift +++ b/TableProTests/Models/Schema/CreateTableDraftBuilderTests.swift @@ -65,6 +65,37 @@ struct CreateTableDraftBuilderTests { ) } + // MARK: - Engines that key every row themselves + + private func namedOnlyPlan(suppliesItsOwnKey: Bool) -> CreateTablePlan { + CreateTableDraftBuilder.plan( + tableName: "events", + options: CreateTableOptions(), + columns: [column("", "")], + indexes: [], + foreignKeys: [], + dialect: ForeignKeyDialect.forType(.sqlite), + includesEngineOptions: false, + suppliesItsOwnKey: suppliesItsOwnKey + ) + } + + @Test("A table with only a name is planned on an engine that supplies its own key") + func nameOnlyTableOnAnEngineWithItsOwnKey() throws { + let result = namedOnlyPlan(suppliesItsOwnKey: true) + #expect(result.issues.isEmpty) + let definition = try #require(result.definition) + #expect(definition.tableName == "events") + #expect(definition.columns.isEmpty) + } + + @Test("A table with only a name still needs a column everywhere else") + func nameOnlyTableNeedsAColumnOtherwise() { + let result = namedOnlyPlan(suppliesItsOwnKey: false) + #expect(result.definition == nil) + #expect(!result.issues.isEmpty) + } + // MARK: - The reported bug @Test("a foreign key with no constraint name is emitted") diff --git a/TableProTests/Plugins/MongoDBCollectionDDLTests.swift b/TableProTests/Plugins/MongoDBCollectionDDLTests.swift new file mode 100644 index 0000000000..c5bf86fc1a --- /dev/null +++ b/TableProTests/Plugins/MongoDBCollectionDDLTests.swift @@ -0,0 +1,279 @@ +// +// MongoDBCollectionDDLTests.swift +// TableProTests +// + +import Foundation +import TableProPluginKit +import Testing + +struct MongoDBCollectionDDLTests { + private func column( + _ name: String, + _ type: String, + nullable: Bool = true, + primaryKey: Bool = false + ) -> PluginColumnDefinition { + PluginColumnDefinition( + name: name, + dataType: type, + isNullable: nullable, + defaultValue: nil, + isPrimaryKey: primaryKey, + autoIncrement: false, + comment: nil, + unsigned: false, + onUpdate: nil, + charset: nil, + collation: nil + ) + } + + private func definition(_ name: String, _ columns: [PluginColumnDefinition]) -> PluginCreateTableDefinition { + PluginCreateTableDefinition(tableName: name, columns: columns) + } + + private func index( + _ name: String, + _ columns: [String], + unique: Bool = false, + type: String? = nil, + whereClause: String? = nil + ) -> PluginIndexDefinition { + PluginIndexDefinition(name: name, columns: columns, isUnique: unique, indexType: type, whereClause: whereClause) + } + + /// The options the statement passes to `createCollection`, read back as JSON. + private func options(of statement: String) throws -> [String: Any] { + let start = try #require(statement.firstIndex(of: "{")) + let end = try #require(statement.lastIndex(of: "}")) + let data = try #require(String(statement[start ... end]).data(using: .utf8)) + return try #require(try JSONSerialization.jsonObject(with: data) as? [String: Any]) + } + + private func jsonSchema(of statement: String) throws -> [String: Any] { + let validator = try #require(try options(of: statement)["validator"] as? [String: Any]) + return try #require(validator["$jsonSchema"] as? [String: Any]) + } + + @Test("The reported draft becomes a collection whose validator lists every field in order") + func reportedDraftBecomesAValidator() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement(for: definition("articles", [ + column("_id", "objectId", nullable: false, primaryKey: true), + column("title", "string", nullable: false), + column("seoDescription", "string"), + column("slug", "string"), + column("content", "string"), + column("tags", "array"), + column("lang", "string"), + column("date", "date"), + column("schemaVersion", "int") + ])) + + #expect(statement.hasPrefix("db.createCollection(\"articles\", {")) + let schema = try jsonSchema(of: statement) + #expect(schema["bsonType"] as? String == "object") + #expect(schema["required"] as? [String] == ["title"]) + let properties = try #require(schema["properties"] as? [String: Any]) + #expect((properties["_id"] as? [String: Any])?["bsonType"] as? String == "objectId") + #expect((properties["title"] as? [String: Any])?["bsonType"] as? String == "string") + #expect((properties["tags"] as? [String: Any])?["bsonType"] as? [String] == ["array", "null"]) + #expect((properties["schemaVersion"] as? [String: Any])?["bsonType"] as? [String] == ["int", "null"]) + + let declared = MongoDBCollectionSchema.parse(jsonSchema: statement.jsonSchemaText) + #expect(declared.fields.map { $0.name } == [ + "_id", "title", "seoDescription", "slug", "content", "tags", "lang", "date", "schemaVersion" + ]) + } + + @Test("Leaving the server's validation level and action out keeps its strict, error defaults") + func validationDefaultsAreLeftToTheServer() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement( + for: definition("articles", [column("title", "string")]) + ) + let options = try options(of: statement) + #expect(Set(options.keys) == ["validator"]) + } + + @Test("A collection with no fields beyond _id stays schemaless") + func onlyIdMeansNoValidator() { + #expect(MongoDBCollectionDDL.createCollectionStatement(for: definition("events", [])) == "db.createCollection(\"events\")") + #expect( + MongoDBCollectionDDL.createCollectionStatement( + for: definition("events", [column("_id", "objectId", nullable: false, primaryKey: true)]) + ) == "db.createCollection(\"events\")" + ) + } + + @Test("No required list is written when every field is nullable, because an empty one is refused") + func noRequiredWhenEverythingIsNullable() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement( + for: definition("notes", [column("body", "string")]) + ) + #expect(try jsonSchema(of: statement)["required"] == nil) + } + + @Test("A type is matched to its alias whatever its case, since the server's aliases are case-sensitive") + func typeAliasesAreNormalised() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement(for: definition("t", [ + column("ref", "ObjectId", nullable: false), + column("name", "STRING", nullable: false), + column("flag", "Bool", nullable: false) + ])) + let properties = try #require(try jsonSchema(of: statement)["properties"] as? [String: Any]) + #expect((properties["ref"] as? [String: Any])?["bsonType"] as? String == "objectId") + #expect((properties["name"] as? [String: Any])?["bsonType"] as? String == "string") + #expect((properties["flag"] as? [String: Any])?["bsonType"] as? String == "bool") + } + + @Test("A nullable null field is not written as a duplicate type") + func nullTypeIsNotDuplicated() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement( + for: definition("t", [column("nothing", "null")]) + ) + let properties = try #require(try jsonSchema(of: statement)["properties"] as? [String: Any]) + #expect((properties["nothing"] as? [String: Any])?["bsonType"] as? String == "null") + } + + @Test("Names with quotes, backslashes and non-ASCII text stay inside their string literals") + func namesAreEscaped() throws { + let statement = MongoDBCollectionDDL.createCollectionStatement(for: definition("a\"b\\c", [ + column("título \"x\"", "string", nullable: false) + ])) + #expect(statement.hasPrefix("db.createCollection(\"a\\\"b\\\\c\", {")) + let schema = try jsonSchema(of: statement) + #expect(schema["required"] as? [String] == ["título \"x\""]) + } + + @Test("A type MongoDB lacks still produces a statement, so the server explains the refusal") + func unknownTypeStillProducesAStatement() { + let statement = MongoDBCollectionDDL.createCollectionStatement( + for: definition("t", [column("n", "VARCHAR(255)", nullable: false)]) + ) + #expect(statement.contains("\"bsonType\": \"VARCHAR(255)\"")) + } + + // MARK: - Refusals + + @Test("A primary key on a field other than _id is refused and names the field") + func primaryKeyOnIdFieldIsRefused() throws { + let reason = try #require(MongoDBCollectionDDL.refusal(for: .addColumn( + column("id", "objectId", nullable: false, primaryKey: true) + ))) + #expect(reason.contains("id")) + #expect(reason.contains("_id")) + } + + @Test("_id as the primary key is accepted") + func idAsPrimaryKeyIsAccepted() { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn( + column("_id", "objectId", nullable: false, primaryKey: true) + )) == nil) + } + + @Test("_id typed as anything but objectId is refused, because the grid lets the server generate it") + func idWithAnotherTypeIsRefused() { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column("_id", "string", nullable: false))) != nil) + } + + @Test("A type that is not a BSON alias is refused and names the field and the type") + func unknownTypeIsRefused() throws { + let reason = try #require(MongoDBCollectionDDL.refusal(for: .addColumn(column("title", "VARCHAR")))) + #expect(reason.contains("title")) + #expect(reason.contains("VARCHAR")) + } + + @Test("Every listed type is accepted", arguments: MongoDBBsonType.aliases) + func listedTypesAreAccepted(alias: String) { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column("f", alias))) == nil) + } + + @Test("Field names MongoDB cannot address are refused", arguments: ["$price", "a.b"]) + func unaddressableNamesAreRefused(name: String) { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column(name, "string"))) != nil) + } + + // MARK: - Indexes + + @Test("An index keeps its fields in the order they were given") + func indexKeepsKeyOrder() { + let statement = MongoDBCollectionDDL.createIndexStatement( + collection: "articles", index: index("lang_date", ["lang", "date", "slug"], unique: true) + ) + #expect(statement == "db.articles.createIndex({\"lang\": 1, \"date\": 1, \"slug\": 1}, {\"name\": \"lang_date\", \"unique\": true})") + } + + @Test("An index with no name lets the server name it") + func unnamedIndexOmitsItsName() { + #expect(MongoDBCollectionDDL.createIndexStatement(collection: "articles", index: index("", ["slug"])) + == "db.articles.createIndex({\"slug\": 1})") + } + + @Test("A collection the shell cannot name as a property is reached through getCollection") + func indexOnAnAwkwardCollectionName() throws { + let statement = try #require(MongoDBCollectionDDL.createIndexStatement( + collection: "my.collection", index: index("", ["slug"]) + )) + #expect(statement.hasPrefix("db.getCollection(\"my.collection\").createIndex(")) + } + + @Test("The editor's index types map to MongoDB's key kinds") + func indexTypesMap() { + let cases: [(type: String, key: String)] = [ + ("BTREE", "1"), ("HASH", "\"hashed\""), ("FULLTEXT", "\"text\""), ("SPATIAL", "\"2dsphere\"") + ] + for entry in cases { + let statement = MongoDBCollectionDDL.createIndexStatement( + collection: "c", index: index("", ["f"], type: entry.type) + ) + #expect(statement == "db.c.createIndex({\"f\": \(entry.key)})", "\(entry.type)") + } + } + + @Test("An index type MongoDB has no equivalent for is refused and produces no statement") + func unsupportedIndexTypeIsRefused() { + let gin = index("g", ["f"], type: "GIN") + #expect(MongoDBCollectionDDL.createIndexStatement(collection: "c", index: gin) == nil) + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(gin)) != nil) + #expect(MongoDBCollectionDDL.unsupportedIndexTypes.contains("GIN")) + } + + @Test("A hashed index can be neither unique nor compound") + func hashedIndexLimits() { + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("h", ["f"], unique: true, type: "HASH"))) != nil) + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("h", ["f", "g"], type: "HASH"))) != nil) + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("h", ["f"], type: "HASH"))) == nil) + } + + @Test("An index on _id alone is refused, because MongoDB already has one") + func indexOnIdIsRefused() { + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("i", ["_id"]))) != nil) + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("i", ["_id", "lang"]))) == nil) + } + + @Test("A partial index written in SQL is refused") + func whereClauseIsRefused() { + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("p", ["f"], whereClause: "f > 0"))) != nil) + } +} + +private extension String { + /// The `$jsonSchema` object inside a `createCollection` statement, as text. + var jsonSchemaText: String { + guard let marker = range(of: "\"$jsonSchema\": ") else { return "{}" } + let tail = self[marker.upperBound...] + var depth = 0 + var end = tail.startIndex + for index in tail.indices { + if tail[index] == "{" { depth += 1 } + if tail[index] == "}" { + depth -= 1 + if depth == 0 { + end = index + break + } + } + } + return String(tail[tail.startIndex ... end]) + } +} diff --git a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift new file mode 100644 index 0000000000..2b9b8836ae --- /dev/null +++ b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift @@ -0,0 +1,118 @@ +// +// MongoDBCollectionSchemaTests.swift +// TableProTests +// + +import Foundation +import TableProPluginKit +import Testing + +struct MongoDBCollectionSchemaTests { + /// A `listCollections` reply as `bson_as_canonical_extended_json` writes it, fields deliberately + /// out of alphabetical order so a dictionary round trip would show. + private func reply(options: String) -> String { + """ + { "cursor" : { "id" : { "$numberLong" : "0" }, "ns" : "blog.$cmd.listCollections", "firstBatch" : [ \ + { "name" : "articles", "type" : "collection", "options" : \(options), \ + "info" : { "readOnly" : false }, "idIndex" : { "v" : { "$numberInt" : "2" }, "key" : { "_id" : { "$numberInt" : "1" } }, "name" : "_id_" } } ] }, \ + "ok" : { "$numberDouble" : "1.0" } } + """ + } + + private let articlesOptions = """ + { "validator" : { "$jsonSchema" : { "bsonType" : "object", "required" : [ "title", "status" ], \ + "properties" : { "title" : { "bsonType" : "string" }, "tags" : { "bsonType" : [ "array", "null" ] }, \ + "date" : { "bsonType" : [ "date", "null" ] }, "status" : { "enum" : [ "draft", "live" ] }, \ + "views" : { "bsonType" : "long" }, "attachment" : { "bsonType" : "binData" }, \ + "score" : { "type" : "number" } } } }, "validationLevel" : "strict", "validationAction" : "error" } + """ + + @Test("Declared fields come back in the order the validator lists them") + func fieldsKeepTheirOrder() { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(schema.fields.map { $0.name } == ["title", "tags", "date", "status", "views", "attachment", "score"]) + } + + @Test("A field is required only when the validator's required list names it") + func requiredComesFromTheRequiredList() throws { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(try #require(schema.field(named: "title")).isRequired) + #expect(try #require(schema.field(named: "status")).isRequired) + #expect(try #require(schema.field(named: "tags")).isRequired == false) + } + + @Test("A nullable union still types the value, and an open or binary type does not") + func valueKinds() { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(schema.valueKinds["title"] == .string) + #expect(schema.valueKinds["tags"] == .array) + #expect(schema.valueKinds["date"] == .date) + #expect(schema.valueKinds["views"] == .int64) + #expect(schema.valueKinds["status"] == nil) + #expect(schema.valueKinds["attachment"] == nil) + #expect(schema.valueKinds["score"] == nil) + } + + @Test("A string enum is kept as the field's allowed values") + func enumsAreKept() { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(schema.allowedValues["status"] == ["draft", "live"]) + #expect(schema.allowedValues["title"] == nil) + } + + @Test("A declared field is shown with the type name a sampled one of its kind would have") + func declaredTypeNamesMatchSampledOnes() throws { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + let date = try #require(schema.field(named: "date")) + let attachment = try #require(schema.field(named: "attachment")) + let score = try #require(schema.field(named: "score")) + #expect(date.columnTypeName(representation: .unspecified) + == BsonDocumentFlattener.typeName(for: .date, representation: .unspecified)) + #expect(attachment.columnTypeName(representation: .unspecified) + == BsonDocumentFlattener.typeName(for: .binary(subtype: 0), representation: .unspecified)) + #expect(score.columnTypeName(representation: .unspecified) == "number") + } + + @Test("A collection with no validator, or a validator without $jsonSchema, declares nothing") + func noSchemaDeclaresNothing() { + #expect(MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: "{ }")).isEmpty) + #expect(MongoDBCollectionSchema.parse( + listCollectionsReply: reply(options: "{ \"validator\" : { \"age\" : { \"$gte\" : { \"$numberInt\" : \"0\" } } } }") + ).isEmpty) + #expect(MongoDBCollectionSchema.parse(listCollectionsReply: "{ \"cursor\" : { \"firstBatch\" : [ ] } }").isEmpty) + #expect(MongoDBCollectionSchema.parse(listCollectionsReply: "not json").isEmpty) + } + + @Test("A field whose name carries an escaped quote is still read") + func escapedFieldNamesSurvive() { + let schema = MongoDBCollectionSchema.parse(jsonSchema: """ + { "properties" : { "a\\"b" : { "bsonType" : "string" }, "plain" : { "bsonType" : "int" } } } + """) + #expect(Set(schema.fields.map { $0.name }) == ["a\"b", "plain"]) + } + + @Test("The command asks for one collection by name, escaped") + func listCollectionsCommandIsEscaped() throws { + let command = MongoDBCollectionSchema.listCollectionsCommand(for: "a\"b") + let data = try #require(command.data(using: .utf8)) + let parsed = try #require(try JSONSerialization.jsonObject(with: data) as? [String: Any]) + #expect((parsed["filter"] as? [String: Any])?["name"] as? String == "a\"b") + } + + // MARK: - Shape + + @Test("An empty collection presents _id and then its declared fields") + func emptyCollectionColumns() { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(MongoDBCollectionShape.emptyCollectionColumns(declaring: schema) + == ["_id", "title", "tags", "date", "status", "views", "attachment", "score"]) + #expect(MongoDBCollectionShape.emptyCollectionColumns(declaring: .empty) == ["_id"]) + } + + @Test("Declared fields no sampled document holds follow the sampled columns") + func unseenDeclaredColumns() { + let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) + #expect(MongoDBCollectionShape.declaredColumnsMissing(from: ["_id", "title", "extra"], schema: schema) + == ["tags", "date", "status", "views", "attachment", "score"]) + } +} diff --git a/TableProTests/Plugins/MongoDBQueryBuilderTests.swift b/TableProTests/Plugins/MongoDBQueryBuilderTests.swift index a5ef6a86db..17f62f0871 100644 --- a/TableProTests/Plugins/MongoDBQueryBuilderTests.swift +++ b/TableProTests/Plugins/MongoDBQueryBuilderTests.swift @@ -863,6 +863,27 @@ struct MongoDBQueryBuilderTests { } } + /// U+0D4E is a Unicode Prepend letter: it joins the next scalar into one `Character`, so a + /// `(` after it answered `isLetter` and the name went into the statement bare, as code. + @Test("A collection name is spelled bare only when it is a plain ASCII identifier") + func accessorSpellsOnlyAsciiIdentifiersBare() { + #expect(MongoCollectionAccessor.expression(for: "orders") == "db.orders") + #expect(MongoCollectionAccessor.expression(for: "order_2") == "db.order_2") + for name in ["a\u{0D4E}(\u{0D4E})", "tên", "2025", "a b", "a;b"] { + #expect(MongoCollectionAccessor.expression(for: name).hasPrefix("db.getCollection(\""), "\(name)") + } + } + + @Test("Escaping works scalar by scalar, so a quote joined to a Prepend letter is still escaped") + func escapingSeesQuotesInsideGraphemeClusters() throws { + let hostile = "x\u{0600}\"}); db.victim.drop(); ({\"" + let escaped = MongoDBQueryBuilder.escapeJsonString(hostile) + let data = try #require("\"\(escaped)\"".data(using: .utf8)) + let decoded = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) as? String + #expect(decoded == hostile) + #expect(MongoDBQueryBuilder.escapeJsonString("a\r\nb") == "a\\r\\nb") + } + // MARK: - Raw filter normalization private static let normalizer = MongoDBRawFilterNormalizer() diff --git a/TableProTests/Plugins/MongoDBStatementGeneratorTests.swift b/TableProTests/Plugins/MongoDBStatementGeneratorTests.swift index 456030bb4e..b1149aa0b4 100644 --- a/TableProTests/Plugins/MongoDBStatementGeneratorTests.swift +++ b/TableProTests/Plugins/MongoDBStatementGeneratorTests.swift @@ -254,7 +254,9 @@ struct MongoDBStatementGeneratorTests { #expect(document?["leadingZero"] as? String == "01") } - @Test("Insert quotes integers that overflow Int64 but keeps in-range integers numeric") + /// The maximum Int64 is past 2^53, where a bare JavaScript literal rounds: JavaScriptCore reads + /// `9223372036854775807` as `9223372036854776000`. It has to cross as `$numberLong`. + @Test("Insert quotes integers that overflow Int64 and writes the largest ones as $numberLong") func insertQuotesInt64Overflow() { let gen = MongoDBStatementGenerator( collectionName: "users", @@ -281,7 +283,7 @@ struct MongoDBStatementGeneratorTests { let document = firstArgumentObject(in: results[0].statement) #expect(document?["overflow"] as? String == "12345678901234567890") - #expect(document?["maxInt64"] as? Int64 == 9_223_372_036_854_775_807) + #expect((document?["maxInt64"] as? [String: Any])?["$numberLong"] as? String == "9223372036854775807") } @Test("Insert not in insertedRowIndices is skipped") diff --git a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift index 2f0e2d2025..f0a8a39066 100644 --- a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift +++ b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift @@ -11,12 +11,14 @@ struct MongoDBWriteBackTypeTests { private func update( column: String, to newValue: PluginCellValue, - kinds: [String: BsonValueKind] + kinds: [String: BsonValueKind], + declared: [String: BsonValueKind] = [:] ) -> String? { let gen = MongoDBStatementGenerator( collectionName: "products", columns: ["_id", column], - columnKinds: kinds + columnKinds: kinds, + declaredKinds: declared ) let change = PluginRowChange( rowIndex: 0, @@ -81,4 +83,174 @@ struct MongoDBWriteBackTypeTests { let stmt = try #require(update(column: "note", to: .text("{draft"), kinds: [:])) #expect(stmt.contains(#""note": "{draft""#)) } + + // MARK: - Dates and ObjectIds + + @Test("A date column is written as a BSON date, not the text the grid shows") + func dateColumnWritesADate() throws { + let stmt = try #require(update(column: "when", to: .text("2024-01-02T03:04:05Z"), kinds: ["when": .date])) + #expect(stmt.contains(#""when": {"$date": {"$numberLong": "1704164645000"}}"#)) + } + + @Test("A date the picker wrote is read in the local zone, which is the zone the picker shows") + func pickedDateIsLocalWallClock() throws { + let stmt = try #require(update(column: "when", to: .text("2024-01-02 03:04:05"), kinds: ["when": .date])) + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = .current + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + let local = try #require(formatter.date(from: "2024-01-02 03:04:05")) + let millis = Int64((local.timeIntervalSince1970 * 1_000).rounded()) + #expect(stmt.contains("{\"$date\": {\"$numberLong\": \"\(millis)\"}}")) + } + + @Test("A date-only value is a date too") + func dateOnlyIsADate() throws { + let stmt = try #require(update(column: "day", to: .text("2024-01-02"), kinds: ["day": .date])) + #expect(stmt.contains(#""day": {"$date": "#)) + } + + @Test("A bare number typed into a date column stays a number rather than becoming 1970") + func numberInADateColumnStaysANumber() throws { + let stmt = try #require(update(column: "day", to: .text("2024"), kinds: ["day": .date])) + #expect(stmt.contains(#""day": 2024"#)) + } + + @Test("A field holding ObjectIds keeps holding ObjectIds") + func objectIdColumnWritesAnObjectId() throws { + let stmt = try #require(update( + column: "authorId", to: .text("65a1b2c3d4e5f60718293a4b"), kinds: ["authorId": .objectId] + )) + #expect(stmt.contains(#""authorId": {"$oid": "65a1b2c3d4e5f60718293a4b"}"#)) + } + + // MARK: - Declared kinds + + @Test("A field the validator declares a string is written as one, whatever the text looks like") + func declaredStringIsAlwaysAString() throws { + for text in ["123", "true", "null", "[1, 2]"] { + let stmt = try #require(update(column: "code", to: .text(text), kinds: [:], declared: ["code": .string])) + let escaped = text.replacingOccurrences(of: "\"", with: "\\\"") + #expect(stmt.contains("\"code\": \"\(escaped)\""), "\(text)") + } + } + + @Test("A sampled string column still reads typed JSON, because sampling is a majority vote") + func sampledStringKeepsItsSpelling() throws { + let stmt = try #require(update(column: "meta", to: .text(#"{"a":1}"#), kinds: ["meta": .string])) + #expect(stmt.contains(#""meta": {"a":1}"#)) + } + + @Test("A declared type outranks what the sampled documents held") + func declaredKindWins() throws { + let stmt = try #require(update( + column: "rate", to: .text("3"), kinds: ["rate": .int32], declared: ["rate": .double] + )) + #expect(stmt.contains(#"{"$numberDouble": "3"}"#)) + } + + @Test("An int field takes a bare number that fits in 32 bits") + func int32ColumnWritesABareNumber() throws { + let stmt = try #require(update(column: "n", to: .text("42"), kinds: [:], declared: ["n": .int32])) + #expect(stmt.contains(#""n": 42"#)) + } + + // MARK: - Numbers past a double + + /// Measured with JavaScriptCore: `JSON.stringify({a: 9007199254740993})` gives + /// `9007199254740992`, so the bare literal reached the server one off. + @Test("An integer past 2^53 is written as a 64-bit integer so JavaScript cannot round it") + func hugeIntegerIsNotRounded() throws { + let stmt = try #require(update(column: "big", to: .text("9007199254740993"), kinds: [:])) + #expect(stmt.contains(#""big": {"$numberLong": "9007199254740993"}"#)) + } + + @Test("An integer JavaScript holds exactly stays bare") + func exactIntegerStaysBare() throws { + let stmt = try #require(update(column: "n", to: .text("9007199254740992"), kinds: [:])) + #expect(stmt.contains(#""n": 9007199254740992"#)) + } + + // MARK: - Statements are code + + /// The statement is JavaScript the shell evaluates, so text that only looks like an array + /// used to be pasted in as code. + @Test("Stored text shaped like an array but not JSON is written as a string, never run") + func arrayShapedCodeIsAString() throws { + let hostile = #"[db.getCollection("audit").drop()]"# + let stmt = try #require(update(column: "note", to: .text(hostile), kinds: [:])) + #expect(stmt.contains("\"note\": \(MongoScriptJson.jsonString(hostile))")) + #expect(!stmt.contains(#""note": [db"#)) + } + + @Test("Stored text shaped like a document but not JSON is written as a string, never run") + func documentShapedCodeIsAString() throws { + let hostile = #"{a: db.dropDatabase()}"# + let stmt = try #require(update(column: "note", to: .text(hostile), kinds: [:])) + #expect(stmt.contains("\"note\": \(MongoScriptJson.jsonString(hostile))")) + } + + /// U+0600 is a Unicode Prepend character, so it and the quote after it are one `Character`, which + /// matched no escape case and went into the statement raw, closing the string literal early. + @Test("A quote joined to a Prepend character is still escaped, so the value cannot close its string") + func quoteInsideAGraphemeClusterIsEscaped() throws { + let hostile = "x\u{0600}\"}}); db.getCollection(\"victim\").drop(); db.x.find({\"a\": {\"b\": \"" + let stmt = try #require(update(column: "note", to: .text(hostile), kinds: [:], declared: ["note": .string])) + let start = try #require(stmt.range(of: "\"$set\": ")).upperBound + let setDocument = String(stmt[start...].dropLast(2)) + let data = try #require(setDocument.data(using: .utf8)) + let parsed = try #require(try JSONSerialization.jsonObject(with: data) as? [String: Any]) + #expect(parsed["note"] as? String == hostile) + } + + @Test("Restoring a deleted row writes a hostile stored string back as a string") + func restoreDoesNotRunStoredText() throws { + let gen = MongoDBStatementGenerator(collectionName: "notes", columns: ["_id", "body"]) + let hostile = #"[db.getCollection("audit").drop()]"# + let statement = try #require(gen.generateRestore(rows: [["507f1f77bcf86cd799439011", .text(hostile)]])?.first) + #expect(statement.statement.contains("\"body\": \(MongoScriptJson.jsonString(hostile))")) + } + + // MARK: - _id + + private func updateFilter(id: String, idKind: BsonValueKind?) -> String? { + let gen = MongoDBStatementGenerator( + collectionName: "codes", + columns: ["_id", "label"], + columnKinds: idKind.map { ["_id": $0] } ?? [:] + ) + let change = PluginRowChange( + rowIndex: 0, + type: .update, + cellChanges: [(columnIndex: 1, columnName: "label", oldValue: .text("a"), newValue: .text("b"))], + originalRow: [.text(id), .text("a")] + ) + return gen.generateStatements( + from: [change], insertedRowData: [:], deletedRowIndices: [], insertedRowIndices: [] + ).first?.statement + } + + @Test("A string _id that looks like a number is matched as a string") + func stringIdIsMatchedAsAString() throws { + let stmt = try #require(updateFilter(id: "1001", idKind: .string)) + #expect(stmt.contains(#"{"_id": "1001"}"#)) + } + + @Test("A string _id that looks like an ObjectId is matched as a string") + func hexStringIdIsMatchedAsAString() throws { + let stmt = try #require(updateFilter(id: "65a1b2c3d4e5f60718293a4b", idKind: .string)) + #expect(stmt.contains(#"{"_id": "65a1b2c3d4e5f60718293a4b"}"#)) + } + + @Test("A 64-bit _id is matched as one") + func longIdIsMatchedAsALong() throws { + let stmt = try #require(updateFilter(id: "42", idKind: .int64)) + #expect(stmt.contains(#"{"_id": {"$numberLong": "42"}}"#)) + } + + @Test("An _id of unknown kind is still read from its spelling") + func unknownIdFallsBackToItsSpelling() throws { + let stmt = try #require(updateFilter(id: "65a1b2c3d4e5f60718293a4b", idKind: nil)) + #expect(stmt.contains(#"{"_id": {"$oid": "65a1b2c3d4e5f60718293a4b"}}"#)) + } } diff --git a/TableProTests/Views/Sidebar/DatabaseTreeMenuSpecTests.swift b/TableProTests/Views/Sidebar/DatabaseTreeMenuSpecTests.swift index d6ab12ad1a..c3d0c8305a 100644 --- a/TableProTests/Views/Sidebar/DatabaseTreeMenuSpecTests.swift +++ b/TableProTests/Views/Sidebar/DatabaseTreeMenuSpecTests.swift @@ -50,6 +50,7 @@ struct DatabaseTreeMenuSpecTests { canCopyObjects: Bool = true, canDuplicateDatabase: Bool = true, canCreateType: Bool = false, + canCreateTable: Bool = true, supportsCreateSchema: Bool = false, supportsSchemaOwner: Bool = false, supportsSchemaPrivileges: Bool = false, @@ -116,6 +117,7 @@ struct DatabaseTreeMenuSpecTests { canCopyObjects: canCopyObjects, canDuplicateDatabase: canDuplicateDatabase, canCreateType: canCreateType, + canCreateTable: canCreateTable, objectToolSupport: objectToolSupport ) } @@ -184,6 +186,16 @@ struct DatabaseTreeMenuSpecTests { #expect(issued.contains(.createView)) } + /// MongoDB before its plugin could create a collection, and Redis, Kafka and every other engine + /// without a create hook, opened the grid and refused only once it was filled in. + @Test("An engine that cannot create a table is not offered New Table") + func emptyAreaHidesNewTableWithoutCreateSupport() { + let issued = commands(DatabaseTreeMenuSpec.sections(for: context(clicked: nil, canCreateTable: false))) + + #expect(!issued.contains(.createTable)) + #expect(issued.contains(.createView)) + } + @Test("Read-only hides creation from the empty area too") func readOnlyEmptyAreaHidesCreation() { let issued = commands(DatabaseTreeMenuSpec.sections(for: context(clicked: nil, isReadOnly: true))) diff --git a/docs/databases/mongodb.mdx b/docs/databases/mongodb.mdx index 1ced6ed10e..6f674496de 100644 --- a/docs/databases/mongodb.mdx +++ b/docs/databases/mongodb.mdx @@ -73,11 +73,13 @@ Over an SSH tunnel only the first host is used and the rest of the list is dropp ## Browsing collections -Click a collection to page through its documents. Column layout is inferred by sampling documents, not read from a validator, so a field missing from the sample gets no column. ObjectIds render as strings, arrays and nested objects as formatted JSON. +Click a collection to page through its documents. The columns are the fields in the documents on screen, then any field the collection's `$jsonSchema` validator declares that none of them hold, so an empty collection still shows the fields it was created with. ObjectIds render as strings, arrays and nested objects as formatted JSON. + +An edited cell keeps its field's type. A date stays a date, whether typed as `2024-01-02T03:04:05Z`, as `2024-01-02`, or picked in the date picker, which writes your local time. An ObjectId stays an ObjectId, an integer past 2^53 is sent as a 64-bit integer, and a field the validator declares as `string` is written as text even when it reads `123` or `true`. Where the grid has no type to go on, `123` is a number and `{"a": 1}` a document; text that only looks like JSON is written as a string. The filter bar's column picker lists paths inside nested objects and arrays of objects, so `customer.country` and `items.sku` filter directly; a row on an array field chooses **any element** or **same element**, which makes one array entry satisfy every row set to it. See [Filtering](/features/filtering#nested-fields). A field name containing a literal dot is left out of the picker, since MongoDB reads a dot as a path separator; reach it with `$getField` inside `$expr`. -The Structure tab lists a collection's indexes; create and drop them from a query tab with `db.users.createIndex({"email": 1})` and `db.users.dropIndex("email_1")`. **New Database** asks for a database name and a first collection, both required. **New View** opens a query tab holding a `db.createView("view_name", "source_collection", [pipeline])` template, and editing a view pre-fills `db.runCommand({"collMod": …})`. +The Structure tab lists a collection's indexes; drop one from a query tab with `db.users.dropIndex("email_1")`. **New Database** asks for a database name and a first collection, both required. **New View** opens a query tab holding a `db.createView("view_name", "source_collection", [pipeline])` template, and editing a view pre-fills `db.runCommand({"collMod": …})`. ### Binary UUIDs @@ -85,6 +87,26 @@ A binary subtype 4 field renders as `UUID("8cd003eb-4a25-4324-9332-88fce2da0d1a" Once set, the value renders as `LegacyJavaUUID("…")` and reads that way everywhere, filters and MQL export included. Nothing stored is rewritten, `uuidRepresentation=javaLegacy` in a pasted URL sets the same option, and a change takes effect on the next connect. +## Creating a collection + +Choose **Database > New Table…**. Each row of the grid is a field, and its type is one of the BSON types in the list: `objectId`, `string`, `int`, `long`, `double`, `decimal`, `date`, `bool`, `array`, `object` and the rest. **Create Table** turns the rows into a `$jsonSchema` validator and runs `db.createCollection("articles", {"validator": …})`, which **SQL Preview** shows first. + + + SQL Preview tab of Create Table showing db.createCollection for articles with a $jsonSchema listing _id, title, tags, date and schemaVersion + SQL Preview tab of Create Table showing db.createCollection for articles with a $jsonSchema listing _id, title, tags, date and schemaVersion + + +| Row | Becomes | +|-----|---------| +| **Nullable** NO | The field is in `required`: a document without it, or with `null`, is refused | +| **Nullable** YES | The field is optional and may hold `null` | +| `_id` | Always the key, typed `objectId`. Leave it out and MongoDB still adds one | +| No rows, only a name | A collection with no validator | + +MongoDB enforces the validator with its defaults, `strict` and `error`, so an insert or edit of the wrong type fails with `Document failed validation`. **Primary Key** on any field other than `_id` is refused, and so are field names that start with `$` or contain a dot. + +Each row on the **Indexes** tab runs as a `createIndex` after the collection is made, with its fields in the order listed. **BTREE** is an ascending key, **HASH** is `hashed`, **FULLTEXT** is `text`, and **SPATIAL** is `2dsphere`. A hashed index takes one field and cannot be unique. + ## Writing queries Queries run through JavaScriptCore, so a statement is JavaScript and the whole language is @@ -179,6 +201,7 @@ New connections default to **Disabled**, and the driver has no TLS fallback: **P - A row with no `_id` cannot be updated or deleted. The save is skipped rather than matched on the remaining fields. Keep `_id` in the projection so every row carries one. - `_id` is read-only in the grid, and left out of an insert entirely so the server generates it. To choose your own, insert with `db.collection.insertOne({…})`. - Transactions are not exposed. Statements always run standalone, on any topology. +- **New Table…** writes the validator with the server's own level and action. To log bad documents instead of refusing them, run `db.runCommand({collMod: "articles", validationAction: "warn"})` after creating the collection. - Nested paths filter but do not sort. Sorting works on the grid's own columns. - **same element** covers a field one array deep. A path through an array inside another array needs nested `$elemMatch`, so those filter with dot notation only. - GridFS buckets are not browsable, and change streams are unsupported. diff --git a/docs/features/table-operations.mdx b/docs/features/table-operations.mdx index a2dadaf373..c246853d2a 100644 --- a/docs/features/table-operations.mdx +++ b/docs/features/table-operations.mdx @@ -7,7 +7,7 @@ Dropping a table cannot be undone, and **Cascade** in the confirmation dialog wi ## Create a table or view -**Database > New Table…** opens the visual structure editor (see [Table Structure](/features/table-structure)); on [DynamoDB](/databases/dynamodb#creating-and-changing-tables) it opens a form for the keys, capacity and indexes instead. **Database > New View…** opens a query tab holding a `CREATE VIEW` template for the engine. Both are also on the sidebar's right-click menu over empty space, where [safe mode](/features/safe-mode) removes them rather than dimming them. +**Database > New Table…** opens the visual structure editor (see [Table Structure](/features/table-structure)); on [DynamoDB](/databases/dynamodb#creating-and-changing-tables) it opens a form for the keys, capacity and indexes instead. **Database > New View…** opens a query tab holding a `CREATE VIEW` template for the engine. Both are also on the sidebar's right-click menu over empty space, where [safe mode](/features/safe-mode) removes them rather than dimming them. On an engine with no way to create a table, such as Redis or Kafka, **New Table…** is dimmed in the menu bar and absent from the sidebar. ## Drop and truncate diff --git a/docs/features/table-structure.mdx b/docs/features/table-structure.mdx index 563d328d4a..d89ae349ca 100644 --- a/docs/features/table-structure.mdx +++ b/docs/features/table-structure.mdx @@ -238,7 +238,7 @@ Lists partitions and parts from `system.parts`. **Optimize** merges parts; **Dro ## Creating a new table -Choose **Database > New Table…**, or right-click empty space in the sidebar and choose **New Table…**. The tab holds a **Table Name** field (with Engine, Charset, and Collation pickers on MySQL and MariaDB), the same three editing grids, and a **SQL Preview** tab carrying the live `CREATE TABLE`. **Create Table** (`Cmd+Enter` or `Cmd+S`) executes it and opens the new table. +Choose **Database > New Table…**, or right-click empty space in the sidebar and choose **New Table…**. The tab holds a **Table Name** field (with Engine, Charset, and Collation pickers on MySQL and MariaDB), the same three editing grids, and a **SQL Preview** tab carrying the live statements it will run. **Create Table** (`Cmd+Enter` or `Cmd+S`) executes it and opens the new table. On the **Foreign Keys** grid, **Columns**, **Ref Table**, and **Ref Columns** open menus of the new table's own columns, the database's tables, and the referenced table's columns. Each menu ends in **Custom…** for a name the list does not carry, and picking a second entry in a comma-separated cell appends to it. @@ -249,12 +249,12 @@ A row that is started and unfinished is never dropped from the generated SQL. ** Indexes run as their own `CREATE INDEX` statements after the table, in one transaction where the engine has transactional DDL. -Visual table creation is supported for MySQL, MariaDB, PostgreSQL, PGlite, SQLite, SQL Server, ClickHouse, DuckDB, Oracle, Snowflake, libSQL, Cloudflare D1, Trino, Teradata, and Dameng. +Visual table creation is supported for MySQL, MariaDB, PostgreSQL, PGlite, SQLite, SQL Server, ClickHouse, DuckDB, Oracle, Snowflake, libSQL, Cloudflare D1, Trino, Teradata, Dameng, and [MongoDB](/databases/mongodb#creating-a-collection). DynamoDB uses a form instead. On every other engine **New Table…** is dimmed. ## MongoDB collections -MongoDB structure is read-only, and inferred from the collection's first 200 documents: top-level field names are unioned across the sample, and each field takes its most common BSON type. `_id` comes first, marked as the primary key. The DDL tab shows indexes as `createIndex()` commands for `mongosh`, plus the validator and capped-collection options where present. +MongoDB structure is read-only, and inferred from the collection's first 200 documents: top-level field names are unioned across the sample, and each field takes its most common BSON type. Fields the collection's `$jsonSchema` validator declares follow, including ones no sampled document holds, and a field in the validator's `required` list is not nullable. `_id` comes first, marked as the primary key. The DDL tab shows indexes as `createIndex()` commands for `mongosh`, plus the validator and capped-collection options where present. ## Limitations diff --git a/docs/images/mongodb-create-collection-dark.png b/docs/images/mongodb-create-collection-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..5b95102a85375633b4281faa2d8d3a23dbd37e2c GIT binary patch literal 167230 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 P{;4QlIGc9{efNI>W?M~e literal 0 HcmV?d00001 diff --git a/docs/images/mongodb-create-collection.png b/docs/images/mongodb-create-collection.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ed790d98553bc5a91ccadadceaac8adff402bb GIT binary patch literal 149730 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 atVuV!qdU);{!G{gezec(pDEU`y8T~bwS9yD literal 0 HcmV?d00001 diff --git a/project.yml b/project.yml index 5ef6d9d521..eb1ae23d64 100644 --- a/project.yml +++ b/project.yml @@ -507,6 +507,8 @@ targets: - Plugins/MongoDBDriverPlugin/BsonDocumentFlattener.swift - Plugins/MongoDBDriverPlugin/MongoDBDecimal128.swift - Plugins/MongoDBDriverPlugin/MongoDBAuthSourceResolver.swift + - Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift + - Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift - Plugins/MongoDBDriverPlugin/MongoDBCreateDatabasePlan.swift - Plugins/MongoDBDriverPlugin/MongoDBFilterClause.swift - Plugins/MongoDBDriverPlugin/MongoDBFilterValue.swift From 313aa7d1e52519c0a3109434d7513c46d7624954 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 26 Sep 2026 10:27:32 +0700 Subject: [PATCH 2/4] fix(plugin-mongodb): keep row identity, int spelling, field order and cancellation intact on the new write and schema paths --- .../BsonDocumentFlattener.swift | 16 +++ .../MongoDBCollectionDDL.swift | 22 ++- .../MongoDBCollectionSchema.swift | 9 +- .../MongoDBConnection.swift | 1 - .../MongoDBPluginDriver.swift | 45 ++++-- .../MongoDBStatementGenerator.swift | 13 +- .../MongoDBDriverPlugin/MongoScriptJson.swift | 135 ++++++++++++------ .../MongoDB/BsonDocumentFlattenerTests.swift | 9 ++ .../Plugins/MongoDBCollectionDDLTests.swift | 23 ++- .../MongoDBCollectionSchemaTests.swift | 26 +++- .../Plugins/MongoDBWriteBackTypeTests.swift | 26 +++- docs/databases/mongodb.mdx | 3 +- 12 files changed, 259 insertions(+), 69 deletions(-) diff --git a/Plugins/MongoDBDriverPlugin/BsonDocumentFlattener.swift b/Plugins/MongoDBDriverPlugin/BsonDocumentFlattener.swift index 3980645f3f..66462cb345 100644 --- a/Plugins/MongoDBDriverPlugin/BsonDocumentFlattener.swift +++ b/Plugins/MongoDBDriverPlugin/BsonDocumentFlattener.swift @@ -371,6 +371,22 @@ struct BsonDocumentFlattener { // MARK: - Type Inference + /// The kind every non-null value of a field shares, or nil when they differ or there are none. + static func uniformKind( + of field: String, + in documents: [[String: Any]], + representation: MongoDBUuidRepresentation + ) -> BsonValueKind? { + var shared: BsonValueKind? + for doc in documents { + guard let value = doc[field], !(value is NSNull) else { continue } + let kind = valueKind(for: value, representation: representation) + if let shared, shared != kind { return nil } + shared = kind + } + return shared + } + private static func inferValueKind( for field: String, in documents: [[String: Any]], diff --git a/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift b/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift index dcbf72325b..21d80c4627 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBCollectionDDL.swift @@ -87,6 +87,12 @@ enum MongoDBCollectionDDL { column.name ) } + if isReorderedByTheShell(column.name) { + return String( + format: String(localized: "The shell would reorder or drop a field named %@. Choose another name."), + column.name + ) + } guard let alias = MongoDBBsonType.alias(forEditorType: column.dataType) else { return String( format: String(localized: "%1$@ has type %2$@, which MongoDB does not have. Choose a type from the list."), @@ -108,6 +114,17 @@ enum MongoDBCollectionDDL { return nil } + /// The statement is a JavaScript object literal, and JavaScript lists integer-like keys first in + /// ascending order and reads `__proto__` as the prototype rather than as a key, so neither kind of + /// name reaches the server where it was written. + private static func isReorderedByTheShell(_ name: String) -> Bool { + if name == "__proto__" { return true } + guard !name.isEmpty, name.utf8.allSatisfy({ (UInt8(ascii: "0") ... UInt8(ascii: "9")).contains($0) }) else { + return false + } + return name == "0" || !name.hasPrefix("0") + } + // MARK: - Indexes private static func indexKeyValue(for indexType: String?) -> String? { @@ -138,9 +155,12 @@ enum MongoDBCollectionDDL { index.indexType ?? "" ) } - if index.columns == [idField] { + if keyValue == "1", index.columns == [idField] { return String(localized: "MongoDB already indexes _id.") } + if keyValue == "\"text\"", index.isUnique { + return String(localized: "A text index cannot be unique.") + } guard keyValue == "\"hashed\"" else { return nil } if index.isUnique { return String(localized: "A hashed index cannot be unique.") diff --git a/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift index 0e16e8ec96..6026e0a6b6 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift @@ -151,16 +151,21 @@ struct MongoDBCollectionSchema: Equatable, Sendable { } private static func orderedKeys(of objectText: String, in decoded: [String: Any]) -> [String] { - let textual = MongoScriptJson.members(of: objectText).map(\.key).filter { decoded[$0] != nil } - let seen = Set(textual) + var seen = Set() + let textual = MongoScriptJson.members(of: objectText).map(\.key).filter { + decoded[$0] != nil && seen.insert($0).inserted + } return textual + decoded.keys.filter { !seen.contains($0) }.sorted() } + /// An `enum` of strings with no type of its own still only admits strings, and typing the field + /// as one is what keeps `"1"` from being written back as the number 1 and refused. private static func declaredTypes(in spec: [String: Any]) -> [String] { if let single = spec["bsonType"] as? String { return [single] } if let many = spec["bsonType"] as? [String] { return many } if let single = spec["type"] as? String { return [MongoDBBsonType.alias(forJsonSchemaType: single)].compactMap { $0 } } if let many = spec["type"] as? [String] { return many.compactMap(MongoDBBsonType.alias(forJsonSchemaType:)) } + if stringEnum(spec["enum"]) != nil { return ["string"] } return [] } diff --git a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift index 7ae49284c9..310cee6cd3 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBConnection.swift @@ -555,7 +555,6 @@ final class MongoDBConnection: @unchecked Sendable { /// The reply as canonical Extended JSON text, members in the order the server sent them. func runCommandJson(_ command: String, database: String? = nil) async throws -> String { #if canImport(CLibMongoc) - resetCancellation() return try await pluginDispatchAsync(on: queue) { [self] in guard !isShuttingDown, let client = self.client else { throw MongoDBError.notConnected diff --git a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift index 9a99367c1d..a6509afacc 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift @@ -19,6 +19,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { private var columnKindsByCollection: [String: [String: BsonValueKind]] = [:] private var fieldPathKindsByCollection: [String: [String: BsonValueKind]] = [:] private var declaredSchemasByCollection: [String: MongoDBCollectionSchema] = [:] + private var identityKindsByCollection: [String: BsonValueKind] = [:] private static let logger = Logger(subsystem: "com.TablePro", category: "MongoDBPluginDriver") @@ -204,7 +205,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { valueCeiling: ceiling ) if let switched = outcome.databaseSwitch { currentDb = switched } - let declared = await declaredColumns(for: outcome) + let declared = try await declaredColumns(for: outcome) return capToRowCap( MongoScriptResultBuilder.result( for: outcome, @@ -235,7 +236,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { /// whose writer and filters use it: `fetchColumns` runs on a pooled driver whose caches this /// one never sees. Read again on a first page, so a refresh picks up a changed validator, and /// reused while paging. - private func declaredColumns(for outcome: MongoScriptStatementResult) async -> MongoDBCollectionSchema { + private func declaredColumns(for outcome: MongoScriptStatementResult) async throws -> MongoDBCollectionSchema { guard outcome.producedDocuments, let find = outcome.find, find.database == currentDb, let collection = outcome.collection, !collection.isEmpty, @@ -248,7 +249,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { if find.skip > 0, let cached { schema = cached } else { - schema = await declaredSchema(of: collection, conn: conn) + schema = try await declaredSchema(of: collection, conn: conn) columnKindLock.withLock { declaredSchemasByCollection[key] = schema } } return find.returnsWholeDocuments ? schema : .empty @@ -328,7 +329,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { filter: "{}", sort: nil, projection: nil, skip: 0, limit: MongoStreamProjection.sampleSize ).docs - let schema = await declaredSchema(of: table, conn: conn) + let schema = try await declaredSchema(of: table, conn: conn) if docs.isEmpty { return MongoDBCollectionShape.emptyCollectionColumns(declaring: schema) @@ -370,14 +371,19 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { } /// A user whose role can `find` but not `listCollections` still browses, with no declared - /// fields, so a failed read is an empty schema rather than an error. - private func declaredSchema(of collection: String, conn: MongoDBConnection) async -> MongoDBCollectionSchema { - guard let reply = try? await conn.runCommandJson( - MongoDBCollectionSchema.listCollectionsCommand(for: collection), database: currentDb - ) else { + /// fields, so a failed read is an empty schema rather than an error. A Stop is not a failure: + /// swallowing it returned the page as though nothing had been cancelled. + private func declaredSchema(of collection: String, conn: MongoDBConnection) async throws -> MongoDBCollectionSchema { + do { + let reply = try await conn.runCommandJson( + MongoDBCollectionSchema.listCollectionsCommand(for: collection), database: currentDb + ) + return MongoDBCollectionSchema.parse(listCollectionsReply: reply) + } catch is CancellationError { + throw CancellationError() + } catch { return .empty } - return MongoDBCollectionSchema.parse(listCollectionsReply: reply) } func fetchAllColumns(schema: String?) async throws -> [String: [PluginColumnInfo]] { @@ -789,7 +795,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { ) -> [(statement: String, parameters: [PluginCellValue])]? { let generator = MongoDBStatementGenerator( collectionName: table, columns: columns, columnKinds: columnKinds(for: table), - declaredKinds: declaredKinds(for: table) + declaredKinds: declaredKinds(for: table), identityKind: identityKind(for: table) ) return generator.generateStatements( from: changes, insertedRowData: insertedRowData, @@ -806,7 +812,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { ) -> [(statement: String, parameters: [PluginCellValue])]? { let generator = MongoDBStatementGenerator( collectionName: table, columns: columns, columnKinds: columnKinds(for: table), - declaredKinds: declaredKinds(for: table) + declaredKinds: declaredKinds(for: table), identityKind: identityKind(for: table) ) return generator.generateRestore(rows: rows) } @@ -916,6 +922,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { for: sampled, documents: documents, representation: uuidRepresentation ) rememberColumnKinds(sampledKinds, for: sampled, collection: collection) + rememberIdentityKind(of: documents, collection: collection) rememberFieldPathKinds(from: documents, collection: collection) let unseen = MongoDBCollectionShape.declaredColumnsMissing(from: sampled, schema: declared) let columns = sampled + unseen @@ -982,6 +989,20 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { return columnKindLock.withLock { columnKindsByCollection[key] ?? [:] } } + private func rememberIdentityKind(of documents: [[String: Any]], collection: String) { + guard !collection.isEmpty else { return } + let kind = BsonDocumentFlattener.uniformKind( + of: MongoDBCollectionDDL.idField, in: documents, representation: uuidRepresentation + ) + let key = columnKindKey(collection) + columnKindLock.withLock { identityKindsByCollection[key] = kind } + } + + private func identityKind(for collection: String) -> BsonValueKind? { + let key = columnKindKey(collection) + return columnKindLock.withLock { identityKindsByCollection[key] } + } + private func declaredKinds(for collection: String) -> [String: BsonValueKind] { let key = columnKindKey(collection) return columnKindLock.withLock { declaredSchemasByCollection[key]?.valueKinds ?? [:] } diff --git a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift index 9d31620c9a..86fe97f25b 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift @@ -20,6 +20,10 @@ struct MongoDBStatementGenerator { /// Kinds the collection's validator declares. The server rejects any other type for these /// fields, so they outrank what the sampled documents happen to hold. var declaredKinds: [String: BsonValueKind] = [:] + /// The kind every sampled `_id` shares, or nil when they differ. An `_id` filter has to carry the + /// row's own type, and a majority kind would quote an ObjectId in a mostly-string collection and + /// match nothing. + var identityKind: BsonValueKind? private var collectionAccessor: String { MongoCollectionAccessor.expression(for: collectionName) @@ -243,11 +247,12 @@ struct MongoDBStatementGenerator { if let binary = MongoDBUuidCodec.extendedJsonFromWrapper(idValue) { return binary } - switch kind(of: "_id") { + let idKind = declaredKinds["_id"] ?? identityKind + switch idKind { case .string: return "\"\(escapeJsonString(idValue))\"" case .objectId, .int32, .int64, .double, .decimal128: - if let typed = typedJson(idValue, kind: kind(of: "_id")) { return typed } + if let typed = typedJson(idValue, kind: idKind) { return typed } default: break } @@ -331,8 +336,8 @@ struct MongoDBStatementGenerator { guard MongoDBJsonNumber.isValid(value), let parsed = Double(value), parsed.isFinite else { return nil } return "{\"$numberDouble\": \"\(escapeJsonString(value))\"}" case .int32: - guard Int32(value) != nil else { return nil } - return value + guard let parsed = Int32(value) else { return nil } + return String(parsed) default: return nil } diff --git a/Plugins/MongoDBDriverPlugin/MongoScriptJson.swift b/Plugins/MongoDBDriverPlugin/MongoScriptJson.swift index f406ec5d05..781a983114 100644 --- a/Plugins/MongoDBDriverPlugin/MongoScriptJson.swift +++ b/Plugins/MongoDBDriverPlugin/MongoScriptJson.swift @@ -108,25 +108,29 @@ enum MongoScriptJson { } /// Every member of a JSON object, in the order the document carries them, each value as text. + /// + /// The text is walked scalar by scalar, never by `Character`: a Unicode Prepend character joins + /// the `"` or `\` after it into one grapheme cluster, and a scan by cluster then misses the end + /// of a string that libbson escaped correctly. static func members(of objectJson: String) -> [(key: String, value: String)] { - let characters = Array(objectJson) - guard let start = characters.firstIndex(of: "{") else { return [] } + let scalars = Array(objectJson.unicodeScalars) + guard let start = scalars.firstIndex(of: "{") else { return [] } var index = start + 1 var pairs: [(key: String, value: String)] = [] - while index < characters.count { - skipWhitespace(characters, &index) - guard index < characters.count, characters[index] == "\"" else { return pairs } - guard let name = readString(characters, &index) else { return pairs } - skipWhitespace(characters, &index) - guard index < characters.count, characters[index] == ":" else { return pairs } + while index < scalars.count { + skipWhitespace(scalars, &index) + guard index < scalars.count, scalars[index] == "\"" else { return pairs } + guard let name = readString(scalars, &index) else { return pairs } + skipWhitespace(scalars, &index) + guard index < scalars.count, scalars[index] == ":" else { return pairs } index += 1 - skipWhitespace(characters, &index) + skipWhitespace(scalars, &index) let valueStart = index - skipValue(characters, &index) - pairs.append((name, String(characters[valueStart ..< index]).trimmingCharacters(in: .whitespaces))) - skipWhitespace(characters, &index) - guard index < characters.count, characters[index] == "," else { return pairs } + skipValue(scalars, &index) + pairs.append((name, text(scalars[valueStart ..< index]))) + skipWhitespace(scalars, &index) + guard index < scalars.count, scalars[index] == "," else { return pairs } index += 1 } return pairs @@ -134,19 +138,19 @@ enum MongoScriptJson { /// The elements of a JSON array, each as its own text. static func topLevelElements(_ arrayJson: String) -> [String] { - let characters = Array(arrayJson) - guard let start = characters.firstIndex(of: "[") else { return [] } + let scalars = Array(arrayJson.unicodeScalars) + guard let start = scalars.firstIndex(of: "[") else { return [] } var index = start + 1 var elements: [String] = [] - while index < characters.count { - skipWhitespace(characters, &index) - guard index < characters.count, characters[index] != "]" else { break } + while index < scalars.count { + skipWhitespace(scalars, &index) + guard index < scalars.count, scalars[index] != "]" else { break } let elementStart = index - skipValue(characters, &index) - elements.append(String(characters[elementStart ..< index]).trimmingCharacters(in: .whitespaces)) - skipWhitespace(characters, &index) - guard index < characters.count, characters[index] == "," else { break } + skipValue(scalars, &index) + elements.append(text(scalars[elementStart ..< index])) + skipWhitespace(scalars, &index) + guard index < scalars.count, scalars[index] == "," else { break } index += 1 } return elements @@ -154,46 +158,93 @@ enum MongoScriptJson { // MARK: - Scanning - private static func skipWhitespace(_ characters: [Character], _ index: inout Int) { - while index < characters.count, characters[index].isWhitespace { index += 1 } + private static func text(_ slice: ArraySlice) -> String { + var view = String.UnicodeScalarView() + view.append(contentsOf: slice) + return String(view).trimmingCharacters(in: .whitespaces) + } + + private static func isWhitespace(_ scalar: Unicode.Scalar) -> Bool { + scalar == " " || scalar == "\t" || scalar == "\n" || scalar == "\r" + } + + private static func skipWhitespace(_ scalars: [Unicode.Scalar], _ index: inout Int) { + while index < scalars.count, isWhitespace(scalars[index]) { index += 1 } } - private static func readString(_ characters: [Character], _ index: inout Int) -> String? { - guard index < characters.count, characters[index] == "\"" else { return nil } + private static func readString(_ scalars: [Unicode.Scalar], _ index: inout Int) -> String? { + guard index < scalars.count, scalars[index] == "\"" else { return nil } index += 1 - var value = "" - while index < characters.count { - let character = characters[index] - if character == "\\" { - index += 2 - continue - } + var value = String.UnicodeScalarView() + while index < scalars.count { + let scalar = scalars[index] index += 1 - if character == "\"" { return value } - value.append(character) + switch scalar { + case "\"": + return String(value) + case "\\": + guard let decoded = readEscape(scalars, &index) else { return nil } + value.append(decoded) + default: + value.append(scalar) + } } return nil } - private static func skipValue(_ characters: [Character], _ index: inout Int) { + private static func readEscape(_ scalars: [Unicode.Scalar], _ index: inout Int) -> Unicode.Scalar? { + guard index < scalars.count else { return nil } + let escape = scalars[index] + index += 1 + switch escape { + case "\"", "\\", "/": return escape + case "b": return "\u{08}" + case "f": return "\u{0C}" + case "n": return "\n" + case "r": return "\r" + case "t": return "\t" + case "u": return readUnicodeEscape(scalars, &index) + default: return nil + } + } + + private static func readUnicodeEscape(_ scalars: [Unicode.Scalar], _ index: inout Int) -> Unicode.Scalar? { + guard let unit = readHexUnit(scalars, &index) else { return nil } + guard (0xD800 ... 0xDBFF).contains(unit) else { return Unicode.Scalar(unit) } + guard index + 1 < scalars.count, scalars[index] == "\\", scalars[index + 1] == "u" else { return nil } + index += 2 + guard let low = readHexUnit(scalars, &index), (0xDC00 ... 0xDFFF).contains(low) else { return nil } + return Unicode.Scalar(0x10000 + ((unit - 0xD800) << 10) + (low - 0xDC00)) + } + + private static func readHexUnit(_ scalars: [Unicode.Scalar], _ index: inout Int) -> UInt32? { + guard index + 4 <= scalars.count else { return nil } + var digits = String.UnicodeScalarView() + digits.append(contentsOf: scalars[index ..< index + 4]) + guard let unit = UInt32(String(digits), radix: 16) else { return nil } + index += 4 + return unit + } + + private static func skipValue(_ scalars: [Unicode.Scalar], _ index: inout Int) { var depth = 0 var inString = false var escaped = false - while index < characters.count { - let character = characters[index] + while index < scalars.count { + let scalar = scalars[index] if escaped { escaped = false index += 1 continue } if inString { - if character == "\\" { escaped = true } - if character == "\"" { inString = false } + if scalar == "\\" { escaped = true } + if scalar == "\"" { inString = false } index += 1 continue } - switch character { + switch scalar { case "\"": inString = true case "{", "[": @@ -207,7 +258,7 @@ enum MongoScriptJson { break } index += 1 - if depth == 0, character == "}" || character == "]" { return } + if depth == 0, scalar == "}" || scalar == "]" { return } } } } diff --git a/TableProTests/Core/MongoDB/BsonDocumentFlattenerTests.swift b/TableProTests/Core/MongoDB/BsonDocumentFlattenerTests.swift index 7e089bda8a..a0e0bc565f 100644 --- a/TableProTests/Core/MongoDB/BsonDocumentFlattenerTests.swift +++ b/TableProTests/Core/MongoDB/BsonDocumentFlattenerTests.swift @@ -8,6 +8,15 @@ import TableProPluginKit import Testing struct BsonDocumentFlattenerTests { + @Test("A field's kind is uniform only when every value that is there shares it") + func uniformKindNeedsEveryValueToAgree() { + let same: [[String: Any]] = [["_id": "a"], ["_id": "b"], ["other": 1]] + let mixed: [[String: Any]] = [["_id": "a"], ["_id": MongoDBObjectId(hex: "65a1b2c3d4e5f60718293a4b")]] + #expect(BsonDocumentFlattener.uniformKind(of: "_id", in: same, representation: .unspecified) == .string) + #expect(BsonDocumentFlattener.uniformKind(of: "_id", in: mixed, representation: .unspecified) == nil) + #expect(BsonDocumentFlattener.uniformKind(of: "_id", in: [], representation: .unspecified) == nil) + } + // MARK: - unionColumns(from:) @Suite("unionColumns") diff --git a/TableProTests/Plugins/MongoDBCollectionDDLTests.swift b/TableProTests/Plugins/MongoDBCollectionDDLTests.swift index c5bf86fc1a..39b91b0219 100644 --- a/TableProTests/Plugins/MongoDBCollectionDDLTests.swift +++ b/TableProTests/Plugins/MongoDBCollectionDDLTests.swift @@ -188,6 +188,16 @@ struct MongoDBCollectionDDLTests { #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column("f", alias))) == nil) } + @Test("Field names a JavaScript object reorders or drops are refused", arguments: ["10", "0", "__proto__"]) + func shellReorderedNamesAreRefused(name: String) { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column(name, "string"))) != nil) + } + + @Test("Names that only look numeric are kept", arguments: ["007", "10a", "a10"]) + func nonCanonicalNumericNamesAreKept(name: String) { + #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column(name, "string"))) == nil) + } + @Test("Field names MongoDB cannot address are refused", arguments: ["$price", "a.b"]) func unaddressableNamesAreRefused(name: String) { #expect(MongoDBCollectionDDL.refusal(for: .addColumn(column(name, "string"))) != nil) @@ -245,12 +255,23 @@ struct MongoDBCollectionDDLTests { #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("h", ["f"], type: "HASH"))) == nil) } - @Test("An index on _id alone is refused, because MongoDB already has one") + @Test("An ascending index on _id alone is refused, because MongoDB already has one") func indexOnIdIsRefused() { #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("i", ["_id"]))) != nil) #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("i", ["_id", "lang"]))) == nil) } + @Test("A hashed index on _id is its own index, the one a hashed shard key needs") + func hashedIdIndexIsAccepted() { + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("h", ["_id"], type: "HASH"))) == nil) + } + + @Test("A text index cannot be unique") + func uniqueTextIndexIsRefused() { + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("t", ["body"], unique: true, type: "FULLTEXT"))) != nil) + #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("t", ["body"], type: "FULLTEXT"))) == nil) + } + @Test("A partial index written in SQL is refused") func whereClauseIsRefused() { #expect(MongoDBCollectionDDL.refusal(for: .addIndex(index("p", ["f"], whereClause: "f > 0"))) != nil) diff --git a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift index 2b9b8836ae..8233c937c1 100644 --- a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift +++ b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift @@ -83,12 +83,32 @@ struct MongoDBCollectionSchemaTests { #expect(MongoDBCollectionSchema.parse(listCollectionsReply: "not json").isEmpty) } - @Test("A field whose name carries an escaped quote is still read") + @Test("A field whose name carries an escaped quote keeps its own name and its place") func escapedFieldNamesSurvive() { let schema = MongoDBCollectionSchema.parse(jsonSchema: """ - { "properties" : { "a\\"b" : { "bsonType" : "string" }, "plain" : { "bsonType" : "int" } } } + { "properties" : { "a\\"b" : { "bsonType" : "string" }, "ab" : { "bsonType" : "int" }, \ + "plain" : { "bsonType" : "bool" } } } """) - #expect(Set(schema.fields.map { $0.name }) == ["a\"b", "plain"]) + #expect(schema.fields.map { $0.name } == ["a\"b", "ab", "plain"]) + } + + @Test("An enum of strings with no type of its own types the field as a string") + func stringEnumTypesTheField() throws { + let schema = MongoDBCollectionSchema.parse(jsonSchema: """ + { "properties" : { "level" : { "enum" : [ "1", "2" ] } } } + """) + let level = try #require(schema.field(named: "level")) + #expect(level.valueKind == .string) + #expect(level.allowedValues == ["1", "2"]) + } + + /// U+0600 joins the `\` after it into one `Character`, so a scan by `Character` missed the + /// escape and ended the string at the quote libbson had escaped. + @Test("A key holding a Prepend character before an escaped quote is read whole") + func membersReadKeysScalarByScalar() { + let json = "{\"x\u{0600}\\\"y\": 1, \"next\": 2}" + let keys = MongoScriptJson.members(of: json).map { $0.key } + #expect(keys == ["x\u{0600}\"y", "next"]) } @Test("The command asks for one collection by name, escaped") diff --git a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift index f0a8a39066..34181fb938 100644 --- a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift +++ b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift @@ -213,11 +213,16 @@ struct MongoDBWriteBackTypeTests { // MARK: - _id - private func updateFilter(id: String, idKind: BsonValueKind?) -> String? { + private func updateFilter( + id: String, + idKind: BsonValueKind?, + sampledKinds: [String: BsonValueKind] = [:] + ) -> String? { let gen = MongoDBStatementGenerator( collectionName: "codes", columns: ["_id", "label"], - columnKinds: idKind.map { ["_id": $0] } ?? [:] + columnKinds: sampledKinds, + identityKind: idKind ) let change = PluginRowChange( rowIndex: 0, @@ -248,6 +253,23 @@ struct MongoDBWriteBackTypeTests { #expect(stmt.contains(#"{"_id": {"$numberLong": "42"}}"#)) } + @Test("A mostly-string _id column does not quote a row whose _id kinds differ") + func mixedIdKindsFallBackToTheSpelling() throws { + let stmt = try #require(updateFilter( + id: "65a1b2c3d4e5f60718293a4b", idKind: nil, sampledKinds: ["_id": .string] + )) + #expect(stmt.contains(#"{"_id": {"$oid": "65a1b2c3d4e5f60718293a4b"}}"#)) + } + + /// JavaScriptCore evaluates a bare `010` in a statement as the octal number 8. + @Test("An int written with a leading zero is sent as its decimal value") + func int32IsCanonicalised() throws { + let stmt = try #require(update(column: "n", to: .text("010"), kinds: [:], declared: ["n": .int32])) + #expect(stmt.contains(#""n": 10"#)) + let negative = try #require(update(column: "n", to: .text("-010"), kinds: ["n": .int32])) + #expect(negative.contains(#""n": -10"#)) + } + @Test("An _id of unknown kind is still read from its spelling") func unknownIdFallsBackToItsSpelling() throws { let stmt = try #require(updateFilter(id: "65a1b2c3d4e5f60718293a4b", idKind: nil)) diff --git a/docs/databases/mongodb.mdx b/docs/databases/mongodb.mdx index 6f674496de..7b38d2fedd 100644 --- a/docs/databases/mongodb.mdx +++ b/docs/databases/mongodb.mdx @@ -105,7 +105,7 @@ Choose **Database > New Table…**. Each row of the grid is a field, and its typ MongoDB enforces the validator with its defaults, `strict` and `error`, so an insert or edit of the wrong type fails with `Document failed validation`. **Primary Key** on any field other than `_id` is refused, and so are field names that start with `$` or contain a dot. -Each row on the **Indexes** tab runs as a `createIndex` after the collection is made, with its fields in the order listed. **BTREE** is an ascending key, **HASH** is `hashed`, **FULLTEXT** is `text`, and **SPATIAL** is `2dsphere`. A hashed index takes one field and cannot be unique. +Each row on the **Indexes** tab runs as a `createIndex` after the collection is made, with its fields in the order listed. **BTREE** is an ascending key, **HASH** is `hashed`, **FULLTEXT** is `text`, and **SPATIAL** is `2dsphere`. A hashed index takes one field and cannot be unique, and a text index cannot be unique either. ## Writing queries @@ -201,6 +201,7 @@ New connections default to **Disabled**, and the driver has no TLS fallback: **P - A row with no `_id` cannot be updated or deleted. The save is skipped rather than matched on the remaining fields. Keep `_id` in the projection so every row carries one. - `_id` is read-only in the grid, and left out of an insert entirely so the server generates it. To choose your own, insert with `db.collection.insertOne({…})`. - Transactions are not exposed. Statements always run standalone, on any topology. +- A collection takes one text index. A second **FULLTEXT** row fails after the collection and the indexes before it are created: list every text field in one index instead. - **New Table…** writes the validator with the server's own level and action. To log bad documents instead of refusing them, run `db.runCommand({collMod: "articles", validationAction: "warn"})` after creating the collection. - Nested paths filter but do not sort. Sorting works on the grid's own columns. - **same element** covers a field one array deep. A path through an array inside another array needs nested `$elemMatch`, so those filter with dot notation only. From b8b2fc7b4d18bccb22471b63d524b43f015c1dfe Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 26 Sep 2026 10:42:41 +0700 Subject: [PATCH 3/4] test(plugin-mongodb): type string-only enums in the schema tests and add the new refusals to the string catalog --- TablePro/Resources/Localizable.xcstrings | 6 ++++++ TableProTests/Plugins/MongoDBCollectionSchemaTests.swift | 4 ++-- TableProTests/Plugins/MongoDBQueryBuilderTests.swift | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/TablePro/Resources/Localizable.xcstrings b/TablePro/Resources/Localizable.xcstrings index dcbef14383..c07bc87127 100644 --- a/TablePro/Resources/Localizable.xcstrings +++ b/TablePro/Resources/Localizable.xcstrings @@ -184157,6 +184157,12 @@ }, "%@ cannot take rows from this file, so no table was created." : { + }, + "The shell would reorder or drop a field named %@. Choose another name." : { + + }, + "A text index cannot be unique." : { + } }, "version" : "1.1" diff --git a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift index 8233c937c1..b51e43a53e 100644 --- a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift +++ b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift @@ -41,14 +41,14 @@ struct MongoDBCollectionSchemaTests { #expect(try #require(schema.field(named: "tags")).isRequired == false) } - @Test("A nullable union still types the value, and an open or binary type does not") + @Test("A nullable union and a string enum type the value, and an open or binary type does not") func valueKinds() { let schema = MongoDBCollectionSchema.parse(listCollectionsReply: reply(options: articlesOptions)) #expect(schema.valueKinds["title"] == .string) #expect(schema.valueKinds["tags"] == .array) #expect(schema.valueKinds["date"] == .date) #expect(schema.valueKinds["views"] == .int64) - #expect(schema.valueKinds["status"] == nil) + #expect(schema.valueKinds["status"] == .string) #expect(schema.valueKinds["attachment"] == nil) #expect(schema.valueKinds["score"] == nil) } diff --git a/TableProTests/Plugins/MongoDBQueryBuilderTests.swift b/TableProTests/Plugins/MongoDBQueryBuilderTests.swift index 17f62f0871..1f026bbf0b 100644 --- a/TableProTests/Plugins/MongoDBQueryBuilderTests.swift +++ b/TableProTests/Plugins/MongoDBQueryBuilderTests.swift @@ -878,8 +878,9 @@ struct MongoDBQueryBuilderTests { func escapingSeesQuotesInsideGraphemeClusters() throws { let hostile = "x\u{0600}\"}); db.victim.drop(); ({\"" let escaped = MongoDBQueryBuilder.escapeJsonString(hostile) - let data = try #require("\"\(escaped)\"".data(using: .utf8)) - let decoded = try JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) as? String + let decoded = try JSONSerialization.jsonObject( + with: Data("\"\(escaped)\"".utf8), options: [.fragmentsAllowed] + ) as? String #expect(decoded == hostile) #expect(MongoDBQueryBuilder.escapeJsonString("a\r\nb") == "a\\r\\nb") } From a6ec9076d4b88b07e6daa3a04dff32ad643dab98 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Sat, 26 Sep 2026 10:49:09 +0700 Subject: [PATCH 4/4] fix(plugin-mongodb): bound the schema read, skip it for projected finds, and type date and bool _id filters --- .../MongoDBCollectionSchema.swift | 7 ++++++- .../MongoDBPluginDriver.swift | 8 ++++---- .../MongoDBStatementGenerator.swift | 4 +++- .../MongoDBCollectionSchemaTests.swift | 1 + .../Plugins/MongoDBWriteBackTypeTests.swift | 19 +++++++++++++++++++ 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift index 6026e0a6b6..a6a4604d3a 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBCollectionSchema.swift @@ -110,8 +110,13 @@ struct MongoDBCollectionSchema: Equatable, Sendable { return values } + /// Bounded on the server, because the read runs after a find on the session driver and a + /// stalled catalog would hold every later statement on the connection behind it. + static let listCollectionsTimeoutMS = 5_000 + static func listCollectionsCommand(for collection: String) -> String { - "{\"listCollections\": 1, \"filter\": {\"name\": \(MongoScriptJson.jsonString(collection))}}" + "{\"listCollections\": 1, \"filter\": {\"name\": \(MongoScriptJson.jsonString(collection))}, " + + "\"maxTimeMS\": \(listCollectionsTimeoutMS)}" } /// Reads a `listCollections` reply's first collection. The reply is walked as text because the diff --git a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift index a6509afacc..505a894d84 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBPluginDriver.swift @@ -231,14 +231,14 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { } } - /// The fields the collection declares, for a find that returned whole documents; empty for a - /// projection, a pipeline or anything else. Read on the session driver because that is the one + /// The fields the collection declares, for a find that returned whole documents; empty, and not + /// read at all, for a projection, a pipeline or anything else. Read on the session driver because that is the one /// whose writer and filters use it: `fetchColumns` runs on a pooled driver whose caches this /// one never sees. Read again on a first page, so a refresh picks up a changed validator, and /// reused while paging. private func declaredColumns(for outcome: MongoScriptStatementResult) async throws -> MongoDBCollectionSchema { guard outcome.producedDocuments, - let find = outcome.find, find.database == currentDb, + let find = outcome.find, find.returnsWholeDocuments, find.database == currentDb, let collection = outcome.collection, !collection.isEmpty, let conn = mongoConnection else { return .empty @@ -252,7 +252,7 @@ final class MongoDBPluginDriver: PluginDatabaseDriver, @unchecked Sendable { schema = try await declaredSchema(of: collection, conn: conn) columnKindLock.withLock { declaredSchemasByCollection[key] = schema } } - return find.returnsWholeDocuments ? schema : .empty + return schema } private func declaredTypeName(of column: String, in schema: MongoDBCollectionSchema) -> String { diff --git a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift index 86fe97f25b..90bb05085d 100644 --- a/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift +++ b/Plugins/MongoDBDriverPlugin/MongoDBStatementGenerator.swift @@ -251,8 +251,10 @@ struct MongoDBStatementGenerator { switch idKind { case .string: return "\"\(escapeJsonString(idValue))\"" - case .objectId, .int32, .int64, .double, .decimal128: + case .objectId, .int32, .int64, .double, .decimal128, .date: if let typed = typedJson(idValue, kind: idKind) { return typed } + case .boolean: + if idValue == "true" || idValue == "false" { return idValue } default: break } diff --git a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift index b51e43a53e..14d10629d9 100644 --- a/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift +++ b/TableProTests/Plugins/MongoDBCollectionSchemaTests.swift @@ -117,6 +117,7 @@ struct MongoDBCollectionSchemaTests { let data = try #require(command.data(using: .utf8)) let parsed = try #require(try JSONSerialization.jsonObject(with: data) as? [String: Any]) #expect((parsed["filter"] as? [String: Any])?["name"] as? String == "a\"b") + #expect(parsed["maxTimeMS"] as? Int == MongoDBCollectionSchema.listCollectionsTimeoutMS) } // MARK: - Shape diff --git a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift index 34181fb938..9d9781817d 100644 --- a/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift +++ b/TableProTests/Plugins/MongoDBWriteBackTypeTests.swift @@ -203,6 +203,25 @@ struct MongoDBWriteBackTypeTests { #expect(parsed["note"] as? String == hostile) } + /// Measured on macOS 27: `JSONSerialization` refuses each of these, so none reaches the statement + /// as source. This pins it, because a parser that stopped at the first complete value would let + /// the rest run. + @Test("JSON followed by more text is written as a string, never pasted as source") + func jsonPrefixWithTrailingCodeIsAString() throws { + let payloads = [ + "{}, injected: db.dropDatabase(), tail: {}", + #"{"a":1}, db.dropDatabase(), {}"#, + "[1], db.dropDatabase(), [2]" + ] + for hostile in payloads { + let stmt = try #require(update(column: "note", to: .text(hostile), kinds: [:])) + #expect(stmt.contains("\"note\": \(MongoScriptJson.jsonString(hostile))"), "\(hostile)") + let gen = MongoDBStatementGenerator(collectionName: "notes", columns: ["_id", "note"]) + let restored = try #require(gen.generateRestore(rows: [["507f1f77bcf86cd799439011", .text(hostile)]])?.first) + #expect(restored.statement.contains("\"note\": \(MongoScriptJson.jsonString(hostile))"), "\(hostile)") + } + } + @Test("Restoring a deleted row writes a hostile stored string back as a string") func restoreDoesNotRunStoredText() throws { let gen = MongoDBStatementGenerator(collectionName: "notes", columns: ["_id", "body"])