sqlglot.dialects.duckdb
1from __future__ import annotations 2 3 4from sqlglot import exp, tokens 5 6from sqlglot.dialects.dialect import ( 7 Dialect, 8 NormalizationStrategy, 9 UsingColumnOrder, 10) 11from sqlglot.generators.duckdb import DuckDBGenerator, WS_CONTROL_CHARS_TO_DUCK # noqa: F401 12from sqlglot.parsers.duckdb import DuckDBParser 13from sqlglot.tokens import TokenType 14from sqlglot.typing.duckdb import EXPRESSION_METADATA 15 16 17class DuckDB(Dialect): 18 NULL_ORDERING = "nulls_are_last" 19 SUPPORTS_USER_DEFINED_TYPES = True 20 INDEX_OFFSET = 1 21 CONCAT_COALESCE = True 22 CONCAT_WS_COALESCE = True 23 SUPPORTS_ORDER_BY_ALL = True 24 SUPPORTS_LIMIT_ALL = True 25 SUPPORTS_FIXED_SIZE_ARRAYS = True 26 TABLES_REFERENCEABLE_AS_COLUMNS = True 27 STRICT_JSON_PATH_SYNTAX = False 28 NUMBERS_CAN_BE_UNDERSCORE_SEPARATED = True 29 UUID_IS_STRING_TYPE = False 30 USING_COLUMN_ORDER = UsingColumnOrder.IN_PLACE 31 32 # https://duckdb.org/docs/sql/introduction.html#creating-a-new-table 33 NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_INSENSITIVE 34 ASCII_ONLY_NORMALIZATION = True 35 36 DATE_PART_MAPPING = { 37 **Dialect.DATE_PART_MAPPING, 38 "DAYOFWEEKISO": "ISODOW", 39 } 40 41 EXPRESSION_METADATA = EXPRESSION_METADATA.copy() 42 43 DATE_PART_MAPPING.pop("WEEKDAY") 44 45 INVERSE_TIME_MAPPING = { 46 "%e": "%-d", # BigQuery's space-padded day (%e) -> DuckDB's no-padding day (%-d) 47 "%:z": "%z", # In DuckDB %z can represent +/-HH:MM, +/-HHMM, or +/-HH. 48 "%-z": "%z", 49 "%f_zero": "%n", 50 "%f_one": "%n", 51 "%f_two": "%n", 52 "%f_three": "%g", 53 "%f_four": "%n", 54 "%f_five": "%n", 55 "%f_seven": "%n", 56 "%f_eight": "%n", 57 "%f_nine": "%n", 58 } 59 60 def to_json_path(self, path: exp.Expr | None) -> exp.Expr | None: 61 if isinstance(path, exp.Literal): 62 # DuckDB also supports the JSON pointer syntax, where every path starts with a `/`. 63 # Additionally, it allows accessing the back of lists using the `[#-i]` syntax. 64 # This check ensures we'll avoid trying to parse these as JSON paths, which can 65 # either result in a noisy warning or in an invalid representation of the path. 66 path_text = path.name 67 if path_text.startswith("/") or "[#" in path_text: 68 return path 69 70 return super().to_json_path(path) 71 72 UNESCAPED_SEQUENCES = {"\\a": "a", "\\v": "v"} 73 74 class Tokenizer(tokens.Tokenizer): 75 NUMERIC_ESCAPES = {"x": (16, 1, 2, 0xFF), "0": (8, 1, 3, 0o777)} 76 NUMERIC_ESCAPES_ARE_BYTES = True 77 DROP_UNKNOWN_ESCAPES = True 78 BYTE_STRINGS = [("e'", "'"), ("E'", "'")] 79 BYTE_STRING_ESCAPES = ["'", "\\"] 80 HEREDOC_STRINGS = ["$"] 81 82 HEREDOC_TAG_IS_IDENTIFIER = True 83 HEREDOC_STRING_ALTERNATIVE = TokenType.PARAMETER 84 85 KEYWORDS = { 86 **tokens.Tokenizer.KEYWORDS, 87 "//": TokenType.DIV, 88 "**": TokenType.DSTAR, 89 "^@": TokenType.CARET_AT, 90 "@>": TokenType.AT_GT, 91 "<@": TokenType.LT_AT, 92 "ATTACH": TokenType.ATTACH, 93 "BINARY": TokenType.VARBINARY, 94 "BITSTRING": TokenType.BIT, 95 "BPCHAR": TokenType.TEXT, 96 "CHAR": TokenType.TEXT, 97 "DATETIME": TokenType.TIMESTAMPNTZ, 98 "DETACH": TokenType.DETACH, 99 "FORCE": TokenType.FORCE, 100 "INSTALL": TokenType.INSTALL, 101 "INT8": TokenType.BIGINT, 102 "LOGICAL": TokenType.BOOLEAN, 103 "MACRO": TokenType.FUNCTION, 104 "ONLY": TokenType.ONLY, 105 "PIVOT_WIDER": TokenType.PIVOT, 106 "POSITIONAL": TokenType.POSITIONAL, 107 "RESET": TokenType.COMMAND, 108 "ROW": TokenType.STRUCT, 109 "SIGNED": TokenType.INT, 110 "STRING": TokenType.TEXT, 111 "SUMMARIZE": TokenType.SUMMARIZE, 112 "TIMESTAMP": TokenType.TIMESTAMPNTZ, 113 "TIMESTAMP_S": TokenType.TIMESTAMP_S, 114 "TIMESTAMP_MS": TokenType.TIMESTAMP_MS, 115 "TIMESTAMP_NS": TokenType.TIMESTAMP_NS, 116 "TIMESTAMP_US": TokenType.TIMESTAMP, 117 "UBIGINT": TokenType.UBIGINT, 118 "UINTEGER": TokenType.UINT, 119 "USMALLINT": TokenType.USMALLINT, 120 "UTINYINT": TokenType.UTINYINT, 121 "VARCHAR": TokenType.TEXT, 122 } 123 KEYWORDS.pop("/*+") 124 125 SINGLE_TOKENS = { 126 **tokens.Tokenizer.SINGLE_TOKENS, 127 "$": TokenType.PARAMETER, 128 } 129 130 VAR_SINGLE_TOKENS = {"$"} 131 132 COMMANDS = tokens.Tokenizer.COMMANDS - {TokenType.SHOW} 133 134 Parser = DuckDBParser 135 136 Generator = DuckDBGenerator
18class DuckDB(Dialect): 19 NULL_ORDERING = "nulls_are_last" 20 SUPPORTS_USER_DEFINED_TYPES = True 21 INDEX_OFFSET = 1 22 CONCAT_COALESCE = True 23 CONCAT_WS_COALESCE = True 24 SUPPORTS_ORDER_BY_ALL = True 25 SUPPORTS_LIMIT_ALL = True 26 SUPPORTS_FIXED_SIZE_ARRAYS = True 27 TABLES_REFERENCEABLE_AS_COLUMNS = True 28 STRICT_JSON_PATH_SYNTAX = False 29 NUMBERS_CAN_BE_UNDERSCORE_SEPARATED = True 30 UUID_IS_STRING_TYPE = False 31 USING_COLUMN_ORDER = UsingColumnOrder.IN_PLACE 32 33 # https://duckdb.org/docs/sql/introduction.html#creating-a-new-table 34 NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_INSENSITIVE 35 ASCII_ONLY_NORMALIZATION = True 36 37 DATE_PART_MAPPING = { 38 **Dialect.DATE_PART_MAPPING, 39 "DAYOFWEEKISO": "ISODOW", 40 } 41 42 EXPRESSION_METADATA = EXPRESSION_METADATA.copy() 43 44 DATE_PART_MAPPING.pop("WEEKDAY") 45 46 INVERSE_TIME_MAPPING = { 47 "%e": "%-d", # BigQuery's space-padded day (%e) -> DuckDB's no-padding day (%-d) 48 "%:z": "%z", # In DuckDB %z can represent +/-HH:MM, +/-HHMM, or +/-HH. 49 "%-z": "%z", 50 "%f_zero": "%n", 51 "%f_one": "%n", 52 "%f_two": "%n", 53 "%f_three": "%g", 54 "%f_four": "%n", 55 "%f_five": "%n", 56 "%f_seven": "%n", 57 "%f_eight": "%n", 58 "%f_nine": "%n", 59 } 60 61 def to_json_path(self, path: exp.Expr | None) -> exp.Expr | None: 62 if isinstance(path, exp.Literal): 63 # DuckDB also supports the JSON pointer syntax, where every path starts with a `/`. 64 # Additionally, it allows accessing the back of lists using the `[#-i]` syntax. 65 # This check ensures we'll avoid trying to parse these as JSON paths, which can 66 # either result in a noisy warning or in an invalid representation of the path. 67 path_text = path.name 68 if path_text.startswith("/") or "[#" in path_text: 69 return path 70 71 return super().to_json_path(path) 72 73 UNESCAPED_SEQUENCES = {"\\a": "a", "\\v": "v"} 74 75 class Tokenizer(tokens.Tokenizer): 76 NUMERIC_ESCAPES = {"x": (16, 1, 2, 0xFF), "0": (8, 1, 3, 0o777)} 77 NUMERIC_ESCAPES_ARE_BYTES = True 78 DROP_UNKNOWN_ESCAPES = True 79 BYTE_STRINGS = [("e'", "'"), ("E'", "'")] 80 BYTE_STRING_ESCAPES = ["'", "\\"] 81 HEREDOC_STRINGS = ["$"] 82 83 HEREDOC_TAG_IS_IDENTIFIER = True 84 HEREDOC_STRING_ALTERNATIVE = TokenType.PARAMETER 85 86 KEYWORDS = { 87 **tokens.Tokenizer.KEYWORDS, 88 "//": TokenType.DIV, 89 "**": TokenType.DSTAR, 90 "^@": TokenType.CARET_AT, 91 "@>": TokenType.AT_GT, 92 "<@": TokenType.LT_AT, 93 "ATTACH": TokenType.ATTACH, 94 "BINARY": TokenType.VARBINARY, 95 "BITSTRING": TokenType.BIT, 96 "BPCHAR": TokenType.TEXT, 97 "CHAR": TokenType.TEXT, 98 "DATETIME": TokenType.TIMESTAMPNTZ, 99 "DETACH": TokenType.DETACH, 100 "FORCE": TokenType.FORCE, 101 "INSTALL": TokenType.INSTALL, 102 "INT8": TokenType.BIGINT, 103 "LOGICAL": TokenType.BOOLEAN, 104 "MACRO": TokenType.FUNCTION, 105 "ONLY": TokenType.ONLY, 106 "PIVOT_WIDER": TokenType.PIVOT, 107 "POSITIONAL": TokenType.POSITIONAL, 108 "RESET": TokenType.COMMAND, 109 "ROW": TokenType.STRUCT, 110 "SIGNED": TokenType.INT, 111 "STRING": TokenType.TEXT, 112 "SUMMARIZE": TokenType.SUMMARIZE, 113 "TIMESTAMP": TokenType.TIMESTAMPNTZ, 114 "TIMESTAMP_S": TokenType.TIMESTAMP_S, 115 "TIMESTAMP_MS": TokenType.TIMESTAMP_MS, 116 "TIMESTAMP_NS": TokenType.TIMESTAMP_NS, 117 "TIMESTAMP_US": TokenType.TIMESTAMP, 118 "UBIGINT": TokenType.UBIGINT, 119 "UINTEGER": TokenType.UINT, 120 "USMALLINT": TokenType.USMALLINT, 121 "UTINYINT": TokenType.UTINYINT, 122 "VARCHAR": TokenType.TEXT, 123 } 124 KEYWORDS.pop("/*+") 125 126 SINGLE_TOKENS = { 127 **tokens.Tokenizer.SINGLE_TOKENS, 128 "$": TokenType.PARAMETER, 129 } 130 131 VAR_SINGLE_TOKENS = {"$"} 132 133 COMMANDS = tokens.Tokenizer.COMMANDS - {TokenType.SHOW} 134 135 Parser = DuckDBParser 136 137 Generator = DuckDBGenerator
Default NULL ordering method to use if not explicitly set.
Possible values: "nulls_are_small", "nulls_are_large", "nulls_are_last"
A NULL arg in CONCAT yields NULL by default, but in some dialects it yields an empty string.
A NULL arg in CONCAT_WS yields NULL by default, but in some dialects it is skipped.
Whether ORDER BY ALL is supported (expands to all the selected columns) as in DuckDB, Spark3/Databricks
Whether expressions such as x::INT[5] should be parsed as fixed-size array defs/casts e.g. in DuckDB. In dialects which don't support fixed size arrays such as Snowflake, this should be interpreted as a subscript/index operator.
Whether table names can be referenced as columns (treated as structs).
BigQuery allows tables to be referenced as columns in queries, automatically treating them as struct values containing all the table's columns.
For example, in BigQuery: SELECT t FROM my_table AS t -- Returns entire row as a struct
Whether failing to parse a JSON path expression using the JSONPath dialect will log a warning.
Whether number literals can include underscores for better readability
Where star expansion places the columns of a USING or NATURAL join.
Given a(a_id, k1, k2) and b(k2, b_id, k1), SELECT * FROM a JOIN b USING (k2, k1) returns:
USING_LIST:k2, k1, a_id, b_idLEFT_TABLE:k1, k2, a_id, b_idIN_PLACE:a_id, k1, k2, b_id
When join columns come first, this applies at every join: each USING join moves its columns ahead of all the columns to its left.
Specifies the strategy according to which identifiers should be normalized.
Whether identifiers are only normalized with respect to ASCII characters, e.g. Ä and
ä are different identifiers in DuckDB, but the same identifier in Spark.
61 def to_json_path(self, path: exp.Expr | None) -> exp.Expr | None: 62 if isinstance(path, exp.Literal): 63 # DuckDB also supports the JSON pointer syntax, where every path starts with a `/`. 64 # Additionally, it allows accessing the back of lists using the `[#-i]` syntax. 65 # This check ensures we'll avoid trying to parse these as JSON paths, which can 66 # either result in a noisy warning or in an invalid representation of the path. 67 path_text = path.name 68 if path_text.startswith("/") or "[#" in path_text: 69 return path 70 71 return super().to_json_path(path)
Mapping of an escaped sequence (\n) to its unescaped version (
).
Whether string literals support escape sequences (e.g. \n). Set by the metaclass based on the tokenizer's STRING_ESCAPES.
Whether byte string literals support escape sequences. Set by the metaclass based on the tokenizer's BYTE_STRING_ESCAPES.
Mapping of an identifier escape char (\) to its escaped version (\\). Set by the metaclass based on the tokenizer's IDENTIFIER_ESCAPES.
75 class Tokenizer(tokens.Tokenizer): 76 NUMERIC_ESCAPES = {"x": (16, 1, 2, 0xFF), "0": (8, 1, 3, 0o777)} 77 NUMERIC_ESCAPES_ARE_BYTES = True 78 DROP_UNKNOWN_ESCAPES = True 79 BYTE_STRINGS = [("e'", "'"), ("E'", "'")] 80 BYTE_STRING_ESCAPES = ["'", "\\"] 81 HEREDOC_STRINGS = ["$"] 82 83 HEREDOC_TAG_IS_IDENTIFIER = True 84 HEREDOC_STRING_ALTERNATIVE = TokenType.PARAMETER 85 86 KEYWORDS = { 87 **tokens.Tokenizer.KEYWORDS, 88 "//": TokenType.DIV, 89 "**": TokenType.DSTAR, 90 "^@": TokenType.CARET_AT, 91 "@>": TokenType.AT_GT, 92 "<@": TokenType.LT_AT, 93 "ATTACH": TokenType.ATTACH, 94 "BINARY": TokenType.VARBINARY, 95 "BITSTRING": TokenType.BIT, 96 "BPCHAR": TokenType.TEXT, 97 "CHAR": TokenType.TEXT, 98 "DATETIME": TokenType.TIMESTAMPNTZ, 99 "DETACH": TokenType.DETACH, 100 "FORCE": TokenType.FORCE, 101 "INSTALL": TokenType.INSTALL, 102 "INT8": TokenType.BIGINT, 103 "LOGICAL": TokenType.BOOLEAN, 104 "MACRO": TokenType.FUNCTION, 105 "ONLY": TokenType.ONLY, 106 "PIVOT_WIDER": TokenType.PIVOT, 107 "POSITIONAL": TokenType.POSITIONAL, 108 "RESET": TokenType.COMMAND, 109 "ROW": TokenType.STRUCT, 110 "SIGNED": TokenType.INT, 111 "STRING": TokenType.TEXT, 112 "SUMMARIZE": TokenType.SUMMARIZE, 113 "TIMESTAMP": TokenType.TIMESTAMPNTZ, 114 "TIMESTAMP_S": TokenType.TIMESTAMP_S, 115 "TIMESTAMP_MS": TokenType.TIMESTAMP_MS, 116 "TIMESTAMP_NS": TokenType.TIMESTAMP_NS, 117 "TIMESTAMP_US": TokenType.TIMESTAMP, 118 "UBIGINT": TokenType.UBIGINT, 119 "UINTEGER": TokenType.UINT, 120 "USMALLINT": TokenType.USMALLINT, 121 "UTINYINT": TokenType.UTINYINT, 122 "VARCHAR": TokenType.TEXT, 123 } 124 KEYWORDS.pop("/*+") 125 126 SINGLE_TOKENS = { 127 **tokens.Tokenizer.SINGLE_TOKENS, 128 "$": TokenType.PARAMETER, 129 } 130 131 VAR_SINGLE_TOKENS = {"$"} 132 133 COMMANDS = tokens.Tokenizer.COMMANDS - {TokenType.SHOW}
Inherited Members
- sqlglot.tokens.Tokenizer
- Tokenizer
- BIT_STRINGS
- HEX_STRINGS
- RAW_STRINGS
- UNICODE_STRINGS
- IDENTIFIERS
- QUOTES
- STRING_ESCAPES
- IDENTIFIER_ESCAPES
- STRING_ESCAPES_ALLOWED_IN_RAW_STRINGS
- LONE_SURROGATE_REPLACEMENT
- NESTED_COMMENTS
- DASH_COMMENT_REQUIRES_BOUNDARY
- COMMENTS_TERMINATE_AT_NEWLINE_ONLY
- HINT_START
- TOKENS_PRECEDING_HINT
- COMMAND_PREFIX_TOKENS
- NUMERIC_LITERALS
- NUMBERS_CAN_HAVE_DECIMALS
- COMMENTS
- dialect
- tokenize
- sql
- size
- tokens