fix(yashandb): keep trigger blocks together

This commit is contained in:
t8y2 committed 2026-06-24 16:58:47 +08:00
1 parent 95137b70a6
commit b56d680cbe
1 file changed
+218 -47
+218 -47
View File
@@ -55,32 +55,99 @@ pub struct SqlFileImportStatement {
pub source_statement_count: usize, pub source_statement_count: usize,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SqlDialectProfile {
supports_hash_line_comments: bool,
supports_oracle_plsql_blocks: bool,
supports_slash_line_block_delimiter: bool,
supports_custom_delimiter_commands: bool,
supports_dollar_quoted_strings: bool,
supports_go_batch_separator: bool,
keeps_sqlserver_module_batch_at_cursor: bool,
}
impl Default for SqlDialectProfile {
fn default() -> Self {
Self {
supports_hash_line_comments: false,
supports_oracle_plsql_blocks: false,
supports_slash_line_block_delimiter: false,
supports_custom_delimiter_commands: true,
supports_dollar_quoted_strings: true,
supports_go_batch_separator: false,
keeps_sqlserver_module_batch_at_cursor: false,
}
}
}
impl SqlDialectProfile {
fn for_database_type(db_type: DatabaseType) -> Self {
if Self::is_oracle_like_database(db_type) {
return Self::oracle_like();
}
if matches!(db_type, DatabaseType::SqlServer) {
return Self::sql_server();
}
if Self::is_mysql_compatible_database(db_type) {
return Self::mysql_compatible();
}
Self::default()
}
fn mysql_compatible() -> Self {
Self { supports_hash_line_comments: true, ..Self::default() }
}
fn oracle_like() -> Self {
Self { supports_oracle_plsql_blocks: true, supports_slash_line_block_delimiter: true, ..Self::default() }
}
fn sql_server() -> Self {
Self { supports_go_batch_separator: true, keeps_sqlserver_module_batch_at_cursor: true, ..Self::default() }
}
fn is_mysql_compatible_database(db_type: DatabaseType) -> bool {
matches!(
db_type,
DatabaseType::Mysql
| DatabaseType::Doris
| DatabaseType::StarRocks
| DatabaseType::ManticoreSearch
| DatabaseType::Goldendb
)
}
fn is_oracle_like_database(db_type: DatabaseType) -> bool {
matches!(
db_type,
DatabaseType::Oracle
| DatabaseType::Dameng
| DatabaseType::Gaussdb
| DatabaseType::Yashandb
| DatabaseType::OceanbaseOracle
)
}
}
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct SqlParsingOptions { pub struct SqlParsingOptions {
pub supports_hash_line_comments: bool, profile: SqlDialectProfile,
pub supports_oracle_plsql_blocks: bool,
} }
impl SqlParsingOptions { impl SqlParsingOptions {
pub fn for_database_type(db_type: DatabaseType) -> Self { pub fn for_database_type(db_type: DatabaseType) -> Self {
Self { Self::from_profile(SqlDialectProfile::for_database_type(db_type))
supports_hash_line_comments: matches!(
db_type,
DatabaseType::Mysql
| DatabaseType::Doris
| DatabaseType::StarRocks
| DatabaseType::ManticoreSearch
| DatabaseType::Goldendb
),
supports_oracle_plsql_blocks: matches!(
db_type,
DatabaseType::Oracle | DatabaseType::Dameng | DatabaseType::Gaussdb | DatabaseType::OceanbaseOracle
),
}
} }
pub fn mysql_compatible() -> Self { pub fn mysql_compatible() -> Self {
Self { supports_hash_line_comments: true, ..Self::default() } Self::from_profile(SqlDialectProfile::mysql_compatible())
}
fn from_profile(profile: SqlDialectProfile) -> Self {
Self { profile }
} }
} }
@@ -220,7 +287,7 @@ impl SqlStatementSplitter {
i += 1; i += 1;
continue; continue;
} }
if self.options.supports_hash_line_comments && ch == '#' { if self.options.profile.supports_hash_line_comments && ch == '#' {
self.in_line_comment = true; self.in_line_comment = true;
self.buffer.push(ch); self.buffer.push(ch);
self.previous = Some(ch); self.previous = Some(ch);
@@ -234,7 +301,13 @@ impl SqlStatementSplitter {
i += 1; i += 1;
continue; continue;
} }
if let Some(tag) = dollar_quote_tag_at(&chars, i) { if let Some(tag) = self
.options
.profile
.supports_dollar_quoted_strings
.then(|| dollar_quote_tag_at(&chars, i))
.flatten()
{
if self.custom_delimiter.is_none() && !self.on_delimiter_line() { if self.custom_delimiter.is_none() && !self.on_delimiter_line() {
for tag_ch in tag.chars() { for tag_ch in tag.chars() {
self.buffer.push(tag_ch); self.buffer.push(tag_ch);
@@ -263,7 +336,8 @@ impl SqlStatementSplitter {
';' if !self.in_single_quote && !self.in_double_quote && !self.in_backtick => { ';' if !self.in_single_quote && !self.in_double_quote && !self.in_backtick => {
if self.custom_delimiter.is_some() { if self.custom_delimiter.is_some() {
self.buffer.push(ch); self.buffer.push(ch);
} else if self.options.supports_oracle_plsql_blocks && starts_with_oracle_plsql_block(&self.buffer) } else if self.options.profile.supports_oracle_plsql_blocks
&& starts_with_oracle_plsql_block(&self.buffer)
{ {
self.buffer.push(ch); self.buffer.push(ch);
if oracle_plsql_block_is_complete(&self.buffer) { if oracle_plsql_block_is_complete(&self.buffer) {
@@ -281,7 +355,7 @@ impl SqlStatementSplitter {
let buf_end = self.buffer.len() - 1; let buf_end = self.buffer.len() - 1;
let last_line_start = self.buffer[..buf_end].rfind('\n').map_or(0, |p| p + 1); let last_line_start = self.buffer[..buf_end].rfind('\n').map_or(0, |p| p + 1);
let last_line = self.buffer[last_line_start..buf_end].trim(); let last_line = self.buffer[last_line_start..buf_end].trim();
if self.options.supports_oracle_plsql_blocks && last_line == "/" { if self.options.profile.supports_slash_line_block_delimiter && last_line == "/" {
let before = self.buffer[..last_line_start].trim(); let before = self.buffer[..last_line_start].trim();
if has_executable_sql_with_options(before, self.options) { if has_executable_sql_with_options(before, self.options) {
statements.push(before.to_string()); statements.push(before.to_string());
@@ -291,7 +365,13 @@ impl SqlStatementSplitter {
i += 1; i += 1;
continue; continue;
} }
if let Some(new_delim) = parse_delimiter_command(last_line) { if let Some(new_delim) = self
.options
.profile
.supports_custom_delimiter_commands
.then(|| parse_delimiter_command(last_line))
.flatten()
{
self.custom_delimiter = if new_delim == ";" { None } else { Some(new_delim.to_string()) }; self.custom_delimiter = if new_delim == ";" { None } else { Some(new_delim.to_string()) };
if last_line_start > 0 { if last_line_start > 0 {
let before = self.buffer[..last_line_start].trim(); let before = self.buffer[..last_line_start].trim();
@@ -324,13 +404,13 @@ impl SqlStatementSplitter {
let mut statements = Vec::new(); let mut statements = Vec::new();
let trimmed = self.buffer.trim(); let trimmed = self.buffer.trim();
let last_line = trimmed.rsplit('\n').next().unwrap_or(trimmed).trim(); let last_line = trimmed.rsplit('\n').next().unwrap_or(trimmed).trim();
if parse_delimiter_command(last_line).is_some() { if self.options.profile.supports_custom_delimiter_commands && parse_delimiter_command(last_line).is_some() {
let before = trimmed.rsplit_once('\n').map(|x| x.0).unwrap_or("").trim(); let before = trimmed.rsplit_once('\n').map(|x| x.0).unwrap_or("").trim();
if has_executable_sql_with_options(before, self.options) { if has_executable_sql_with_options(before, self.options) {
statements.push(before.to_string()); statements.push(before.to_string());
} }
self.buffer.clear(); self.buffer.clear();
} else if self.options.supports_oracle_plsql_blocks && last_line == "/" { } else if self.options.profile.supports_slash_line_block_delimiter && last_line == "/" {
let before = trimmed.rsplit_once('\n').map(|x| x.0).unwrap_or("").trim(); let before = trimmed.rsplit_once('\n').map(|x| x.0).unwrap_or("").trim();
if has_executable_sql_with_options(before, self.options) { if has_executable_sql_with_options(before, self.options) {
statements.push(before.to_string()); statements.push(before.to_string());
@@ -474,12 +554,12 @@ fn split_statement_range_at_blank_lines(
statement: &SqlStatementRange, statement: &SqlStatementRange,
options: SqlParsingOptions, options: SqlParsingOptions,
) -> Vec<SqlStatementRange> { ) -> Vec<SqlStatementRange> {
if options.supports_oracle_plsql_blocks && starts_with_oracle_plsql_block(&statement.text) { if options.profile.supports_oracle_plsql_blocks && starts_with_oracle_plsql_block(&statement.text) {
return vec![statement.clone()]; return vec![statement.clone()];
} }
let mut ranges = Vec::new(); let mut ranges = Vec::new();
let mut scanner = SqlScanner::default(); let mut scanner = SqlScanner::with_profile(options.profile);
let mut current_start = statement.start; let mut current_start = statement.start;
let mut line_start = statement.start; let mut line_start = statement.start;
let mut line_has_non_whitespace = false; let mut line_has_non_whitespace = false;
@@ -590,7 +670,7 @@ fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions
i += 2; i += 2;
continue; continue;
} }
if options.supports_hash_line_comments && ch == '#' { if options.profile.supports_hash_line_comments && ch == '#' {
in_line_comment = true; in_line_comment = true;
i += ch.len_utf8(); i += ch.len_utf8();
continue; continue;
@@ -600,7 +680,9 @@ fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions
i += 2; i += 2;
continue; continue;
} }
if let Some(tag) = dollar_quote_tag_at_str(sql, i) { if let Some(tag) =
options.profile.supports_dollar_quoted_strings.then(|| dollar_quote_tag_at_str(sql, i)).flatten()
{
if custom_delimiter.is_none() && !is_on_delimiter_line(sql, start, i) { if custom_delimiter.is_none() && !is_on_delimiter_line(sql, start, i) {
i += tag.len(); i += tag.len();
dollar_quote_tag = Some(tag); dollar_quote_tag = Some(tag);
@@ -610,13 +692,15 @@ fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions
if ch == '\n' { if ch == '\n' {
let line_start = sql[..i].rfind('\n').map_or(0, |pos| pos + 1); let line_start = sql[..i].rfind('\n').map_or(0, |pos| pos + 1);
let line = sql[line_start..i].trim(); let line = sql[line_start..i].trim();
if options.supports_oracle_plsql_blocks && line == "/" { if options.profile.supports_slash_line_block_delimiter && line == "/" {
push_statement_range(&mut ranges, sql, start, line_start, options); push_statement_range(&mut ranges, sql, start, line_start, options);
start = i + ch.len_utf8(); start = i + ch.len_utf8();
i = start; i = start;
continue; continue;
} }
if let Some(new_delimiter) = parse_delimiter_command(line) { if let Some(new_delimiter) =
options.profile.supports_custom_delimiter_commands.then(|| parse_delimiter_command(line)).flatten()
{
let before = sql[start..line_start].trim(); let before = sql[start..line_start].trim();
if has_executable_sql_with_options(before, options) { if has_executable_sql_with_options(before, options) {
push_statement_range(&mut ranges, sql, start, line_start, options); push_statement_range(&mut ranges, sql, start, line_start, options);
@@ -644,7 +728,7 @@ fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions
} }
';' if !in_single_quote && !in_double_quote && !in_backtick && custom_delimiter.is_none() => { ';' if !in_single_quote && !in_double_quote && !in_backtick && custom_delimiter.is_none() => {
let is_oracle_plsql = let is_oracle_plsql =
options.supports_oracle_plsql_blocks && starts_with_oracle_plsql_block(&sql[start..i]); options.profile.supports_oracle_plsql_blocks && starts_with_oracle_plsql_block(&sql[start..i]);
if is_oracle_plsql { if is_oracle_plsql {
if !oracle_plsql_block_is_complete(&sql[start..i + ch.len_utf8()]) { if !oracle_plsql_block_is_complete(&sql[start..i + ch.len_utf8()]) {
i += ch.len_utf8(); i += ch.len_utf8();
@@ -674,11 +758,11 @@ fn split_sql_statement_ranges_with_options(sql: &str, options: SqlParsingOptions
let trimmed = sql[start..].trim(); let trimmed = sql[start..].trim();
let last_line = trimmed.rsplit('\n').next().unwrap_or(trimmed).trim(); let last_line = trimmed.rsplit('\n').next().unwrap_or(trimmed).trim();
if parse_delimiter_command(last_line).is_some() { if options.profile.supports_custom_delimiter_commands && parse_delimiter_command(last_line).is_some() {
if let Some(line_start) = sql[start..].rfind('\n').map(|pos| start + pos + 1) { if let Some(line_start) = sql[start..].rfind('\n').map(|pos| start + pos + 1) {
push_statement_range(&mut ranges, sql, start, line_start, options); push_statement_range(&mut ranges, sql, start, line_start, options);
} }
} else if options.supports_oracle_plsql_blocks && last_line == "/" { } else if options.profile.supports_slash_line_block_delimiter && last_line == "/" {
if let Some(line_start) = sql[start..].rfind('\n').map(|pos| start + pos + 1) { if let Some(line_start) = sql[start..].rfind('\n').map(|pos| start + pos + 1) {
push_statement_range(&mut ranges, sql, start, line_start, options); push_statement_range(&mut ranges, sql, start, line_start, options);
} }
@@ -768,7 +852,7 @@ fn dollar_quote_tag_at_str(sql: &str, index: usize) -> Option<String> {
} }
pub fn split_sql_batches(sql: &str) -> Vec<String> { pub fn split_sql_batches(sql: &str) -> Vec<String> {
let ranges = split_sql_batch_ranges(sql); let ranges = split_sql_batch_ranges(sql, SqlDialectProfile::sql_server());
if ranges.is_empty() { if ranges.is_empty() {
let trimmed = sql.trim(); let trimmed = sql.trim();
return if trimmed.is_empty() { Vec::new() } else { vec![trimmed.to_string()] }; return if trimmed.is_empty() { Vec::new() } else { vec![trimmed.to_string()] };
@@ -776,7 +860,7 @@ pub fn split_sql_batches(sql: &str) -> Vec<String> {
ranges.into_iter().map(|range| range.text).collect() ranges.into_iter().map(|range| range.text).collect()
} }
fn split_sql_batch_ranges(sql: &str) -> Vec<SqlStatementRange> { fn split_sql_batch_ranges(sql: &str, profile: SqlDialectProfile) -> Vec<SqlStatementRange> {
let mut batches = Vec::new(); let mut batches = Vec::new();
let mut current_start = 0; let mut current_start = 0;
let lines: Vec<&str> = sql.split('\n').collect(); let lines: Vec<&str> = sql.split('\n').collect();
@@ -788,8 +872,9 @@ fn split_sql_batch_ranges(sql: &str) -> Vec<SqlStatementRange> {
offset = line_end + 1; // +1 for the '\n' offset = line_end + 1; // +1 for the '\n'
let trimmed = line.trim(); let trimmed = line.trim();
if trimmed.eq_ignore_ascii_case("go") if profile.supports_go_batch_separator
|| trimmed.to_ascii_lowercase().starts_with("go ") && trimmed[2..].trim().is_empty() && (trimmed.eq_ignore_ascii_case("go")
|| trimmed.to_ascii_lowercase().starts_with("go ") && trimmed[2..].trim().is_empty())
{ {
push_batch_range(&mut batches, sql, current_start, line_start); push_batch_range(&mut batches, sql, current_start, line_start);
current_start = line_end.min(sql.len()); current_start = line_end.min(sql.len());
@@ -817,12 +902,13 @@ fn push_batch_range(ranges: &mut Vec<SqlStatementRange>, sql: &str, start: usize
} }
fn find_sqlserver_statement_at_cursor(sql: &str, cursor_pos: usize) -> String { fn find_sqlserver_statement_at_cursor(sql: &str, cursor_pos: usize) -> String {
let profile = SqlDialectProfile::sql_server();
let cursor = utf16_offset_to_byte_index(sql, cursor_pos); let cursor = utf16_offset_to_byte_index(sql, cursor_pos);
let batches = split_sql_batch_ranges(sql); let batches = split_sql_batch_ranges(sql, profile);
for (idx, batch) in batches.iter().enumerate() { for (idx, batch) in batches.iter().enumerate() {
if cursor >= batch.start && cursor <= batch.end { if cursor >= batch.start && cursor <= batch.end {
if starts_with_sqlserver_module_ddl(&batch.text) { if profile.keeps_sqlserver_module_batch_at_cursor && starts_with_sqlserver_module_ddl(&batch.text) {
return batch.text.clone(); return batch.text.clone();
} }
let relative_cursor = sql[..cursor].encode_utf16().count() - sql[..batch.start].encode_utf16().count(); let relative_cursor = sql[..cursor].encode_utf16().count() - sql[..batch.start].encode_utf16().count();
@@ -831,7 +917,7 @@ fn find_sqlserver_statement_at_cursor(sql: &str, cursor_pos: usize) -> String {
if cursor < batch.start { if cursor < batch.start {
if let Some(prev) = idx.checked_sub(1).and_then(|prev_idx| batches.get(prev_idx)) { if let Some(prev) = idx.checked_sub(1).and_then(|prev_idx| batches.get(prev_idx)) {
if starts_with_sqlserver_module_ddl(&prev.text) { if profile.keeps_sqlserver_module_batch_at_cursor && starts_with_sqlserver_module_ddl(&prev.text) {
return prev.text.clone(); return prev.text.clone();
} }
let relative_cursor = prev.text.encode_utf16().count(); let relative_cursor = prev.text.encode_utf16().count();
@@ -1152,8 +1238,8 @@ fn parse_insert_values_tail(tail: &str) -> Option<String> {
} }
} }
#[derive(Default)]
struct SqlScanner { struct SqlScanner {
profile: SqlDialectProfile,
in_single_quote: bool, in_single_quote: bool,
in_double_quote: bool, in_double_quote: bool,
in_backtick: bool, in_backtick: bool,
@@ -1164,6 +1250,10 @@ struct SqlScanner {
} }
impl SqlScanner { impl SqlScanner {
fn with_profile(profile: SqlDialectProfile) -> Self {
Self { profile, ..Self::default() }
}
fn step(&mut self, sql: &str, idx: usize, ch: char) { fn step(&mut self, sql: &str, idx: usize, ch: char) {
if let Some(tag) = self.dollar_quote_tag.clone() { if let Some(tag) = self.dollar_quote_tag.clone() {
if sql[idx..].starts_with(&tag) { if sql[idx..].starts_with(&tag) {
@@ -1192,9 +1282,13 @@ impl SqlScanner {
if !self.in_single_quote && !self.in_double_quote && !self.in_backtick { if !self.in_single_quote && !self.in_double_quote && !self.in_backtick {
if ch == '-' && next == Some('-') { if ch == '-' && next == Some('-') {
self.in_line_comment = true; self.in_line_comment = true;
} else if self.profile.supports_hash_line_comments && ch == '#' {
self.in_line_comment = true;
} else if ch == '/' && next == Some('*') { } else if ch == '/' && next == Some('*') {
self.in_block_comment = true; self.in_block_comment = true;
} else if let Some(tag) = dollar_quote_tag_at_str(sql, idx) { } else if let Some(tag) =
self.profile.supports_dollar_quoted_strings.then(|| dollar_quote_tag_at_str(sql, idx)).flatten()
{
self.dollar_quote_tag = Some(tag); self.dollar_quote_tag = Some(tag);
} }
} }
@@ -1224,6 +1318,21 @@ impl SqlScanner {
} }
} }
impl Default for SqlScanner {
fn default() -> Self {
Self {
profile: SqlDialectProfile::default(),
in_single_quote: false,
in_double_quote: false,
in_backtick: false,
in_line_comment: false,
in_block_comment: false,
dollar_quote_tag: None,
previous: None,
}
}
}
fn keyword_at(sql: &str, idx: usize, keyword: &str) -> bool { fn keyword_at(sql: &str, idx: usize, keyword: &str) -> bool {
let end = idx + keyword.len(); let end = idx + keyword.len();
sql.get(idx..end).is_some_and(|candidate| candidate.eq_ignore_ascii_case(keyword)) sql.get(idx..end).is_some_and(|candidate| candidate.eq_ignore_ascii_case(keyword))
@@ -1456,7 +1565,7 @@ fn leading_executable_sql_with_options(sql: &str, options: SqlParsingOptions) ->
continue; continue;
} }
if options.supports_hash_line_comments && bytes[i] == b'#' { if options.profile.supports_hash_line_comments && bytes[i] == b'#' {
i += 1; i += 1;
while i < bytes.len() && bytes[i] != b'\n' { while i < bytes.len() && bytes[i] != b'\n' {
i += 1; i += 1;
@@ -1499,7 +1608,7 @@ fn first_executable_sql_token_with_options(sql: &str, options: SqlParsingOptions
continue; continue;
} }
if options.supports_hash_line_comments && bytes[i] == b'#' { if options.profile.supports_hash_line_comments && bytes[i] == b'#' {
i += 1; i += 1;
while i < bytes.len() && bytes[i] != b'\n' { while i < bytes.len() && bytes[i] != b'\n' {
i += 1; i += 1;
@@ -1819,7 +1928,7 @@ fn has_executable_sql_with_options(statement: &str, options: SqlParsingOptions)
continue; continue;
} }
if options.supports_hash_line_comments && ch == '#' { if options.profile.supports_hash_line_comments && ch == '#' {
in_line_comment = true; in_line_comment = true;
previous = Some(ch); previous = Some(ch);
i += 1; i += 1;
@@ -1866,8 +1975,8 @@ mod tests {
use super::{ use super::{
decode_sql_file_bytes, find_statement_at_cursor_for_database, optimize_sql_file_import_statements, decode_sql_file_bytes, find_statement_at_cursor_for_database, optimize_sql_file_import_statements,
prepare_sql_file_statement, split_sql_script, split_sql_statements_for_database, prepare_sql_file_statement, split_sql_script, split_sql_statements_for_database,
starts_with_executable_sql_keyword, starts_with_executable_sql_keyword_for_database, SqlFileStatementAction, starts_with_executable_sql_keyword, starts_with_executable_sql_keyword_for_database, SqlDialectProfile,
SqlStatementSplitter, SqlFileStatementAction, SqlStatementSplitter,
}; };
#[test] #[test]
@@ -2298,6 +2407,45 @@ DELIMITER ;";
assert_eq!(super::split_sql_statements(sql), vec!["SELECT 1", "SELECT 2"]); assert_eq!(super::split_sql_statements(sql), vec!["SELECT 1", "SELECT 2"]);
} }
#[test]
fn sql_dialect_profiles_map_database_types_to_parser_capabilities() {
let default = SqlDialectProfile::for_database_type(DatabaseType::Postgres);
assert_eq!(default, SqlDialectProfile::default());
assert!(default.supports_custom_delimiter_commands);
assert!(default.supports_dollar_quoted_strings);
assert!(!default.supports_hash_line_comments);
assert!(!default.supports_oracle_plsql_blocks);
assert!(!default.supports_slash_line_block_delimiter);
assert!(!default.supports_go_batch_separator);
assert!(!default.keeps_sqlserver_module_batch_at_cursor);
let mysql = SqlDialectProfile::for_database_type(DatabaseType::Mysql);
assert_eq!(mysql, SqlDialectProfile::mysql_compatible());
assert!(mysql.supports_hash_line_comments);
assert!(SqlDialectProfile::for_database_type(DatabaseType::Doris).supports_hash_line_comments);
assert!(SqlDialectProfile::for_database_type(DatabaseType::StarRocks).supports_hash_line_comments);
assert!(SqlDialectProfile::for_database_type(DatabaseType::ManticoreSearch).supports_hash_line_comments);
assert!(SqlDialectProfile::for_database_type(DatabaseType::Goldendb).supports_hash_line_comments);
for db_type in [
DatabaseType::Oracle,
DatabaseType::Dameng,
DatabaseType::Gaussdb,
DatabaseType::Yashandb,
DatabaseType::OceanbaseOracle,
] {
let profile = SqlDialectProfile::for_database_type(db_type);
assert_eq!(profile, SqlDialectProfile::oracle_like());
assert!(profile.supports_oracle_plsql_blocks);
assert!(profile.supports_slash_line_block_delimiter);
}
let sql_server = SqlDialectProfile::for_database_type(DatabaseType::SqlServer);
assert_eq!(sql_server, SqlDialectProfile::sql_server());
assert!(sql_server.supports_go_batch_separator);
assert!(sql_server.keeps_sqlserver_module_batch_at_cursor);
}
#[test] #[test]
fn oracle_like_split_keeps_anonymous_plsql_block_together() { fn oracle_like_split_keeps_anonymous_plsql_block_together() {
let sql = "\ let sql = "\
@@ -2429,6 +2577,29 @@ SELECT 1;";
); );
} }
#[test]
fn yashandb_split_keeps_create_trigger_with_slash_delimiter_together() {
let sql = "\
CREATE TRIGGER \"TB_SC_UPDATE_TIME_TRI\"
BEFORE UPDATE ON \"TB_SC\"
FOR EACH ROW
BEGIN
IF NOT UPDATING('UPDATE_TIME') THEN
\t\t:NEW.\"UPDATE_TIME\" := CURRENT_TIMESTAMP;
END IF;
END;
/
SELECT 1;";
assert_eq!(
split_sql_statements_for_database(sql, DatabaseType::Yashandb),
vec![
"CREATE TRIGGER \"TB_SC_UPDATE_TIME_TRI\"\nBEFORE UPDATE ON \"TB_SC\"\nFOR EACH ROW\n BEGIN\n IF NOT UPDATING('UPDATE_TIME') THEN\n \t\t:NEW.\"UPDATE_TIME\" := CURRENT_TIMESTAMP;\n END IF;\nEND;",
"SELECT 1"
]
);
}
#[test] #[test]
fn oracle_like_split_keeps_create_package_together() { fn oracle_like_split_keeps_create_package_together() {
let sql = "\ let sql = "\