github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/ddl/formatter-sqlite3.go (about) 1 package ddl 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 ) 8 9 type SQLite3Formatter struct { 10 Template bool // set to true to enable template output (supports prefixes) 11 } 12 13 // NewSQLite3Formatter returns a new SQLite3Formatter. If the template argument 14 // is true then table prefixes (and any other templatable features) 15 // will be output in Go template form, for use with migrations. Passing false 16 // will produce raw SQL that can be executed directly. 17 func NewSQLite3Formatter(template bool) *SQLite3Formatter { 18 return &SQLite3Formatter{Template: template} 19 } 20 21 func (f *SQLite3Formatter) tmplPrefix() string { 22 if f.Template { 23 return "{{.TablePrefix}}" 24 } 25 return "" 26 } 27 28 func (f *SQLite3Formatter) DriverName() string { 29 return "sqlite3" 30 } 31 32 func (f *SQLite3Formatter) Format(stmt Stmt) ([]string, error) { 33 34 var buf bytes.Buffer 35 36 switch st := stmt.(type) { 37 38 case *CreateTableStmt: 39 ifNotExistsStr := "" 40 if st.IfNotExistsValue { 41 ifNotExistsStr = "IF NOT EXISTS " 42 } 43 fmt.Fprintf(&buf, `CREATE TABLE %s%s (`+"\n", ifNotExistsStr, sqlite3QuoteIdent(f.tmplPrefix()+st.NameValue)) 44 45 skipPKBlock := false 46 for _, col := range st.Columns { 47 48 // due to syntactic funk, we need to declare the primary key on the column for 49 // autoincrement functionality and cannot have a separate PRIMARY KEY(field) block 50 if col.DataTypeValue == BigIntAutoPK { 51 skipPKBlock = true 52 } 53 54 colstr, err := sqlite3ColStr(col) 55 if err != nil { 56 return nil, err 57 } 58 fmt.Fprintf(&buf, " %s,\n", colstr) 59 } 60 61 if (!skipPKBlock) && len(st.PrimaryKeys) > 0 { 62 fmt.Fprintf(&buf, " PRIMARY KEY(") 63 for idx, pk := range st.PrimaryKeys { 64 fmt.Fprintf(&buf, "%s", sqlite3QuoteIdent(pk)) 65 if idx < len(st.PrimaryKeys)-1 { 66 fmt.Fprintf(&buf, ",") 67 } 68 } 69 fmt.Fprintf(&buf, "),\n") 70 } 71 72 for _, fk := range st.ForeignKeys { 73 fmt.Fprintf(&buf, " FOREIGN KEY(%s) REFERENCES %s(%s),", 74 sqlite3QuoteIdent(fk.ColumnValue), 75 sqlite3QuoteIdent(f.tmplPrefix()+fk.OtherTableValue), 76 sqlite3QuoteIdent(fk.OtherColumnValue), 77 ) 78 } 79 80 withoutRowidStr := "" 81 for _, col := range st.Columns { 82 if col.DataTypeValue == VarCharPK { // varchar primary key triggers WITHOUT ROWID 83 withoutRowidStr = " WITHOUT ROWID" 84 break 85 } 86 } 87 if len(st.PrimaryKeys) > 1 { // multiple pks triggers WITHOUT ROWID 88 withoutRowidStr = " WITHOUT ROWID" 89 } 90 91 // remove any trailing comma and close table definition 92 fullStr := strings.TrimSuffix(strings.TrimSpace(buf.String()), ",") + "\n)" + 93 withoutRowidStr 94 return []string{fullStr}, nil 95 96 case *DropTableStmt: 97 fmt.Fprintf(&buf, `DROP TABLE %s`, sqlite3QuoteIdent(f.tmplPrefix()+st.NameValue)) 98 return []string{buf.String()}, nil 99 100 case *AlterTableRenameStmt: 101 fmt.Fprintf(&buf, `ALTER TABLE %s RENAME TO %s`, 102 sqlite3QuoteIdent(f.tmplPrefix()+st.OldNameValue), 103 sqlite3QuoteIdent(f.tmplPrefix()+st.NewNameValue), 104 ) 105 return []string{buf.String()}, nil 106 107 case *AlterTableAddStmt: 108 colStr, err := sqlite3ColStr(&st.DataTypeDef) 109 if err != nil { 110 return nil, err 111 } 112 fmt.Fprintf(&buf, `ALTER TABLE %s ADD COLUMN %s`, 113 sqlite3QuoteIdent(f.tmplPrefix()+st.NameValue), 114 colStr, 115 ) 116 return []string{buf.String()}, nil 117 118 case *CreateIndexStmt: 119 uniqueStr := "" 120 if st.UniqueValue { 121 uniqueStr = " UNIQUE" 122 } 123 ifNotExistsStr := "" 124 if st.IfNotExistsValue { 125 ifNotExistsStr = " IF NOT EXISTS" 126 } 127 colStr := "" 128 for _, colName := range st.ColumnNames { 129 colStr += sqlite3QuoteIdent(colName) + "," 130 } 131 colStr = strings.TrimRight(colStr, ",") 132 fmt.Fprintf(&buf, `CREATE%s INDEX%s %s ON %s(%s)`, 133 uniqueStr, 134 ifNotExistsStr, 135 sqlite3QuoteIdent(f.tmplPrefix()+st.NameValue), 136 sqlite3QuoteIdent(f.tmplPrefix()+st.TableNameValue), 137 colStr, 138 ) 139 return []string{buf.String()}, nil 140 141 case *DropIndexStmt: 142 fmt.Fprintf(&buf, `DROP INDEX %s`, 143 sqlite3QuoteIdent(f.tmplPrefix()+st.NameValue), 144 // NOTE: SQLite3 does not need or allow the table name 145 ) 146 return []string{buf.String()}, nil 147 148 } 149 150 return nil, fmt.Errorf("unknown statement type %T", stmt) 151 } 152 153 func sqlite3QuoteIdent(ident string) string { 154 return quoteIdent(ident, `"`) 155 } 156 157 func sqlite3EncodeString(s string) string { 158 // https://www.sqlite.org/faq.html 159 return `'` + strings.Replace(s, `'`, `''`, -1) + `'` 160 } 161 162 func sqlite3ColStr(col *DataTypeDef) (string, error) { 163 164 defaultStr := "" 165 if col.DefaultValue != nil { 166 if s, ok := col.DefaultValue.(string); ok { 167 defaultStr = fmt.Sprintf(" DEFAULT %s", sqlite3EncodeString(s)) 168 } else { 169 // FIXME: we should be more careful about what escaping and formatting is used here 170 // and the various possible data types 171 defaultStr = fmt.Sprintf(" DEFAULT %v", col.DefaultValue) 172 } 173 } 174 // sqlite3 ignores lengths, don't bother: https://www.sqlite.org/datatype3.html 175 // lengthStr := func(defaultLen int) string { 176 // if col.LengthValue <= 0 { 177 // if defaultLen > 0 { 178 // return fmt.Sprintf("(%d)", defaultLen) 179 // } 180 // return "" 181 // } 182 // return fmt.Sprintf("(%d)", col.LengthValue) 183 // } 184 nullStr := " NOT NULL" 185 if col.NullValue { 186 nullStr = " NULL" 187 } 188 caseSensitiveStr := " COLLATE NOCASE" 189 if col.CaseSensitiveValue { 190 caseSensitiveStr = "" // will default to binary 191 } 192 193 switch col.DataTypeValue { 194 case Custom: 195 return fmt.Sprintf("%s %s", sqlite3QuoteIdent(col.NameValue), col.CustomSQLValue), nil 196 case VarCharPK: 197 // always case sensitive 198 return fmt.Sprintf("%s VARCHAR%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 199 case BigIntAutoPK: 200 return fmt.Sprintf("%s INTEGER PRIMARY KEY AUTOINCREMENT", sqlite3QuoteIdent(col.NameValue)), nil 201 case VarCharFK: 202 // always case sensitive 203 return fmt.Sprintf("%s VARCHAR%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 204 case BigIntFK: 205 return fmt.Sprintf("%s INTEGER%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 206 case Int: 207 return fmt.Sprintf("%s INTEGER%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 208 case IntU: 209 return fmt.Sprintf("%s UNSIGNED INTEGER%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 210 case BigInt: 211 return fmt.Sprintf("%s INTEGER%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 212 case BigIntU: 213 return fmt.Sprintf("%s UNSIGNED INTEGER%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 214 case Double: 215 return fmt.Sprintf("%s REAL%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 216 case DateTime: 217 // datetime values need to be text for things to work correctly with SQLite3 218 return fmt.Sprintf("%s TEXT%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 219 case VarChar: 220 return fmt.Sprintf("%s VARCHAR%s%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, caseSensitiveStr, defaultStr), nil 221 case Text: 222 return fmt.Sprintf("%s TEXT%s%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, caseSensitiveStr, defaultStr), nil 223 case Bool: 224 // same as INTEGER but whatever 225 return fmt.Sprintf("%s BOOLEAN%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 226 case Blob: 227 return fmt.Sprintf("%s BLOB%s%s", sqlite3QuoteIdent(col.NameValue), nullStr, defaultStr), nil 228 229 } 230 231 return "", fmt.Errorf("unknown DataType: %v", col.DataTypeValue) 232 }