Skip to content

Commit 8cba57a

Browse files
gatorsmileAndrew Or
authored and
Andrew Or
committed
[SPARK-14124][SQL][FOLLOWUP] Implement Database-related DDL Commands
#### What changes were proposed in this pull request? First, a few test cases failed in mac OS X because the property value of `java.io.tmpdir` does not include a trailing slash on some platform. Hive always removes the last trailing slash. For example, what I got in the web: ``` Win NT --> C:\TEMP\ Win XP --> C:\TEMP Solaris --> /var/tmp/ Linux --> /var/tmp ``` Second, a couple of test cases are added to verify if the commands work properly. #### How was this patch tested? Added a test case for it and correct the previous test cases. Author: gatorsmile <[email protected]> Author: xiaoli <[email protected]> Author: Xiao Li <[email protected]> Closes apache#12081 from gatorsmile/mkdir.
1 parent 63db2bd commit 8cba57a

File tree

4 files changed

+311
-97
lines changed

4 files changed

+311
-97
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/SessionCatalog.scala

+4
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ class SessionCatalog(
146146
currentDb = db
147147
}
148148

149+
/**
150+
* Get the path for creating a non-default database when database location is not provided
151+
* by users.
152+
*/
149153
def getDefaultDBPath(db: String): String = {
150154
val database = if (conf.caseSensitiveAnalysis) db else db.toLowerCase
151155
new Path(new Path(conf.warehousePath), database + ".db").toString

sql/core/src/main/scala/org/apache/spark/sql/execution/command/ddl.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ import org.apache.spark.sql.types._
4040
* unless 'ifNotExists' is true.
4141
* The syntax of using this command in SQL is:
4242
* {{{
43-
* CREATE DATABASE|SCHEMA [IF NOT EXISTS] database_name
43+
* CREATE (DATABASE|SCHEMA) [IF NOT EXISTS] database_name
44+
* [COMMENT database_comment]
45+
* [LOCATION database_directory]
46+
* [WITH DBPROPERTIES (property_name=property_value, ...)];
4447
* }}}
4548
*/
4649
case class CreateDatabase(

sql/core/src/test/scala/org/apache/spark/sql/execution/command/DDLSuite.scala

+159-90
Original file line numberDiff line numberDiff line change
@@ -95,49 +95,81 @@ class DDLSuite extends QueryTest with SharedSQLContext with BeforeAndAfterEach {
9595
catalog.createPartitions(tableName, Seq(part), ignoreIfExists = false)
9696
}
9797

98+
private def appendTrailingSlash(path: String): String = {
99+
if (!path.endsWith(File.separator)) path + File.separator else path
100+
}
101+
98102
test("the qualified path of a database is stored in the catalog") {
99103
val catalog = sqlContext.sessionState.catalog
100104

101-
val path = System.getProperty("java.io.tmpdir")
102-
// The generated temp path is not qualified.
103-
assert(!path.startsWith("file:/"))
104-
sql(s"CREATE DATABASE db1 LOCATION '$path'")
105-
val pathInCatalog = new Path(catalog.getDatabaseMetadata("db1").locationUri).toUri
106-
assert("file" === pathInCatalog.getScheme)
107-
assert(path === pathInCatalog.getPath)
108-
109-
withSQLConf(
110-
SQLConf.WAREHOUSE_PATH.key -> (System.getProperty("java.io.tmpdir"))) {
111-
sql(s"CREATE DATABASE db2")
112-
val pathInCatalog = new Path(catalog.getDatabaseMetadata("db2").locationUri).toUri
105+
withTempDir { tmpDir =>
106+
val path = tmpDir.toString
107+
// The generated temp path is not qualified.
108+
assert(!path.startsWith("file:/"))
109+
sql(s"CREATE DATABASE db1 LOCATION '$path'")
110+
val pathInCatalog = new Path(catalog.getDatabaseMetadata("db1").locationUri).toUri
113111
assert("file" === pathInCatalog.getScheme)
114-
assert(s"${sqlContext.conf.warehousePath}/db2.db" === pathInCatalog.getPath)
115-
}
112+
val expectedPath = if (path.endsWith(File.separator)) path.dropRight(1) else path
113+
assert(expectedPath === pathInCatalog.getPath)
114+
115+
withSQLConf(SQLConf.WAREHOUSE_PATH.key -> path) {
116+
sql(s"CREATE DATABASE db2")
117+
val pathInCatalog = new Path(catalog.getDatabaseMetadata("db2").locationUri).toUri
118+
assert("file" === pathInCatalog.getScheme)
119+
val expectedPath = appendTrailingSlash(sqlContext.conf.warehousePath) + "db2.db"
120+
assert(expectedPath === pathInCatalog.getPath)
121+
}
116122

117-
sql("DROP DATABASE db1")
118-
sql("DROP DATABASE db2")
123+
sql("DROP DATABASE db1")
124+
sql("DROP DATABASE db2")
125+
}
119126
}
120127

121128
test("Create/Drop Database") {
122-
withSQLConf(
123-
SQLConf.WAREHOUSE_PATH.key -> (System.getProperty("java.io.tmpdir") + File.separator)) {
124-
val catalog = sqlContext.sessionState.catalog
129+
withTempDir { tmpDir =>
130+
val path = tmpDir.toString
131+
withSQLConf(SQLConf.WAREHOUSE_PATH.key -> path) {
132+
val catalog = sqlContext.sessionState.catalog
133+
val databaseNames = Seq("db1", "`database`")
134+
135+
databaseNames.foreach { dbName =>
136+
try {
137+
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
125138

126-
val databaseNames = Seq("db1", "`database`")
139+
sql(s"CREATE DATABASE $dbName")
140+
val db1 = catalog.getDatabaseMetadata(dbNameWithoutBackTicks)
141+
val expectedLocation =
142+
"file:" + appendTrailingSlash(path) + s"$dbNameWithoutBackTicks.db"
143+
assert(db1 == CatalogDatabase(
144+
dbNameWithoutBackTicks,
145+
"",
146+
expectedLocation,
147+
Map.empty))
148+
sql(s"DROP DATABASE $dbName CASCADE")
149+
assert(!catalog.databaseExists(dbNameWithoutBackTicks))
150+
} finally {
151+
catalog.reset()
152+
}
153+
}
154+
}
155+
}
156+
}
127157

158+
test("Create/Drop Database - location") {
159+
val catalog = sqlContext.sessionState.catalog
160+
val databaseNames = Seq("db1", "`database`")
161+
withTempDir { tmpDir =>
162+
val path = tmpDir.toString
163+
val dbPath = "file:" + path
128164
databaseNames.foreach { dbName =>
129165
try {
130166
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
131-
132-
sql(s"CREATE DATABASE $dbName")
167+
sql(s"CREATE DATABASE $dbName Location '$path'")
133168
val db1 = catalog.getDatabaseMetadata(dbNameWithoutBackTicks)
134-
val expectedLocation =
135-
"file:" + System.getProperty("java.io.tmpdir") +
136-
File.separator + s"$dbNameWithoutBackTicks.db"
137169
assert(db1 == CatalogDatabase(
138170
dbNameWithoutBackTicks,
139171
"",
140-
expectedLocation,
172+
if (dbPath.endsWith(File.separator)) dbPath.dropRight(1) else dbPath,
141173
Map.empty))
142174
sql(s"DROP DATABASE $dbName CASCADE")
143175
assert(!catalog.databaseExists(dbNameWithoutBackTicks))
@@ -149,77 +181,78 @@ class DDLSuite extends QueryTest with SharedSQLContext with BeforeAndAfterEach {
149181
}
150182

151183
test("Create Database - database already exists") {
152-
withSQLConf(
153-
SQLConf.WAREHOUSE_PATH.key -> (System.getProperty("java.io.tmpdir") + File.separator)) {
154-
val catalog = sqlContext.sessionState.catalog
155-
val databaseNames = Seq("db1", "`database`")
156-
157-
databaseNames.foreach { dbName =>
158-
try {
159-
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
160-
sql(s"CREATE DATABASE $dbName")
161-
val db1 = catalog.getDatabaseMetadata(dbNameWithoutBackTicks)
162-
val expectedLocation =
163-
"file:" + System.getProperty("java.io.tmpdir") +
164-
File.separator + s"$dbNameWithoutBackTicks.db"
165-
assert(db1 == CatalogDatabase(
166-
dbNameWithoutBackTicks,
167-
"",
168-
expectedLocation,
169-
Map.empty))
170-
171-
val message = intercept[AnalysisException] {
184+
withTempDir { tmpDir =>
185+
val path = tmpDir.toString
186+
withSQLConf(SQLConf.WAREHOUSE_PATH.key -> path) {
187+
val catalog = sqlContext.sessionState.catalog
188+
val databaseNames = Seq("db1", "`database`")
189+
190+
databaseNames.foreach { dbName =>
191+
try {
192+
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
172193
sql(s"CREATE DATABASE $dbName")
173-
}.getMessage
174-
assert(message.contains(s"Database '$dbNameWithoutBackTicks' already exists."))
175-
} finally {
176-
catalog.reset()
194+
val db1 = catalog.getDatabaseMetadata(dbNameWithoutBackTicks)
195+
val expectedLocation =
196+
"file:" + appendTrailingSlash(path) + s"$dbNameWithoutBackTicks.db"
197+
assert(db1 == CatalogDatabase(
198+
dbNameWithoutBackTicks,
199+
"",
200+
expectedLocation,
201+
Map.empty))
202+
203+
val message = intercept[AnalysisException] {
204+
sql(s"CREATE DATABASE $dbName")
205+
}.getMessage
206+
assert(message.contains(s"Database '$dbNameWithoutBackTicks' already exists."))
207+
} finally {
208+
catalog.reset()
209+
}
177210
}
178211
}
179212
}
180213
}
181214

182215
test("Alter/Describe Database") {
183-
withSQLConf(
184-
SQLConf.WAREHOUSE_PATH.key -> (System.getProperty("java.io.tmpdir") + File.separator)) {
185-
val catalog = sqlContext.sessionState.catalog
186-
val databaseNames = Seq("db1", "`database`")
216+
withTempDir { tmpDir =>
217+
val path = tmpDir.toString
218+
withSQLConf(SQLConf.WAREHOUSE_PATH.key -> path) {
219+
val catalog = sqlContext.sessionState.catalog
220+
val databaseNames = Seq("db1", "`database`")
187221

188-
databaseNames.foreach { dbName =>
189-
try {
190-
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
191-
val location =
192-
"file:" + System.getProperty("java.io.tmpdir") +
193-
File.separator + s"$dbNameWithoutBackTicks.db"
194-
195-
sql(s"CREATE DATABASE $dbName")
196-
197-
checkAnswer(
198-
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
199-
Row("Database Name", dbNameWithoutBackTicks) ::
200-
Row("Description", "") ::
201-
Row("Location", location) ::
202-
Row("Properties", "") :: Nil)
203-
204-
sql(s"ALTER DATABASE $dbName SET DBPROPERTIES ('a'='a', 'b'='b', 'c'='c')")
205-
206-
checkAnswer(
207-
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
208-
Row("Database Name", dbNameWithoutBackTicks) ::
209-
Row("Description", "") ::
210-
Row("Location", location) ::
211-
Row("Properties", "((a,a), (b,b), (c,c))") :: Nil)
212-
213-
sql(s"ALTER DATABASE $dbName SET DBPROPERTIES ('d'='d')")
214-
215-
checkAnswer(
216-
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
217-
Row("Database Name", dbNameWithoutBackTicks) ::
218-
Row("Description", "") ::
219-
Row("Location", location) ::
220-
Row("Properties", "((a,a), (b,b), (c,c), (d,d))") :: Nil)
221-
} finally {
222-
catalog.reset()
222+
databaseNames.foreach { dbName =>
223+
try {
224+
val dbNameWithoutBackTicks = cleanIdentifier(dbName)
225+
val location = "file:" + appendTrailingSlash(path) + s"$dbNameWithoutBackTicks.db"
226+
227+
sql(s"CREATE DATABASE $dbName")
228+
229+
checkAnswer(
230+
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
231+
Row("Database Name", dbNameWithoutBackTicks) ::
232+
Row("Description", "") ::
233+
Row("Location", location) ::
234+
Row("Properties", "") :: Nil)
235+
236+
sql(s"ALTER DATABASE $dbName SET DBPROPERTIES ('a'='a', 'b'='b', 'c'='c')")
237+
238+
checkAnswer(
239+
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
240+
Row("Database Name", dbNameWithoutBackTicks) ::
241+
Row("Description", "") ::
242+
Row("Location", location) ::
243+
Row("Properties", "((a,a), (b,b), (c,c))") :: Nil)
244+
245+
sql(s"ALTER DATABASE $dbName SET DBPROPERTIES ('d'='d')")
246+
247+
checkAnswer(
248+
sql(s"DESCRIBE DATABASE EXTENDED $dbName"),
249+
Row("Database Name", dbNameWithoutBackTicks) ::
250+
Row("Description", "") ::
251+
Row("Location", location) ::
252+
Row("Properties", "((a,a), (b,b), (c,c), (d,d))") :: Nil)
253+
} finally {
254+
catalog.reset()
255+
}
223256
}
224257
}
225258
}
@@ -251,7 +284,43 @@ class DDLSuite extends QueryTest with SharedSQLContext with BeforeAndAfterEach {
251284
}
252285
}
253286

254-
// TODO: test drop database in restrict mode
287+
test("drop non-empty database in restrict mode") {
288+
val catalog = sqlContext.sessionState.catalog
289+
val dbName = "db1"
290+
sql(s"CREATE DATABASE $dbName")
291+
292+
// create a table in database
293+
val tableIdent1 = TableIdentifier("tab1", Some(dbName))
294+
createTable(catalog, tableIdent1)
295+
296+
// drop a non-empty database in Restrict mode
297+
val message = intercept[AnalysisException] {
298+
sql(s"DROP DATABASE $dbName RESTRICT")
299+
}.getMessage
300+
assert(message.contains(s"Database '$dbName' is not empty. One or more tables exist"))
301+
302+
catalog.dropTable(tableIdent1, ignoreIfNotExists = false)
303+
304+
assert(catalog.listDatabases().contains(dbName))
305+
sql(s"DROP DATABASE $dbName RESTRICT")
306+
assert(!catalog.listDatabases().contains(dbName))
307+
}
308+
309+
test("drop non-empty database in cascade mode") {
310+
val catalog = sqlContext.sessionState.catalog
311+
val dbName = "db1"
312+
sql(s"CREATE DATABASE $dbName")
313+
314+
// create a table in database
315+
val tableIdent1 = TableIdentifier("tab1", Some(dbName))
316+
createTable(catalog, tableIdent1)
317+
318+
// drop a non-empty database in CASCADE mode
319+
assert(catalog.listTables(dbName).contains(tableIdent1))
320+
assert(catalog.listDatabases().contains(dbName))
321+
sql(s"DROP DATABASE $dbName CASCADE")
322+
assert(!catalog.listDatabases().contains(dbName))
323+
}
255324

256325
test("create table in default db") {
257326
val catalog = sqlContext.sessionState.catalog

0 commit comments

Comments
 (0)