Skip to main content
Loads data from an AWS S3 bucket into Firebolt. Use COPY FROM to load data from external sources, such as from Amazon S3, into Firebolt tables. COPY FROM supports various data loading workflows, including schema discovery, metadata filtering, and parallel processing, offering flexibility for different use cases. This page provides an overview of the COPY FROM syntax, parameters, and best practices.

Syntax

When you authenticate with AWS_ROLE_ARN, set the optional AWS_ROLE_EXTERNAL_ID to add a customer-controlled condition to your role’s trust policy.
For role-based AWS access you can additionally set an external ID. An external ID is a value you choose and control that AWS checks when Firebolt assumes your role, adding a second condition on top of your account’s unique IAM principal. Configuring one is a recommended best practice. See IAM roles.

Parameters

Parameters for CSV files

Parameters for Parquet files

Best practice

Firebolt recommends using a LOCATION object to specify credentials. Location objects provide a secure, centralized way to manage Amazon S3 credentials and URLs. LOCATION objects let you specify credentials with the following:
  • Centralized credential management.
  • Reduced exposure of credentials in queries.
  • Role-based access control.
  • Simplified maintenance and updates.
For a comprehensive guide to LOCATION objects, see LOCATION objects. For detailed information, see CREATE LOCATION.

Settings to control behavior

  • insert_sharding to enforce partition locality during ingestion into partitioned tables.
  • tablet_min_size_bytes and tablet_max_size_bytes to control min/max tablet sizes during ingestion.
  • cross_region_request_mode to access data in cross-region Amazon S3 buckets.

Examples

COPY FROM with LOCATION

The following code example copies data from files matching the *.parquet pattern in the specified my_location into my_table:

Filter by metadata during loading

When loading data into tables, you can filter data using the following options:
  1. LIMIT: Restricts the number of rows loaded, which can be useful to preview or create sample datasets.
  2. OFFSET: Skips a specified number of rows in the final result set before ingestion. The OFFSET clause in COPY FROM behaves the same way as the OFFSET clause in SELECT queries behaves.
    • COPY FROM currently does not support the ORDER BY clause. Thus, using OFFSET may result in different outcomes every time you run the command.
    • Both LIMIT and OFFSET apply to the entire result set, not to individual files.
  3. WHERE: Filters data based on source file metadata, as follows:
    • $source_file_name - The full path of the source file in an Amazon S3 bucket, without the name of the bucket. For example, if your bucket is: s3://my_bucket/xyz/year=2018/month=01/part-00001.parquet, then $source_file_name is xyz/year=2018/month=01/part-00001.parquet.
    • $source_file_timestamp - The timestamp in UTC, to the second when the source file was last modified in an Amazon S3 bucket.
    • $source_file_size - The size in bytes of the source file.
    • $source_file_etag - The ETag of the file, often used for version control.
In the following code example, COPY FROM first reads all the files in the specified directory that were modified in the last three years. Then, it applies the offset and limit clause. As long as all source files modified in the last three years have at least 100 rows combined, the result set will have exactly 50 rows. It returns a table containing 50 rows of data that was modified in the last three years.
Recommended best practice The following code example builds on the previous example by using a LOCATION to store credentials to authenticate:

Use LOCATION to copy data

The following code example creates a table and uses a LOCATION object to copy Parquet files that match the specified pattern into it:

Load multiple directories with LOCATION

The following code example copies data from Parquet files matching a pattern in my_tournaments_location to the tournament_results table:

Load multiple files and directories in parallel

You can use COPY FROM to read multiple sources and from multiple directories into a single table, simultaneously. The following code example reads any file ending in .parquet from multiple directories into table_from_multiple_directories.
The following code example reads two CSV files into table_from_multiple_files:
In the previous example, there are two CSV files in the firebolt_sample_dataset folder: levels.csv and tournaments.csv. These files have a different schema. COPY FROM reads these files into a single table, and infers the schema from the first file. Any column mismatches are filled with NULL values.

Automatic schema discovery

You can use the automatic schema discovery feature in COPY FROM to handle even very large data sources instead of manually defining it. The following apply:
  • Parquet files - Firebolt automatically reads metadata in Parquet files to create corresponding target tables.
  • CSV files - Firebolt infers column types based on the data content itself, which can streamline the initial data loading process significantly. Use WITH HEADER=TRUE if your CSV file contains column names in the first line.
When loading multiple files, Firebolt infers the schema from the most recently modified file. The remaining files must have compatible data types. If types vary between files (e.g., a column contains integers in one file but doubles in another, or is numeric in one file but text in another), the inferred schema may not match all files and thus cause data type errors or query failures. In such cases, we recommend defining an explicit schema using either external tables or COPY FROM into existing tables.
Automatic schema discovery operates on a “best effort” basis, and attempts to balance accuracy with practical usability, but it may not always be error-free. The following query reads levels.csv, a sample dataset from the fictional “Ultra Fast Gaming Inc.” company. The example implicitly uses automatic schema creation with AUTO_CREATE=TRUE, which defaults to TRUE, and also triggers automatic table creation:
Use the following example code to display the table contents, ordered by the fourth column, followed by the fifth column:

Use PATTERN to insert data into an existing table

You can use the PATTERN feature, which uses regular expressions, to select several files that match the specified pattern to populate a target table. The following example uses the *.csv pattern to read all files ending in .csv into the pattern_target table:
In the previous example, there are two CSV files in the firebolt_sample_dataset folder: levels.csv and tournaments.csv. These files have a different schema. COPY FROM reads these files into a single table, and infers the schema from the first file. Any column mismatches are filled with NULL values.

Load metadata into a table

You can load metadata information about your source file into your table so that you can track the source name, timestamp, size, and etag information for each row. You can use the following metadata columns:
  • $source_file_name - The full path of the source file in an Amazon S3 bucket, without the name of the bucket. For example, if your bucket is: s3://my_bucket/xyz/year=2018/month=01/part-00001.parquet, then $source_file_name is xyz/year=2018/month=01/part-00001.parquet.
  • $source_file_timestamp - The timestamp in UTC, to the second when the source file was last modified in an Amazon S3 bucket.
  • $source_file_size - The size in bytes of the source file.
  • $source_file_etag - The ETag of the file, often used for version control.
The following code creates the levels table, and populates it with information from the LevelID column and the timestamp from the source data:
The following code example displays the contents of levels:
The first three rows of the sample output follow:

Column mapping

When loading data into a target table, you can manually map source and target schemas by column name or index position. Column mapping ensures that data from the source file is correctly inserted into the appropriate columns in the target table.
Handling case sensitivity
By default, column mapping in Firebolt is case-insensitive, meaning column names in the COPY FROM statement are matched without quotes and are treated as lowercase. The CASE_SENSITIVE_COLUMN_MAPPING parameter is ignored, and data loads into the table regardless of case differences. If you need to enforce case-sensitive column mapping, set the CASE_SENSITIVE_COLUMN_MAPPING parameter to TRUE. When enabled, column names must match exactly, including case, and COPY FROM will either fail or populate columns with NULL values if there is a case mismatch. You can also use quoted identifiers to preserve case-sensitive behavior.
Mapping by column name
Before mapping schemas and ingesting data, you must first create a target table. You can explicitly map source columns to target table columns by matching column names.
The following code example creates a table and maps columns by name:
This approach allows the column order to differ between the source file and target table, as long as the column names in the COPY FROM statement match exactly with the source file.
Mapping by index position
Instead of mapping by column name, you can reference column positions in the source file using $cN notation, where N represents the column index starting from one.
The following code example maps the first column in the source file to the id column in the target table and the fourth column to name:
This method is useful when column names do not match or when working with files that lack headers.
Mapping source columns and metadata
You can also map both source columns and metadata columns to store additional details about the ingested data. Metadata columns provide useful information such as file creation date, size, and last modified timestamp.
The following code example maps source file columns by name and includes metadata:
In the previous example:
  • Standard columns such as LevelID, NumberofLaps, and SceneDetails are mapped by name.
  • Metadata columns such as date_of_creation, file_name, file_last_modified, and file_size capture file-level information from the source file. This approach is particularly useful for auditing, tracking data lineage, or managing incremental loads.
Note that if a required column in the mapping is missing from the source file, the load will fail unless ALLOW_COLUMN_MISMATCH is enabled. For cases where column names may change or be missing in the source file, see the following section ALLOW_COLUMN_MISMATCH.

Allow column name mismatch

If you specify a column mapping during data loading, COPY FROM treats the columns listed in the <column_mapping> as required. If no column mapping is specified, the columns in the target table are considered required. To allow the data to continue loading when some required columns are missing from the source file, you can use ALLOW_COLUMN_MISMATCH, which is enabled by default. For example, if you create a table with LevelID2 and Name columns and attempt to load data from the levels.csv dataset, which lacks a LevelID2 column, COPY FROM will populate the Name column as specified and fill the LevelID2 column with NULL values. This allows flexible handling of missing data without raising errors, as shown in the following code example:

Load Parquet structs

The SUPPORT_STRUCTS parameter controls how structs in Parquet files are handled during schema inference.
  • When SUPPORT_STRUCTS=TRUE: Nested structures in the source data will be treated as STRUCT data types.
  • When SUPPORT_STRUCTS=FALSE: Nested structures in the source data will be treated as individual shredded columns.
SUPPORT_STRUCTS applies only during schema discovery: It controls the type inference process for structs. When working with predefined schemas: It has no effect, as the system maps incoming data to your defined schema.
Note that we do not fully support mixed cases with regular structs and shredded structs. Internally, we check if the header has at least one struct column. If it does, it will prioritize ingest into struct columns and will not attempt to match shredded columns. We recommend using a consistent schema design that follows either the struct-based or shredded column pattern.

Error handling

The following sections show you how to handle errors for both CSV and Parquet files.

Row-based errors in CSV

COPY FROM generates a row-based error when there’s a mismatch between source and target table columns. In the following example, the col_mismatch_csv table includes a LevelID2 column defined as NOT NULL, that does not exist in the source levels.csv table. It uses MAX_ERRORS_PER_FILE='0%', which causes the loading job to fail if there is a single error.
In the previous code example, when COPY FROM does not see the same column name in the target table col_mismatch_csv, it tries to fill the column with NULL values. Because LevelID2 is defined with a constraint that it cannot have NULL values, the query generates the following error: ERROR: The INSERT INTO statement failed because it tried to insert a NULL into the column LevelID2, which is NOT NULL. Please specify a value or redefine the column's logical constraints. and stops loading into the table.
Allow all row-based errors in CSV
You can change this behavior to allow errors. The following code example allows all errors, and the load job completes even if no data loads into the target table:
Column data type mismatch in CSV
If you try to load data into a column in an existing table that has a different data type than the source data, COPY FROM will attempt to cast the data into the specified data type. If the cast fails, COPY FROM generates an error. To demonstrate this error, the following example intentionally creates a table that defines the LevelID column incorrectly as an integer, instead of as text, and then attempts to copy data into it. It generates the following error: Line 1, Column 8: Unable to cast text 'Thunderbolt Circuit' to integer.
In the previous code example, the query generates an error because the default value for MAX_ERRORS_PER_FILE is 0. You can set MAX_ERRORS_PER_FILE to 100% to allow all errors, as shown in the following section.

Allow all errors, and write them to file

You can also allow all errors, so that the loading job continues until it has attempted to load all rows in your dataset. Firebolt can write these errors to an Amazon S3 bucket as CSV files. If your specified S3 bucket requires access credentials, you must specify them so that Firebolt can write the files on your behalf. Data rows that load without error are ingested in row order. A loading job that specifies writing error files will write files with the following syntax to your Amazon S3 bucket:
  • error_reasons.csv - An error file that contains all the reasons that a row generated an error, and also file-based errors.
  • rejected_rows.csv - An error file that contains all the rejected rows in row order.
Producing an error while reading Parquet files doesn’t generate row-based error files. On error, only a error_reasons.csv file is generated. The previous files will have an order appended to the name such as error_reasons_1.csv. The following code example allows all errors, provides credentials, and writes two error files to an Amazon S3 bucket:
To provide your credentials in the previous example, do the following:
  • Replace the <aws_access_key_id> with an AWS access key ID that is associated with an AWS user or AWS role. The AWS access key ID is a 20-character string such as AKIAIOSFODNN7EXAMPLE.
  • Replace the <aws_secret_access_key> with an AWS secret access key associated with an AWS user or IAM role associated with the AWS access key ID. The AWS secret access key is a 40-character string such as wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY.

Read errors from file

The previous COPY FROM example shows how to create two error files, one that describes the error reasons and one that contains the rows that had errors. This example shows how to load and view the contents of these files. The following code example reads all files that begin with error_reasons and end with .csv into an error_reasons table:
The following code returns the contents of the error_reasons table:
The following output shows an example of the contents of the error_reasons table: The following code reads all files that begin with rejected_rows and ends with .csv into a rejected_rows table:
Use SELECT to view the contents of a file. The following code returns the contents of the rejected_rows table:
The following output shows the contents of the rejected_rows table after running the previous SELECT statement:

Column mapping and default values

You can map a specific source column to a target column, and specify a default value to replace any NULL values generated during mapping. The following code example maps the LevelID column from the levels.csv sample dataset, into a column LevelID_team_A in a target_default_mapping table. It also maps a non-existent Country column in the levels.csv dataset, into LevelsID_team_B:
In the previous example, all rows under LevelsID_team_B will contain the value 50.

Type mismatch errors

If you read a column from a source file into a table with an incompatible data type, the mapping generates a casting error. A loading job that specifies writing error files will write files starting with the following prefixes to a specified Amazon S3 bucket:
  • error_reasons - An error file that contains all the reasons that a row generated an error, and also file-based errors.
  • rejected_rows - An error file that contains all the rejected rows in row order.
The following code uses the Firebolt sample players dataset which has a column PlayerID with a data type of INTEGER, and attempts to read it into an existing column with a DATE data type:
Use the following sample code to view a table that contains the contents of all error files that contain error_reasons:
The type mismatch error in this example creates a file-based error, which is an error that affects the entire file during processing. This query only produces a file-based error and does not produce the rejected_rows error file.