Parameter markers
Description
Parameter markers are named or unnamed typed placeholder variables used to supply values from the API invoking the SQL statement.
Using parameter markers protects your code from SQL injection attacks since it clearly separates provided values from the SQL statements.
You cannot mix named and unnamed parameter markers in the same SQL statement.
Parameter markers can be used wherever literal values are allowed in the SQL syntax.
The marker syntax (:name or ?) does not declare a type. The type is the type of the
bound value:
- In SQL,
USINGbinds a constant expression. A string literal such as'ab'isSTRING. Use a typed literal (5L,DATE '2024-01-01') orCASTto choose another type. A CHAR or VARCHAR value without an explicit collation currently binds asSTRING. An explicitly collated value such asCAST('ab' AS CHAR(2) COLLATE UTF8_LCASE)keeps that CHAR/VARCHAR type. Cast the marker in the SQL text when you need CHAR/VARCHAR without relying on collation. - In Python, Scala, or Java
spark.sql(..., args), Spark infers the type from the host value. A Python or Javastr/StringisSTRING. Host args must be literal-like constants: literals, or array/map/struct constructors of them. AColumnsuch aslit('ab').cast('CHAR(2)')is rejected withINVALID_SQL_ARG.
To convert after binding, wrap the marker in the SQL text: CAST(:p AS CHAR(2)) or
CAST(? AS INT). By default those CHAR/VARCHAR casts are STRING; they stay
CHAR/VARCHAR when types are first-class. See
Character String Types.
To parameterize identifiers (such as table or column names), use the IDENTIFIER clause.
Parameter markers can be provided by:
- Python using its pyspark.sql.SparkSession.sql() API.
- Scala using its org.apache.spark.sql.SparkSession.sql() API.
- Java using its org.apache.spark.sql.SparkSession.sql() API.
- SQL using the
EXECUTE IMMEDIATEstatement.
Named parameter markers
Named parameter markers are typed placeholder variables. The API invoking the SQL statement must supply name-value pairs to associate each parameter marker with a value.
Syntax
:parameter_name
Parameters
-
named_parameter_name
A reference to a supplied parameter marker in form of an unqualified identifier.
Notes
You can reference the same parameter marker multiple times within the same SQL Statement.
If no value has been bound to the parameter marker an UNBOUND_SQL_PARAMETER error is raised.
You are not required to reference all supplied parameter markers.
The mandatory preceding : (colon) differentiates the namespace of named parameter markers from that of column names and SQL parameters.
Type examples
> EXECUTE IMMEDIATE 'SELECT TYPEOF(:p)' USING 'ab' AS p;
string
> SET spark.sql.charVarchar.standardSemantics.enabled = true;
> EXECUTE IMMEDIATE 'SELECT TYPEOF(:p)' USING CAST('ab' AS CHAR(2)) AS p;
string
> EXECUTE IMMEDIATE 'SELECT TYPEOF(:p)' USING CAST('ab' AS CHAR(2) COLLATE UTF8_LCASE) AS p;
char(2) collate UTF8_LCASE
> EXECUTE IMMEDIATE 'SELECT TYPEOF(CAST(:p AS CHAR(2)))' USING 'ab' AS p;
char(2)
spark.sql("SELECT TYPEOF(:p)", args={"p": "ab"}).first()[0]
# 'string'
spark.sql("SET spark.sql.charVarchar.standardSemantics.enabled = true")
spark.sql("SELECT TYPEOF(CAST(:p AS CHAR(2)))", args={"p": "ab"}).first()[0]
# 'char(2)'
Examples
The following example defines two parameter markers:
- later: An
INTERVAL HOURwith value 3. - x: A
DOUBLEwith value 15.0
x is referenced multiple times, while later is referenced once.
-
SQL
> DECLARE stmtStr = 'SELECT current_timestamp() + :later, :x * :x AS square'; > EXECUTE IMMEDIATE stmtStr USING INTERVAL '3' HOURS AS later, 15.0 AS x; 2024-01-19 16:17:16.692303 225.00 -
Scala
import org.apache.spark.sql.SparkSession val spark = SparkSession .builder() .appName("Spark named parameter marker example") .getOrCreate() val argMap = Map("later" -> java.time.Duration.ofHours(3), "x" -> 15.0) spark.sql( sqlText = "SELECT current_timestamp() + :later, :x * :x AS square", args = argMap).show() // +----------------------------------------+------+ // |current_timestamp() + INTERVAL '03' HOUR|square| // +----------------------------------------+------+ // | 2023-02-27 17:48:...|225.00| // +----------------------------------------+------+ -
Java
import org.apache.spark.sql.*; import static java.util.Map.entry; SparkSession spark = SparkSession .builder() .appName("Java Spark named parameter marker example") .getOrCreate(); Map<String, String> argMap = Map.ofEntries( entry("later", java.time.Duration.ofHours(3)), entry("x", 15.0) ); spark.sql( sqlText = "SELECT current_timestamp() + :later, :x * :x AS square", args = argMap).show(); // +----------------------------------------+------+ // |current_timestamp() + INTERVAL '03' HOUR|square| // +----------------------------------------+------+ // | 2023-02-27 17:48:...|225.00| // +----------------------------------------+------+ -
Python
spark.sql("SELECT :x * :y * :z AS volume", args = { "x" : 3, "y" : 4, "z" : 5 }).show() // +------+ // |volume| // +------+ // | 60| // +------+
Unnamed parameter markers
Unnamed parameter markers are typed placeholder variables. The API invoking the SQL statement must supply an array of arguments to associate each parameter marker with a value in the order in which they appear.
Syntax
?
Parameters
?: A reference to a supplied parameter marker in form of a question mark.
Notes
Each occurrence of an unnamed parameter marker consumes a value provided by the API invoking the SQL statement in order.
If no value has been bound to the parameter marker, an UNBOUND_SQL_PARAMETER error is raised.
You are not required to consume all provided values.
The type rules are the same as for named markers: bind a typed USING expression, or
CAST the ? in the statement. A CHAR or VARCHAR USING value without an explicit
collation currently binds as STRING; an explicitly collated CHAR or VARCHAR value
keeps that type. Host spark.sql(..., args) values must be literal-like constants;
they cannot be a cast Column.
> EXECUTE IMMEDIATE 'SELECT TYPEOF(?)' USING 'ab';
string
> SET spark.sql.charVarchar.standardSemantics.enabled = true;
> EXECUTE IMMEDIATE 'SELECT TYPEOF(?)' USING CAST('ab' AS CHAR(2));
string
> EXECUTE IMMEDIATE 'SELECT TYPEOF(?)' USING CAST('ab' AS CHAR(2) COLLATE UTF8_LCASE);
char(2) collate UTF8_LCASE
> EXECUTE IMMEDIATE 'SELECT TYPEOF(CAST(? AS CHAR(2)))' USING 'ab';
char(2)
Examples
The following example defines three parameter markers:
- An
INTERVAL HOURwith value 3. - Two
DOUBLEwith value 15.0 each.
Since the parameters are unnamed each provided value is consumed by at most one parameter.
-
SQL
DECLARE stmtStr = 'SELECT current_timestamp() + ?, ? * ? AS square'; EXECUTE IMMEDIATE stmtStr USING INTERVAL '3' HOURS, 15.0, 15.0; 2024-01-19 16:17:16.692303 225.00 -
Scala
import org.apache.spark.sql.SparkSession val spark = SparkSession .builder() .appName("Spark unnamed parameter marker example") .getOrCreate() val argArr = Array(java.time.Duration.ofHours(3), 15.0, 15.0) spark.sql( sqlText = "SELECT current_timestamp() + ?, ? * ? AS square", args = argArr).show() // +----------------------------------------+------+ // |current_timestamp() + INTERVAL '03' HOUR|square| // +----------------------------------------+------+ // | 2023-02-27 17:48:...|225.00| // +----------------------------------------+------+ -
Java
import org.apache.spark.sql.*; SparkSession spark = SparkSession .builder() .appName("Java Spark unnamed parameter marker example") .getOrCreate(); Object[] argArr = new Object[] { java.time.Duration.ofHours(3), 15.0, 15.0 } spark.sql( sqlText = "SELECT current_timestamp() + ?, ? * ? AS square", args = argArr).show(); // +----------------------------------------+------+ // |current_timestamp() + INTERVAL '03' HOUR|square| // +----------------------------------------+------+ // | 2023-02-27 17:48:...|225.00| // +----------------------------------------+------+ -
Python
spark.sql("SELECT ? * ? * ? AS volume", args = [ 3, 4, 5 ]).show() // +------+ // |volume| // +------+ // | 60| // +------+