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  }