diff --git a/src/Databases/SQLite/DatabaseSQLite.cpp b/src/Databases/SQLite/DatabaseSQLite.cpp index 6ce2cc10e85..19b551e3f78 100644 --- a/src/Databases/SQLite/DatabaseSQLite.cpp +++ b/src/Databases/SQLite/DatabaseSQLite.cpp @@ -40,6 +40,12 @@ DatabaseSQLite::DatabaseSQLite( if (err) throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "SQLite database path '{}' is invalid. Error: {}", database_path_, err.message()); + String user_files_path = fs::canonical(context_->getUserFilesPath()); + if (!canonical_path.starts_with(user_files_path)) + throw Exception(ErrorCodes::PATH_ACCESS_DENIED, + "SQLite database file path '{}' must be inside 'user_files' directory: {}", + database_path_, user_files_path); + sqlite3 * tmp_sqlite_db = nullptr; int status = sqlite3_open(canonical_path.c_str(), &tmp_sqlite_db); diff --git a/src/Storages/StorageSQLite.cpp b/src/Storages/StorageSQLite.cpp index bb30f8ab9f9..2224f9533ee 100644 --- a/src/Storages/StorageSQLite.cpp +++ b/src/Storages/StorageSQLite.cpp @@ -161,11 +161,17 @@ void registerStorageSQLite(StorageFactory & factory) const auto table_name = engine_args[1]->as().value.safeGet(); std::error_code err; - auto canonical_path = fs::canonical(database_path, err); + String canonical_path = fs::canonical(database_path, err); /// The path existance is also checked here. if (err) throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "SQLite database path '{}' is invalid. Error: {}", database_path, err.message()); + String user_files_path = fs::canonical(args.getContext()->getUserFilesPath()); + if (!canonical_path.starts_with(user_files_path)) + throw Exception(ErrorCodes::PATH_ACCESS_DENIED, + "SQLite database file path '{}' must be inside 'user_files' directory: {}", + database_path, user_files_path); + sqlite3 * tmp_sqlite_db = nullptr; int status = sqlite3_open(canonical_path.c_str(), &tmp_sqlite_db); if (status != SQLITE_OK) diff --git a/src/TableFunctions/TableFunctionSQLite.cpp b/src/TableFunctions/TableFunctionSQLite.cpp index 238a776dd8f..038f7fb1e91 100644 --- a/src/TableFunctions/TableFunctionSQLite.cpp +++ b/src/TableFunctions/TableFunctionSQLite.cpp @@ -9,6 +9,8 @@ #include "registerTableFunctions.h" #include +#include + #include #include @@ -83,12 +85,16 @@ void TableFunctionSQLite::parseArguments(const ASTPtr & ast_function, ContextPtr if (err) throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "SQLite database path '{}' is invalid. Error: {}", database_path, err.message()); + String user_files_path = fs::canonical(context->getUserFilesPath()); + if (!canonical_path.starts_with(user_files_path)) + throw Exception(ErrorCodes::PATH_ACCESS_DENIED, "SQLite database file path '{}' must be inside 'user_files' directory", database_path); + sqlite3 * tmp_sqlite_db = nullptr; int status = sqlite3_open(canonical_path.c_str(), &tmp_sqlite_db); if (status != SQLITE_OK) - throw Exception(ErrorCodes::SQLITE_ENGINE_ERROR, - "Failed to open sqlite database. Status: {}. Message: {}", - status, sqlite3_errstr(status)); + throw Exception(ErrorCodes::PATH_ACCESS_DENIED, + "SQLite database file path '{}' must be inside 'user_files' directory: {}", + database_path, user_files_path); sqlite_db = std::shared_ptr(tmp_sqlite_db, sqlite3_close); } diff --git a/tests/queries/0_stateless/01889_sqlite_read_write.reference b/tests/queries/0_stateless/01889_sqlite_read_write.reference index 2388a8b16c5..6a5e77b909b 100644 --- a/tests/queries/0_stateless/01889_sqlite_read_write.reference +++ b/tests/queries/0_stateless/01889_sqlite_read_write.reference @@ -3,14 +3,16 @@ show database tables: table1 table2 table3 +table4 +table5 +show creare table: +CREATE TABLE SQLite.table1\n(\n `col1` Nullable(String),\n `col2` Nullable(Int16)\n)\nENGINE = SQLite +CREATE TABLE SQLite.table2\n(\n `col1` Nullable(Int32),\n `col2` Nullable(String)\n)\nENGINE = SQLite describe table: col1 Nullable(String) col2 Nullable(Int16) col1 Nullable(Int32) col2 Nullable(String) -describe table: -CREATE TABLE SQLite.table1\n(\n `col1` Nullable(String),\n `col2` Nullable(Int16)\n)\nENGINE = SQLite -CREATE TABLE SQLite.table2\n(\n `col1` Nullable(Int32),\n `col2` Nullable(String)\n)\nENGINE = SQLite select *: line1 1 line2 2 @@ -18,28 +20,19 @@ line3 3 1 text1 2 text2 3 text3 -test NULLs: -\N 1 -not a null 2 -\N 3 - 4 -detach -line1 1 -line2 2 -line3 3 -1 text1 -2 text2 -3 text3 +test types +CREATE TABLE SQLite.table4\n(\n `a` Nullable(Int32),\n `b` Nullable(Int32),\n `c` Nullable(Int8),\n `d` Nullable(Int16),\n `e` Nullable(Int32),\n `bigint` Nullable(String),\n `int2` Nullable(String),\n `int8` Nullable(String)\n)\nENGINE = SQLite +CREATE TABLE SQLite.table5\n(\n `a` Nullable(String),\n `b` Nullable(String),\n `c` Nullable(Float64),\n `d` Nullable(Float64),\n `e` Nullable(Float64),\n `f` Nullable(Float32)\n)\nENGINE = SQLite create table engine with table3 CREATE TABLE default.sqlite_table3\n(\n `col1` String,\n `col2` Int32\n)\nENGINE = SQLite 1 not a null 2 3 4 -test types -CREATE TABLE SQLite.table4\n(\n `a` Nullable(Int32),\n `b` Nullable(Int32),\n `c` Nullable(Int8),\n `d` Nullable(Int16),\n `e` Nullable(Int32),\n `bigint` Nullable(String),\n `int2` Nullable(String),\n `int8` Nullable(String)\n)\nENGINE = SQLite -CREATE TABLE SQLite.table5\n(\n `a` Nullable(String),\n `b` Nullable(String),\n `c` Nullable(Float64),\n `d` Nullable(Float64),\n `e` Nullable(Float64),\n `f` Nullable(Float32)\n)\nENGINE = SQLite +line6 6 + 7 test table function line1 1 line2 2 line3 3 +line4 4 diff --git a/tests/queries/0_stateless/01889_sqlite_read_write.sh b/tests/queries/0_stateless/01889_sqlite_read_write.sh index f78736b841a..889c375e61b 100755 --- a/tests/queries/0_stateless/01889_sqlite_read_write.sh +++ b/tests/queries/0_stateless/01889_sqlite_read_write.sh @@ -4,71 +4,77 @@ CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh . "$CUR_DIR"/../shell_config.sh -DATA_FILE1=$CUR_DIR/data_sqlite/db1 -DATA_FILE2=$CUR_DIR/db2 +# See 01658_read_file_to_string_column.sh +user_files_path=$(clickhouse-client --query "select _path,_file from file('nonexist.txt', 'CSV', 'val1 char')" 2>&1 | grep Exception | awk '{gsub("/nonexist.txt","",$9); print $9}') + +mkdir -p ${user_files_path}/ +DB_PATH=${user_files_path}/db1 + + +sqlite3 ${DB_PATH} 'DROP TABLE IF EXISTS table1' +sqlite3 ${DB_PATH} 'DROP TABLE IF EXISTS table2' +sqlite3 ${DB_PATH} 'DROP TABLE IF EXISTS table3' +sqlite3 ${DB_PATH} 'DROP TABLE IF EXISTS table4' +sqlite3 ${DB_PATH} 'DROP TABLE IF EXISTS table5' + +sqlite3 ${DB_PATH} 'CREATE TABLE table1 (col1 text, col2 smallint);' +sqlite3 ${DB_PATH} 'CREATE TABLE table2 (col1 int, col2 text);' + +sqlite3 ${DB_PATH} "INSERT INTO table1 VALUES ('line1', 1), ('line2', 2), ('line3', 3)" +sqlite3 ${DB_PATH} "INSERT INTO table2 VALUES (1, 'text1'), (2, 'text2'), (3, 'text3')" + +sqlite3 ${DB_PATH} 'CREATE TABLE table3 (col1 text, col2 int);' +sqlite3 ${DB_PATH} 'INSERT INTO table3 VALUES (NULL, 1)' +sqlite3 ${DB_PATH} "INSERT INTO table3 VALUES ('not a null', 2)" +sqlite3 ${DB_PATH} 'INSERT INTO table3 VALUES (NULL, 3)' +sqlite3 ${DB_PATH} "INSERT INTO table3 VALUES ('', 4)" + +sqlite3 ${DB_PATH} 'CREATE TABLE table4 (a int, b integer, c tinyint, d smallint, e mediumint, bigint, int2, int8)' +sqlite3 ${DB_PATH} 'CREATE TABLE table5 (a character(20), b varchar(10), c real, d double, e double precision, f float)' + ${CLICKHOUSE_CLIENT} --query='DROP DATABASE IF EXISTS sqlite_database' ${CLICKHOUSE_CLIENT} --query="select 'create database engine'"; -${CLICKHOUSE_CLIENT} --query="CREATE DATABASE sqlite_database ENGINE = SQLite('${DATA_FILE1}')" +${CLICKHOUSE_CLIENT} --query="CREATE DATABASE sqlite_database ENGINE = SQLite('${DB_PATH}')" ${CLICKHOUSE_CLIENT} --query="select 'show database tables:'"; ${CLICKHOUSE_CLIENT} --query='SHOW TABLES FROM sqlite_database;' +${CLICKHOUSE_CLIENT} --query="select 'show creare table:'"; +${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table1;' | sed -r 's/(.*SQLite)(.*)/\1/' +${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table2;' | sed -r 's/(.*SQLite)(.*)/\1/' + ${CLICKHOUSE_CLIENT} --query="select 'describe table:'"; ${CLICKHOUSE_CLIENT} --query='DESCRIBE TABLE sqlite_database.table1;' ${CLICKHOUSE_CLIENT} --query='DESCRIBE TABLE sqlite_database.table2;' -${CLICKHOUSE_CLIENT} --query="select 'describe table:'"; -${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table1;' | sed -r 's/(.*SQLite)(.*)/\1/' -${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table2;' | sed -r 's/(.*SQLite)(.*)/\1/' - ${CLICKHOUSE_CLIENT} --query="select 'select *:'"; ${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_database.table1 ORDER BY col2' ${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_database.table2 ORDER BY col1;' -sqlite3 $CUR_DIR/db2 'DROP TABLE IF EXISTS table3' -sqlite3 $CUR_DIR/db2 'CREATE TABLE table3 (col1 text, col2 int)' -sqlite3 $CUR_DIR/db2 'INSERT INTO table3 VALUES (NULL, 1)' -sqlite3 $CUR_DIR/db2 "INSERT INTO table3 VALUES ('not a null', 2)" -sqlite3 $CUR_DIR/db2 'INSERT INTO table3 VALUES (NULL, 3)' -sqlite3 $CUR_DIR/db2 "INSERT INTO table3 VALUES ('', 4)" +${CLICKHOUSE_CLIENT} --query="select 'test types'"; +${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table4;' | sed -r 's/(.*SQLite)(.*)/\1/' +${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database.table5;' | sed -r 's/(.*SQLite)(.*)/\1/' -${CLICKHOUSE_CLIENT} --query='DROP DATABASE IF EXISTS sqlite_database_2' -${CLICKHOUSE_CLIENT} --query="CREATE DATABASE sqlite_database_2 ENGINE = SQLite('${DATA_FILE2}')" -# Do not run these, bacuase requires permissions in ci for write access to the directory of the created file and chmod does not help. -# ${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_database_2.table3 VALUES (NULL, 3);" -# ${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_database_2.table3 VALUES (NULL, 4);" -# ${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_database_2.table3 VALUES ('line5', 5);" -${CLICKHOUSE_CLIENT} --query="select 'test NULLs:'"; -${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_database_2.table3 ORDER BY col2;' +${CLICKHOUSE_CLIENT} --query='DROP DATABASE IF EXISTS sqlite_database' -${CLICKHOUSE_CLIENT} --query="select 'detach'"; -${CLICKHOUSE_CLIENT} --query='DETACH DATABASE sqlite_database;' -${CLICKHOUSE_CLIENT} --query='ATTACH DATABASE sqlite_database;' - -${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_database.table1 ORDER BY col2' -${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_database.table2 ORDER BY col1;' - -${CLICKHOUSE_CLIENT} --query='DROP DATABASE IF EXISTS sqlite_database;' ${CLICKHOUSE_CLIENT} --query="select 'create table engine with table3'"; ${CLICKHOUSE_CLIENT} --query='DROP TABLE IF EXISTS sqlite_table3' -${CLICKHOUSE_CLIENT} --query="CREATE TABLE sqlite_table3 (col1 String, col2 Int32) ENGINE = SQLite('${DATA_FILE2}', 'table3')" +${CLICKHOUSE_CLIENT} --query="CREATE TABLE sqlite_table3 (col1 String, col2 Int32) ENGINE = SQLite('${DB_PATH}', 'table3')" + ${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_table3;' | sed -r 's/(.*SQLite)(.*)/\1/' -# Do not run these, bacuase requires permissions in ci for write access to the directory of the created file and chmod does not help. -# ${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_table3 VALUES ('line6', 6);" -# ${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_table3 VALUES (NULL, 7);" + +${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_table3 VALUES ('line6', 6);" +${CLICKHOUSE_CLIENT} --query="INSERT INTO sqlite_table3 VALUES (NULL, 7);" + ${CLICKHOUSE_CLIENT} --query='SELECT * FROM sqlite_table3 ORDER BY col2' -sqlite3 $CUR_DIR/db2 'DROP TABLE IF EXISTS table4' -sqlite3 $CUR_DIR/db2 'CREATE TABLE table4 (a int, b integer, c tinyint, d smallint, e mediumint, bigint, int2, int8)' -${CLICKHOUSE_CLIENT} --query="select 'test types'"; -${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database_2.table4;' | sed -r 's/(.*SQLite)(.*)/\1/' -sqlite3 $CUR_DIR/db2 'CREATE TABLE table5 (a character(20), b varchar(10), c real, d double, e double precision, f float)' -${CLICKHOUSE_CLIENT} --query='SHOW CREATE TABLE sqlite_database_2.table5;' | sed -r 's/(.*SQLite)(.*)/\1/' ${CLICKHOUSE_CLIENT} --query="select 'test table function'"; -${CLICKHOUSE_CLIENT} --query="SELECT * FROM sqlite('${DATA_FILE1}', 'table1') ORDER BY col2" +${CLICKHOUSE_CLIENT} --query="INSERT INTO TABLE FUNCTION sqlite('${DB_PATH}', 'table1') SELECT 'line4', 4" +${CLICKHOUSE_CLIENT} --query="SELECT * FROM sqlite('${DB_PATH}', 'table1') ORDER BY col2" -rm ${DATA_FILE2} + +rm -r ${DB_PATH} diff --git a/tests/queries/0_stateless/data_sqlite/db1 b/tests/queries/0_stateless/data_sqlite/db1 deleted file mode 100644 index 776eff686fb..00000000000 Binary files a/tests/queries/0_stateless/data_sqlite/db1 and /dev/null differ