/
SQLiteDriver.scala
179 lines (163 loc) · 7.56 KB
/
SQLiteDriver.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package scala.slick.driver
import scala.slick.SlickException
import scala.slick.lifted._
import scala.slick.ast._
import scala.slick.util.MacroSupport.macroSupportInterpolation
import java.sql.{Timestamp, Time, Date}
import scala.slick.profile.{RelationalProfile, SqlProfile, Capability}
import scala.slick.compiler.CompilerState
import scala.slick.jdbc.meta.MTable
import scala.slick.jdbc.UnitInvoker
import scala.slick.model.Model
import scala.slick.jdbc.meta.{createModel => jdbcCreateModel}
/**
* Slick driver for SQLite.
*
* This driver implements the [[scala.slick.driver.JdbcProfile]]
* ''without'' the following capabilities:
*
* <ul>
* <li>[[scala.slick.profile.RelationalProfile.capabilities.functionDatabase]],
* [[scala.slick.profile.RelationalProfile.capabilities.functionUser]]:
* <code>Functions.user</code> and <code>Functions.database</code> are
* not available in SQLite. Slick will return empty strings for both.</li>
* <li>[[scala.slick.profile.RelationalProfile.capabilities.joinFull]],
* [[scala.slick.profile.RelationalProfile.capabilities.joinRight]]:
* Right and full outer joins are not supported by SQLite.</li>
* <li>[[scala.slick.driver.JdbcProfile.capabilities.mutable]]:
* SQLite does not allow mutation of result sets. All cursors are
* read-only.</li>
* <li>[[scala.slick.profile.SqlProfile.capabilities.sequence]]:
* Sequences are not supported by SQLite.</li>
* <li>[[scala.slick.driver.JdbcProfile.capabilities.returnInsertOther]]:
* When returning columns from an INSERT operation, only a single column
* may be specified which must be the table's AutoInc column.</li>
* <li>[[scala.slick.profile.RelationalProfile.capabilities.typeBigDecimal]]:
* SQLite does not support a decimal type.</li>
* <li>[[scala.slick.profile.RelationalProfile.capabilities.typeBlob]]: Blobs are
* not supported by the SQLite JDBC driver (but binary data in the form of
* <code>Array[Byte]</code> is).</li>
* <li>[[scala.slick.profile.RelationalProfile.capabilities.zip]]:
* Row numbers (required by <code>zip</code> and
* <code>zipWithIndex</code>) are not supported. Trying to generate SQL
* code which uses this feature throws a SlickException.</li>
* </ul>
*
* @author Paul Snively
* @author Stefan Zeiger
*/
trait SQLiteDriver extends JdbcDriver { driver =>
override protected def computeCapabilities: Set[Capability] = (super.computeCapabilities
- RelationalProfile.capabilities.functionDatabase
- RelationalProfile.capabilities.functionUser
- RelationalProfile.capabilities.joinFull
- RelationalProfile.capabilities.joinRight
- JdbcProfile.capabilities.mutable
- SqlProfile.capabilities.sequence
- JdbcProfile.capabilities.returnInsertOther
- RelationalProfile.capabilities.typeBigDecimal
- RelationalProfile.capabilities.typeBlob
- RelationalProfile.capabilities.zip
)
override def getTables: UnitInvoker[MTable] = MTable.getTables(Some(""), Some(""), None, Some(Seq("TABLE")))
override def createModel(implicit session: Backend#Session): Model = jdbcCreateModel(
getTables.list.filter(_.name.name.toLowerCase != "sqlite_sequence"),
this
)
override val columnTypes = new JdbcTypes
override def createQueryBuilder(n: Node, state: CompilerState): QueryBuilder = new QueryBuilder(n, state)
override def createTableDDLBuilder(table: Table[_]): TableDDLBuilder = new TableDDLBuilder(table)
override def createColumnDDLBuilder(column: FieldSymbol, table: Table[_]): ColumnDDLBuilder = new ColumnDDLBuilder(column)
class QueryBuilder(tree: Node, state: CompilerState) extends super.QueryBuilder(tree, state) {
override protected val supportsTuples = false
override protected val concatOperator = Some("||")
override protected def buildOrdering(n: Node, o: Ordering) {
if(o.nulls.last && !o.direction.desc)
b"($n) is null,"
else if(o.nulls.first && o.direction.desc)
b"($n) is null desc,"
expr(n)
if(o.direction.desc) b" desc"
}
override protected def buildFetchOffsetClause(fetch: Option[Long], offset: Option[Long]) = (fetch, offset) match {
case (Some(t), Some(d)) => b" LIMIT $d,$t"
case (Some(t), None ) => b" LIMIT $t"
case (None, Some(d)) => b" LIMIT $d,-1"
case _ =>
}
override def expr(c: Node, skipParens: Boolean = false): Unit = c match {
case Library.UCase(ch) => b"upper(!$ch)"
case Library.LCase(ch) => b"lower(!$ch)"
case Library.%(l, r) => b"\($l%$r\)"
case Library.Ceiling(ch) => b"round($ch+0.5)"
case Library.Floor(ch) => b"round($ch-0.5)"
case Library.User() => b"''"
case Library.Database() => b"''"
case Apply(j: Library.JdbcFunction, ch) if j != Library.Concat =>
/* The SQLite JDBC driver does not support ODBC {fn ...} escapes, so we try
* unescaped function calls by default */
b"${j.name}("
b.sep(ch, ",")(expr(_, true))
b")"
case s: SimpleFunction if s.scalar =>
/* The SQLite JDBC driver does not support ODBC {fn ...} escapes, so we try
* unescaped function calls by default */
b"${s.name}("
b.sep(s.nodeChildren, ",")(expr(_, true))
b")"
case RowNumber(_) => throw new SlickException("SQLite does not support row numbers")
case _ => super.expr(c, skipParens)
}
}
class TableDDLBuilder(table: Table[_]) extends super.TableDDLBuilder(table) {
override protected val foreignKeys = Nil // handled directly in addTableOptions
override protected val primaryKeys = Nil // handled directly in addTableOptions
override protected def addTableOptions(b: StringBuilder) {
for(pk <- table.primaryKeys) {
b append ","
addPrimaryKey(pk, b)
}
for(fk <- table.foreignKeys) {
b append ","
addForeignKey(fk, b)
}
}
}
class ColumnDDLBuilder(column: FieldSymbol) extends super.ColumnDDLBuilder(column) {
override protected def appendOptions(sb: StringBuilder) {
if(defaultLiteral ne null) sb append " DEFAULT " append defaultLiteral
if(autoIncrement) sb append " PRIMARY KEY AUTOINCREMENT"
else if(primaryKey) sb append " PRIMARY KEY"
if(notNull) sb append " NOT NULL"
}
}
class JdbcTypes extends super.JdbcTypes {
override val booleanJdbcType = new BooleanJdbcType
override val dateJdbcType = new DateJdbcType
override val timeJdbcType = new TimeJdbcType
override val timestampJdbcType = new TimestampJdbcType
override val uuidJdbcType = new UUIDJdbcType
/* SQLite does not have a proper BOOLEAN type. The suggested workaround is
* INTEGER with constants 1 and 0 for TRUE and FALSE. */
class BooleanJdbcType extends super.BooleanJdbcType {
override def sqlTypeName = "INTEGER"
override def valueToSQLLiteral(value: Boolean) = if(value) "1" else "0"
}
/* The SQLite JDBC driver does not support the JDBC escape syntax for
* date/time/timestamp literals. SQLite expects these values as milliseconds
* since epoch. */
class DateJdbcType extends super.DateJdbcType {
override def valueToSQLLiteral(value: Date) = value.getTime.toString
}
class TimeJdbcType extends super.TimeJdbcType {
override def valueToSQLLiteral(value: Time) = value.getTime.toString
}
class TimestampJdbcType extends super.TimestampJdbcType {
override def valueToSQLLiteral(value: Timestamp) = value.getTime.toString
}
class UUIDJdbcType extends super.UUIDJdbcType {
override def sqlType = java.sql.Types.BLOB
}
}
}
object SQLiteDriver extends SQLiteDriver