Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fbe3eb7
feat: throws error for string into integer col
luizotavio32 Aug 12, 2025
0f9d3db
test: throws error for string into integer col
luizotavio32 Aug 12, 2025
5f9188d
test: add float validation test
luizotavio32 Aug 13, 2025
fd13b85
feat: casting is now done after the dataframe is created for better e…
luizotavio32 Aug 19, 2025
bc73dd7
test: casting is now done after the dataframe is created for better e…
luizotavio32 Aug 19, 2025
d05070b
removing unused csv
luizotavio32 Aug 19, 2025
1cf7923
feat: validation for empty table names when Trino is selected
luizotavio32 Aug 20, 2025
75586ef
test: validation for empty table names when Trino is selected
luizotavio32 Aug 20, 2025
79920f8
Merge branch 'master' into upload-csv-fixes
luizotavio32 Aug 20, 2025
142965a
chore: pre-commit idents
luizotavio32 Aug 20, 2025
7cca960
refactor: creates a interface to the selected dataase storing id and …
luizotavio32 Aug 21, 2025
bd40677
feat: new type of excpetion raise due to some code overrides implemet…
luizotavio32 Aug 22, 2025
742849a
revert validation on frontend
luizotavio32 Aug 25, 2025
2dbf71d
revert table name frontend validation
luizotavio32 Aug 25, 2025
c538717
refactor: generic exception handling to acquire possible exception me…
luizotavio32 Aug 28, 2025
6fb05f7
korbit suggestions
luizotavio32 Sep 1, 2025
f3e13b6
perfomance increase suggested by claude
luizotavio32 Sep 2, 2025
f7be36f
checks table name entirely on lower case due to Trino enforcing lower…
luizotavio32 Sep 3, 2025
6215ac0
refactor: compares the entire broken column to check for possible add…
luizotavio32 Sep 5, 2025
05d0a44
fixes pylint warnings
luizotavio32 Sep 5, 2025
6c6df38
Defining error limit to 5 as a constant inside csv_reader
luizotavio32 Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions superset/commands/database/uploaders/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
)
from superset.connectors.sqla.models import SqlaTable
from superset.daos.database import DatabaseDAO
from superset.exceptions import SupersetException
from superset.models.core import Database
from superset.sql.parse import Table
from superset.utils.backports import StrEnum
Expand Down Expand Up @@ -124,6 +125,8 @@ def _dataframe_to_database(
df,
to_sql_kwargs=to_sql_kwargs,
)
except SupersetException:
raise
Comment thread
luizotavio32 marked this conversation as resolved.
Outdated
except ValueError as ex:
raise DatabaseUploadFailed(
message=_(
Expand Down
32 changes: 26 additions & 6 deletions superset/commands/database/uploaders/csv_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ def _read_csv( # noqa: C901
kwargs["low_memory"] = False

try:
types = kwargs.pop("dtype", None)
if "chunksize" in kwargs:
chunks = []
total_rows = 0
Expand Down Expand Up @@ -188,13 +189,32 @@ def _read_csv( # noqa: C901
index_col = kwargs.get("index_col")
if isinstance(index_col, str):
result.index.name = index_col
return result
return pd.DataFrame()
df = result
else:
df = pd.read_csv(
filepath_or_buffer=file.stream,
**kwargs,
)

return pd.read_csv(
filepath_or_buffer=file.stream,
**kwargs,
)
if types:
for column, dtype in types.items():
try:
df[column] = df[column].astype(dtype)
Comment thread
luizotavio32 marked this conversation as resolved.
Outdated
except ValueError as ex:
error_msg = f"Non {dtype} value found in column '{column}'."
ex_msg = str(ex)
invalid_value = ex_msg.split(":")[-1].strip().strip("'")
error_msg += (
f" Value: '{invalid_value}'" if invalid_value else ""
)
mask = df[column] == invalid_value
Comment thread
luizotavio32 marked this conversation as resolved.
Outdated
if mask.any() and invalid_value:
line_number = mask.idxmax() + kwargs.get("header", 0) + 1
Comment thread
luizotavio32 marked this conversation as resolved.
Outdated
error_msg += f", line: {line_number}."
raise DatabaseUploadFailed(message=error_msg) from ex
return df
except DatabaseUploadFailed:
raise
except UnicodeDecodeError as ex:
if encoding != DEFAULT_ENCODING:
raise DatabaseUploadFailed(
Expand Down
169 changes: 169 additions & 0 deletions tests/unit_tests/commands/databases/csv_reader_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,175 @@ def test_csv_reader_file_metadata_invalid_file():
)


def test_csv_reader_integer_in_float_column():
csv_data = [
["Name", "Score", "City"],
["name1", 25.5, "city1"],
["name2", 25, "city2"],
]

csv_reader = CSVReader(
options=CSVReaderOptions(column_data_types={"Score": "float"})
)

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (2, 3)
assert df["Score"].dtype == "float64"


def test_csv_reader_object_type_auto_inferring():
# this case below won't raise a error
csv_data = [
["Name", "id", "City"],
["name1", 25.5, "city1"],
["name2", 15, "city2"],
["name3", 123456789086, "city3"],
["name4", "abc", "city4"],
["name5", 4.75, "city5"],
]

csv_reader = CSVReader()

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (5, 3)
# pandas automatically infers the type if column_data_types is not informed
# if there's only one string in the column it converts the whole column to object
assert df["id"].dtype == "object"


def test_csv_reader_float_type_auto_inferring():
csv_data = [
["Name", "id", "City"],
["name1", "25", "city1"],
["name2", "15", "city2"],
["name3", "123456789086", "city3"],
["name5", "4.75", "city5"],
]

csv_reader = CSVReader()

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (4, 3)
# The type here is automatically inferred to float due to 4.75 value
assert df["id"].dtype == "float64"


def test_csv_reader_int_type_auto_inferring():
csv_data = [
["Name", "id", "City"],
["name1", "0", "city1"],
["name2", "15", "city2"],
["name3", "123456789086", "city3"],
["name5", "45", "city5"],
]

csv_reader = CSVReader()

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (4, 3)
assert df["id"].dtype == "int64"


def test_csv_reader_bigint_type_auto_inferring():
csv_data = [
["Name", "id", "City"],
["name1", "9223372036854775807", "city1"],
["name2", "9223372036854775806", "city2"],
["name3", "1234567890123456789", "city3"],
["name4", "0", "city4"],
["name5", "-9223372036854775808", "city5"],
]

csv_reader = CSVReader()

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (5, 3)
assert df["id"].dtype == "int64"
assert df.iloc[0]["id"] == 9223372036854775807
assert df.iloc[4]["id"] == -9223372036854775808


def test_csv_reader_int_typing():
csv_data = [
["Name", "id", "City"],
["name1", "0", "city1"],
["name2", "15", "city2"],
["name3", "123456789086", "city3"],
["name5", "45", "city5"],
]

csv_reader = CSVReader(options=CSVReaderOptions(column_data_types={"id": "int"}))

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (4, 3)
assert df["id"].dtype == "int64"


def test_csv_reader_float_typing():
csv_data = [
["Name", "score", "City"],
["name1", "0", "city1"],
["name2", "15.3", "city2"],
["name3", "45", "city3"],
["name5", "23.1342", "city5"],
]

csv_reader = CSVReader(
options=CSVReaderOptions(column_data_types={"score": "float"})
)

df = csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert df.shape == (4, 3)
assert df["score"].dtype == "float64"


def test_csv_reader_non_numeric_in_integer_column():
csv_data = [
["Name", "Age", "City"],
["name1", "abc", "city1"],
["name2", "25", "city2"],
]

csv_reader = CSVReader(options=CSVReaderOptions(column_data_types={"Age": "int"}))

with pytest.raises(DatabaseUploadFailed) as ex:
csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert (
str(ex.value) == "Non int value found in column 'Age'. Value: 'abc', line: 1."
)


def test_csv_reader_non_numeric_in_float_column():
csv_data = [
["Name", "Score", "City"],
["name1", "5.3", "city1"],
["name2", "25.5", "city2"],
["name3", "24.5", "city3"],
["name4", "1.0", "city4"],
["name5", "one point five", "city5"],
]

csv_reader = CSVReader(
options=CSVReaderOptions(column_data_types={"Score": "float"})
)

with pytest.raises(DatabaseUploadFailed) as ex:
csv_reader.file_to_dataframe(create_csv_file(csv_data))

assert (
str(ex.value)
== "Non float value found in column 'Score'. Value: 'one point five', line: 5."
)


def test_csv_reader_chunking_large_file():
"""Test that chunking is used for large files."""
# Create a large CSV with more than 100k rows
Expand Down
Loading