feat(spanner): add Google Cloud Spanner support
This commit is contained in:
1 parent
2cc8d78e64
commit
f494f90c30
74 files changed
+2920
-53
No files matched your search
@@ -63,6 +63,11 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
|
||||
| DatabaseType::Sqlite
|
||||
| DatabaseType::DuckDb
|
||||
| DatabaseType::Iris
|
||||
// Spanner supports named schemas; the PostgreSQL dialect defaults to `public`.
|
||||
// GoogleSQL's default schema is the empty string, which the blank-schema filters
|
||||
// in `qualified_table_name` / `table_data_qualified_table_name` drop along with
|
||||
// the dot separator (`` `s`.`t` `` with an empty `s` is a Spanner syntax error).
|
||||
| DatabaseType::Spanner
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -670,9 +670,8 @@ pub fn profile_for(db_type: DatabaseType) -> DdlDialectProfile {
|
||||
ClickHouse => clickhouse_profile(),
|
||||
|
||||
DuckDb | Questdb | SapHana | Teradata | Snowflake | Trino | PrestoSql | Hive | Kyuubi | Impala | Spark
|
||||
| Db2 | Informix | Bigquery | Kylin | Ignite | Ignite3 | Oscar | Tdengine | Iotdb | Databricks | Jdbc => {
|
||||
conservative_ansi(db_type)
|
||||
}
|
||||
| Db2 | Informix | Bigquery | Spanner | Kylin | Ignite | Ignite3 | Oscar | Tdengine | Iotdb | Databricks
|
||||
| Jdbc => conservative_ansi(db_type),
|
||||
|
||||
// Non-tabular / not applicable for relational CREATE TABLE
|
||||
Redis | MongoDb | DynamoDb | Elasticsearch | Easysearch | Meilisearch | Qdrant | Milvus | Weaviate
|
||||
|
||||
@@ -136,6 +136,11 @@ pub fn quote_table_identifier(database_type: Option<DatabaseType>, name: &str) -
|
||||
| DatabaseType::Tdengine
|
||||
| DatabaseType::Access
|
||||
| DatabaseType::Bigquery
|
||||
// GoogleSQL (Spanner's default dialect) quotes identifiers with backticks;
|
||||
// double quotes are string literals there, so `SELECT * FROM "users"` is a
|
||||
// syntax error. PostgreSQL-dialect Spanner databases override this through
|
||||
// the identifier quote reported by the agent on connect.
|
||||
| DatabaseType::Spanner
|
||||
| DatabaseType::Questdb,
|
||||
) => {
|
||||
format!("`{}`", name.replace('`', "``"))
|
||||
|
||||
@@ -385,6 +385,11 @@ pub(crate) fn uses_connection_identifier_quote(
|
||||
identifier_quote: Option<&str>,
|
||||
) -> bool {
|
||||
database_type == Some(DatabaseType::Kingbase)
|
||||
// Spanner is dual-dialect: GoogleSQL uses backticks, the PostgreSQL dialect uses
|
||||
// double quotes, and only the connected agent knows which. Unconditional like
|
||||
// Kingbase — when no quote was reported the callers fall back to
|
||||
// `quote_table_identifier`, whose static mapping is GoogleSQL-correct.
|
||||
|| database_type == Some(DatabaseType::Spanner)
|
||||
|| (database_type == Some(DatabaseType::Informix) && identifier_quote.is_some())
|
||||
|| (matches!(database_type, Some(DatabaseType::Gaussdb | DatabaseType::OpenGauss | DatabaseType::Postgres))
|
||||
&& identifier_quote.is_some())
|
||||
|
||||
@@ -34,6 +34,37 @@ fn quotes_identifiers_by_database_type() {
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Jdbc), "users_1"), "users_1");
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Jdbc), "user name"), "user name");
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Iotdb), "root.test.device2"), "root.test.device2");
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Spanner), "user`name"), "`user``name`");
|
||||
}
|
||||
|
||||
/// Spanner databases are created in one of two immutable dialects. The connected
|
||||
/// agent reports the correct identifier quote; when it is missing the static mapping
|
||||
/// must fall back to GoogleSQL (backticks), because GoogleSQL treats double quotes as
|
||||
/// string literals and would reject `SELECT * FROM "users"`.
|
||||
#[test]
|
||||
fn quotes_spanner_identifiers_by_connection_dialect() {
|
||||
// GoogleSQL dialect: agent reports a backtick.
|
||||
assert_eq!(quote_table_data_identifier(Some(DatabaseType::Spanner), "order", Some("`")), "`order`");
|
||||
assert_eq!(quote_table_data_identifier(Some(DatabaseType::Spanner), "user`name", Some("`")), "`user``name`");
|
||||
|
||||
// PostgreSQL dialect: agent reports a double quote.
|
||||
assert_eq!(quote_table_data_identifier(Some(DatabaseType::Spanner), "order", Some("\"")), "\"order\"");
|
||||
assert_eq!(quote_table_data_identifier(Some(DatabaseType::Spanner), "user\"name", Some("\"")), "\"user\"\"name\"");
|
||||
|
||||
// No quote reported (metadata probe failed / caller outside the desktop store):
|
||||
// fall back to the GoogleSQL default rather than the ANSI double quote.
|
||||
assert_eq!(quote_table_data_identifier(Some(DatabaseType::Spanner), "order", None), "`order`");
|
||||
|
||||
// GoogleSQL's default schema is the empty string and must not produce `` ``.`t` ``,
|
||||
// which Spanner rejects with `Invalid empty identifier`.
|
||||
assert_eq!(
|
||||
table_data_qualified_table_name(Some(DatabaseType::Spanner), Some(""), "singers", Some("`")),
|
||||
"`singers`"
|
||||
);
|
||||
assert_eq!(
|
||||
table_data_qualified_table_name(Some(DatabaseType::Spanner), Some("public"), "singers", Some("\"")),
|
||||
"\"public\".\"singers\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -82,6 +113,11 @@ fn qualifies_schema_only_for_schema_aware_databases() {
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Informix), Some("xtdpcky"), "users"), "xtdpcky.users");
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Sqlite), Some("analytics"), "users"), "\"analytics\".\"users\"");
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Jdbc), Some("cbsdw_dwd"), "dwd_test_df"), "dwd_test_df");
|
||||
// GoogleSQL's default schema is the empty string: the qualifier (and its dot) must be
|
||||
// dropped entirely, otherwise Spanner reports `Invalid empty identifier`.
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Spanner), Some(""), "users"), "`users`");
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Spanner), None, "users"), "`users`");
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Spanner), Some("public"), "users"), "`public`.`users`");
|
||||
assert_eq!(qualified_table_name(Some(DatabaseType::Iotdb), Some("root.test"), "device2"), "root.test.device2");
|
||||
assert_eq!(
|
||||
qualified_table_name(Some(DatabaseType::Iotdb), Some("root.test"), "root.test.device2"),
|
||||
@@ -127,6 +163,8 @@ fn maps_table_pagination_strategy_by_database_type() {
|
||||
TablePaginationStrategy::Unbounded
|
||||
);
|
||||
assert_eq!(table_pagination_strategy(Some(DatabaseType::Jdbc)), TablePaginationStrategy::AgentMaxRows);
|
||||
// Both Spanner dialects support `LIMIT n OFFSET m`; pin the fallback.
|
||||
assert_eq!(table_pagination_strategy(Some(DatabaseType::Spanner)), TablePaginationStrategy::LimitOffset);
|
||||
assert_eq!(table_pagination_strategy(None), TablePaginationStrategy::LimitOffset);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user