@@ -145,6 +145,21 @@ final class CloudflareD1PluginDriver: PluginDatabaseDriver, @unchecked Sendable
145145 return mapRawResult ( payload, executionTime: executionTime)
146146 }
147147
148+ func executeBatch( queries: [ String ] ) async throws -> [ PluginQueryResult ] {
149+ guard let client = getClient ( ) else {
150+ throw CloudflareD1Error . notConnected
151+ }
152+
153+ let startTime = Date ( )
154+ let statements = queries. map { ( sql: $0, params: nil as [ Any ? ] ? ) }
155+ let payloads = try await client. executeBatchRaw ( statements: statements)
156+ let elapsed = Date ( ) . timeIntervalSince ( startTime)
157+
158+ return payloads. enumerated ( ) . map { _, payload in
159+ mapRawResult ( payload, executionTime: payload. meta? . duration ?? ( elapsed / Double( payloads. count) ) )
160+ }
161+ }
162+
148163 func cancelQuery( ) throws {
149164 lock. lock ( )
150165 httpClient? . cancelCurrentTask ( )
@@ -577,8 +592,111 @@ final class CloudflareD1PluginDriver: PluginDatabaseDriver, @unchecked Sendable
577592 throw CloudflareD1Error ( message: String ( localized: " Transactions are not supported by Cloudflare D1 " ) )
578593 }
579594
595+ // MARK: - DDL Generation
596+
597+ func generateCreateTableSQL( definition: PluginCreateTableDefinition ) -> String ? {
598+ guard !definition. columns. isEmpty else { return nil }
599+
600+ let tableName = quoteIdentifier ( definition. tableName)
601+ let pkColumns = definition. columns. filter { $0. isPrimaryKey }
602+ let inlinePK = pkColumns. count == 1
603+ var parts : [ String ] = definition. columns. map { d1ColumnDefinition ( $0, inlinePK: inlinePK) }
604+
605+ if pkColumns. count > 1 {
606+ let pkCols = pkColumns. map { quoteIdentifier ( $0. name) } . joined ( separator: " , " )
607+ parts. append ( " PRIMARY KEY ( \( pkCols) ) " )
608+ }
609+
610+ for fk in definition. foreignKeys {
611+ parts. append ( d1ForeignKeyDefinition ( fk) )
612+ }
613+
614+ let sql = " CREATE TABLE \( tableName) ( \n " +
615+ parts. joined ( separator: " , \n " ) +
616+ " \n ); "
617+
618+ return sql
619+ }
620+
621+ func generateAddColumnSQL( table: String , column: PluginColumnDefinition ) -> String ? {
622+ var def = " \( quoteIdentifier ( column. name) ) \( column. dataType) "
623+ if !column. isNullable { def += " NOT NULL " }
624+ if let defaultValue = column. defaultValue, !defaultValue. isEmpty {
625+ def += " DEFAULT \( d1DefaultValue ( defaultValue) ) "
626+ }
627+ return " ALTER TABLE \( quoteIdentifier ( table) ) ADD COLUMN \( def) "
628+ }
629+
630+ func generateDropColumnSQL( table: String , columnName: String ) -> String ? {
631+ " ALTER TABLE \( quoteIdentifier ( table) ) DROP COLUMN \( quoteIdentifier ( columnName) ) "
632+ }
633+
634+ func generateAddIndexSQL( table: String , index: PluginIndexDefinition ) -> String ? {
635+ let uniqueStr = index. isUnique ? " UNIQUE " : " "
636+ let cols = index. columns. map { quoteIdentifier ( $0) } . joined ( separator: " , " )
637+ return " CREATE \( uniqueStr) INDEX \( quoteIdentifier ( index. name) ) ON \( quoteIdentifier ( table) ) ( \( cols) ) "
638+ }
639+
640+ func generateDropIndexSQL( table: String , indexName: String ) -> String ? {
641+ " DROP INDEX IF EXISTS \( quoteIdentifier ( indexName) ) "
642+ }
643+
644+ func generateColumnDefinitionSQL( column: PluginColumnDefinition ) -> String ? {
645+ d1ColumnDefinition ( column, inlinePK: column. isPrimaryKey)
646+ }
647+
648+ func generateIndexDefinitionSQL( index: PluginIndexDefinition , tableName: String ? ) -> String ? {
649+ let uniqueStr = index. isUnique ? " UNIQUE " : " "
650+ let cols = index. columns. map { quoteIdentifier ( $0) } . joined ( separator: " , " )
651+ let onClause = tableName. map { " ON \( quoteIdentifier ( $0) ) " } ?? " "
652+ return " CREATE \( uniqueStr) INDEX \( quoteIdentifier ( index. name) ) \( onClause) ( \( cols) ) "
653+ }
654+
655+ func generateForeignKeyDefinitionSQL( fk: PluginForeignKeyDefinition ) -> String ? {
656+ d1ForeignKeyDefinition ( fk)
657+ }
658+
580659 // MARK: - Private Helpers
581660
661+ private func d1ColumnDefinition( _ col: PluginColumnDefinition , inlinePK: Bool ) -> String {
662+ var def = " \( quoteIdentifier ( col. name) ) \( col. dataType) "
663+ if inlinePK && col. isPrimaryKey {
664+ def += " PRIMARY KEY "
665+ if col. autoIncrement {
666+ def += " AUTOINCREMENT "
667+ }
668+ }
669+ if !col. isNullable {
670+ def += " NOT NULL "
671+ }
672+ if let defaultValue = col. defaultValue {
673+ def += " DEFAULT \( d1DefaultValue ( defaultValue) ) "
674+ }
675+ return def
676+ }
677+
678+ private func d1DefaultValue( _ value: String ) -> String {
679+ let upper = value. uppercased ( )
680+ if upper == " NULL " || upper == " CURRENT_TIMESTAMP " || upper == " CURRENT_DATE " || upper == " CURRENT_TIME "
681+ || value. hasPrefix ( " ' " ) || Int64 ( value) != nil || Double ( value) != nil {
682+ return value
683+ }
684+ return " ' \( escapeStringLiteral ( value) ) ' "
685+ }
686+
687+ private func d1ForeignKeyDefinition( _ fk: PluginForeignKeyDefinition ) -> String {
688+ let cols = fk. columns. map { quoteIdentifier ( $0) } . joined ( separator: " , " )
689+ let refCols = fk. referencedColumns. map { quoteIdentifier ( $0) } . joined ( separator: " , " )
690+ var def = " FOREIGN KEY ( \( cols) ) REFERENCES \( quoteIdentifier ( fk. referencedTable) ) ( \( refCols) ) "
691+ if fk. onDelete != " NO ACTION " {
692+ def += " ON DELETE \( fk. onDelete) "
693+ }
694+ if fk. onUpdate != " NO ACTION " {
695+ def += " ON UPDATE \( fk. onUpdate) "
696+ }
697+ return def
698+ }
699+
582700 private func getClient( ) -> D1HttpClient ? {
583701 lock. lock ( )
584702 defer { lock. unlock ( ) }
0 commit comments