use stdext::function_name;
use test_case::test_case;
use odbc_api::{
ColumnsRow, Cursor, ForeignKeysRow, PrimaryKeysRow, ResultSetMetadata, TablesRow,
buffers::{ColumnarAnyBuffer, Item},
};
use crate::common::{MARIADB, MSSQL, POSTGRES, Profile, SQLITE_3};
#[test_case(MSSQL, 128, 128, 128, 128; "Microsoft SQL Server")]
#[test_case(MARIADB, 256, 0, 256, 255; "Maria DB")]
#[test_case(SQLITE_3, 255, 255, 255, 255; "SQLite 3")]
#[test_case(POSTGRES, 0, 63, 63, 63; "PostgreSQL")]
fn name_limits(
profile: &Profile,
expected_max_catalog_name_len: u16,
expected_max_schema_name_len: u16,
expected_max_table_name_len: u16,
expected_max_column_name_len: u16,
) {
let conn = profile.connection().unwrap();
assert_eq!(
conn.max_catalog_name_len().unwrap(),
expected_max_catalog_name_len
);
assert_eq!(
conn.max_schema_name_len().unwrap(),
expected_max_schema_name_len
);
assert_eq!(
conn.max_table_name_len().unwrap(),
expected_max_table_name_len
);
assert_eq!(
conn.max_column_name_len().unwrap(),
expected_max_column_name_len
);
}
#[test_case(MSSQL, "master"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db"; "Maria DB")]
#[test_case(SQLITE_3, ""; "SQLite 3")]
#[test_case(POSTGRES, "test"; "PostgreSQL")]
fn current_catalog(profile: &Profile, expected_catalog: &str) {
let conn = profile.connection().unwrap();
assert_eq!(conn.current_catalog().unwrap(), expected_catalog);
}
#[allow(deprecated)]
#[test_case(MSSQL, "dbo"; "Microsoft SQL Server")]
#[test_case(MARIADB, ""; "Maria DB")]
#[test_case(SQLITE_3, "dbo"; "SQLite 3")]
fn columns_query(profile: &Profile, schema: &str) {
let table_name = table_name!();
let conn = profile
.setup_empty_table(&table_name, &["VARCHAR(10)"])
.unwrap();
let row_set_buffer =
ColumnarAnyBuffer::try_from_descs(2, conn.columns_buffer_descs(255, 255, 255).unwrap())
.unwrap();
let mut stmt = conn.preallocate().unwrap();
let columns = stmt
.columns_cursor(&conn.current_catalog().unwrap(), schema, &table_name, "a")
.unwrap();
let mut cursor = columns.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
const COLUMN_NAME_INDEX: usize = 3;
let column_names = batch.column(COLUMN_NAME_INDEX).as_text_view().unwrap();
const COLUMN_SIZE_INDEX: usize = 6;
let column_sizes = i32::as_nullable_slice(batch.column(COLUMN_SIZE_INDEX)).unwrap();
let column_has_name_a_and_size_10 = column_names
.iter()
.zip(column_sizes)
.any(|(name, size)| str::from_utf8(name.unwrap()).unwrap() == "a" && *size.unwrap() == 10);
assert!(column_has_name_a_and_size_10);
}
#[test_case(MSSQL, Some("master"), Some("dbo"), None; "Microsoft SQL Server")]
#[test_case(MARIADB, Some("test_db"), None, Some(""); "Maria DB")]
#[test_case(SQLITE_3, None, None, None; "SQLite 3")]
#[test_case(POSTGRES, Some("test"), Some("public"), Some(""); "PostgreSQL")]
fn list_tables_with_preallocated(
profile: &Profile,
catalog: Option<&str>,
schema: Option<&str>,
remarks: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
conn.execute(&format!("CREATE TABLE {table_name} (a INTEGER)"), (), None)
.unwrap();
let mut stmt = conn.preallocate().unwrap();
let iter = stmt.tables("", "", &table_name, "").unwrap();
let rows: Vec<TablesRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(catalog, row.catalog.as_str().unwrap());
assert_eq!(schema, row.schema.as_str().unwrap());
assert_eq!(Some(table_name.as_str()), row.table.as_str().unwrap());
assert_eq!(
Some("TABLE"),
row.table_type.as_str().unwrap(),
"TABLE_TYPE"
);
assert_eq!(remarks, row.remarks.as_str().unwrap(), "REMARKS");
}
#[test_case(MSSQL, Some("master"), Some("dbo"), None; "Microsoft SQL Server")]
#[test_case(MARIADB, Some("test_db"), None, Some(""); "Maria DB")]
#[test_case(SQLITE_3, None, None, None; "SQLite 3")]
#[test_case(POSTGRES, Some("test"), Some("public"), Some(""); "PostgreSQL")]
fn list_tables_with_connection(
profile: &Profile,
catalog: Option<&str>,
schema: Option<&str>,
remarks: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
conn.execute(&format!("CREATE TABLE {table_name} (a INTEGER)"), (), None)
.unwrap();
let iter = conn.tables("", "", &table_name, "").unwrap();
let rows: Vec<TablesRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(catalog, row.catalog.as_str().unwrap());
assert_eq!(schema, row.schema.as_str().unwrap());
assert_eq!(Some(table_name.as_str()), row.table.as_str().unwrap());
assert_eq!(
Some("TABLE"),
row.table_type.as_str().unwrap(),
"TABLE_TYPE"
);
assert_eq!(remarks, row.remarks.as_str().unwrap(), "REMARKS");
}
#[test_case(MSSQL, "master", Some("dbo"), "int", 10, 4, 0, 10, None, None, None, Some("YES"); "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db", None, "INT", 10, 4, 0, 10, Some(""), Some("NULL"), Some(2), Some("YES"); "Maria DB")]
#[test_case(SQLITE_3, "", Some(""), "INTEGER", 9, 10, 10, 0, None, Some("NULL"), Some(16384), Some("YES"); "SQLite 3")]
#[test_case(POSTGRES, "test", Some("public"), "int4", 10, 4, 0, 10, Some(""), None, Some(-1), None; "PostgreSQL")]
fn list_columns_with_preallocated(
profile: &Profile,
catalog: &str,
schema: Option<&str>,
type_name: &str,
column_size: i32,
buffer_length: i32,
decimal_digits: i16,
num_prec_radix: i16,
remarks: Option<&str>,
column_default: Option<&str>,
char_octet_length: Option<i32>,
is_nullable: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
conn.execute(&format!("CREATE TABLE {table_name} (a INTEGER)"), (), None)
.unwrap();
let mut stmt = conn.preallocate().unwrap();
let iter = stmt.columns("", "", &table_name, "").unwrap();
let rows: Vec<ColumnsRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(catalog, row.catalog.as_str().unwrap().unwrap());
assert_eq!(schema, row.schema.as_str().unwrap());
assert_eq!(Some(table_name.as_str()), row.table.as_str().unwrap());
assert_eq!(Some("a"), row.column_name.as_str().unwrap());
assert_eq!(4, row.data_type, "DATA_TYPE");
assert_eq!(
Some(type_name),
row.type_name.as_str().unwrap(),
"TYPE_NAME"
);
assert_eq!(Some(&column_size), row.column_size.as_opt(), "COLUMN_SIZE");
assert_eq!(
Some(&buffer_length),
row.buffer_length.as_opt(),
"BUFFER_LENGTH"
);
assert_eq!(
Some(&decimal_digits),
row.decimal_digits.as_opt(),
"DECIMAL_DIGITS"
);
assert_eq!(
Some(&num_prec_radix),
row.num_prec_radix.as_opt(),
"NUM_PREC_RADIX"
);
assert_eq!(1, row.nullable, "NULLABLE");
assert_eq!(remarks, row.remarks.as_str().unwrap(), "REMARKS");
assert_eq!(
column_default,
row.column_default.as_str().unwrap(),
"COLUMN_DEF"
);
assert_eq!(4, row.sql_data_type, "SQL_DATA_TYPE");
assert_eq!(None, row.sql_datetime_sub.as_opt(), "SQL_DATETIME_SUB");
assert_eq!(
char_octet_length.as_ref(),
row.char_octet_length.as_opt(),
"CHAR_OCTET_LENGTH"
);
assert_eq!(1, row.ordinal_position, "ORDINAL_POSITION");
assert_eq!(
is_nullable,
row.is_nullable.as_str().unwrap(),
"IS_NULLABLE"
);
}
#[test_case(MSSQL, "master", Some("dbo"), "int", 10, 4, 0, 10, None, None, None, Some("YES"); "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db", None, "INT", 10, 4, 0, 10, Some(""), Some("NULL"), Some(2), Some("YES"); "Maria DB")]
#[test_case(SQLITE_3, "", Some(""), "INTEGER", 9, 10, 10, 0, None, Some("NULL"), Some(16384), Some("YES"); "SQLite 3")]
#[test_case(POSTGRES, "test", Some("public"), "int4", 10, 4, 0, 10, Some(""), None, Some(-1), None; "PostgreSQL")]
fn list_columns_with_connection(
profile: &Profile,
catalog: &str,
schema: Option<&str>,
type_name: &str,
column_size: i32,
buffer_length: i32,
decimal_digits: i16,
num_prec_radix: i16,
remarks: Option<&str>,
column_default: Option<&str>,
char_octet_length: Option<i32>,
is_nullable: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
conn.execute(&format!("CREATE TABLE {table_name} (a INTEGER)"), (), None)
.unwrap();
let iter = conn.columns("", "", &table_name, "").unwrap();
let rows: Vec<ColumnsRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(catalog, row.catalog.as_str().unwrap().unwrap());
assert_eq!(schema, row.schema.as_str().unwrap());
assert_eq!(Some(table_name.as_str()), row.table.as_str().unwrap());
assert_eq!(Some("a"), row.column_name.as_str().unwrap());
assert_eq!(4, row.data_type, "DATA_TYPE");
assert_eq!(
Some(type_name),
row.type_name.as_str().unwrap(),
"TYPE_NAME"
);
assert_eq!(Some(&column_size), row.column_size.as_opt(), "COLUMN_SIZE");
assert_eq!(
Some(&buffer_length),
row.buffer_length.as_opt(),
"BUFFER_LENGTH"
);
assert_eq!(
Some(&decimal_digits),
row.decimal_digits.as_opt(),
"DECIMAL_DIGITS"
);
assert_eq!(
Some(&num_prec_radix),
row.num_prec_radix.as_opt(),
"NUM_PREC_RADIX"
);
assert_eq!(1, row.nullable, "NULLABLE");
assert_eq!(remarks, row.remarks.as_str().unwrap(), "REMARKS");
assert_eq!(
column_default,
row.column_default.as_str().unwrap(),
"COLUMN_DEF"
);
assert_eq!(4, row.sql_data_type, "SQL_DATA_TYPE");
assert_eq!(None, row.sql_datetime_sub.as_opt(), "SQL_DATETIME_SUB");
assert_eq!(
char_octet_length.as_ref(),
row.char_octet_length.as_opt(),
"CHAR_OCTET_LENGTH"
);
assert_eq!(1, row.ordinal_position, "ORDINAL_POSITION");
assert_eq!(
is_nullable,
row.is_nullable.as_str().unwrap(),
"IS_NULLABLE"
);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn list_foreign_keys_with_preallocated(profile: &Profile) {
let pk_table_name = table_name!();
let fk_table_name = format!("other_{pk_table_name}");
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {fk_table_name};"), (), None)
.unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {pk_table_name};"), (), None)
.unwrap();
conn.execute(
&format!("CREATE TABLE {pk_table_name} (id INTEGER, PRIMARY KEY(id));"),
(),
None,
)
.unwrap();
conn.execute(
&format!(
"CREATE TABLE {fk_table_name} (ext_id INTEGER, FOREIGN KEY (ext_id) REFERENCES \
{pk_table_name}(id));"
),
(),
None,
)
.unwrap();
let mut stmt = conn.preallocate().unwrap();
let iter = stmt
.foreign_keys("", "", "", "", "", &fk_table_name)
.unwrap();
let rows: Vec<ForeignKeysRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(Some(pk_table_name.as_str()), row.pk_table.as_str().unwrap());
assert_eq!(Some(fk_table_name.as_str()), row.fk_table.as_str().unwrap());
assert_eq!(Some("id"), row.pk_column.as_str().unwrap());
assert_eq!(Some("ext_id"), row.fk_column.as_str().unwrap());
assert_eq!(1, row.key_seq);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn list_foreign_keys_with_connection(profile: &Profile) {
let pk_table_name = table_name!();
let fk_table_name = format!("other_{pk_table_name}");
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {fk_table_name};"), (), None)
.unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {pk_table_name};"), (), None)
.unwrap();
conn.execute(
&format!("CREATE TABLE {pk_table_name} (id INTEGER, PRIMARY KEY(id));"),
(),
None,
)
.unwrap();
conn.execute(
&format!(
"CREATE TABLE {fk_table_name} (ext_id INTEGER, FOREIGN KEY (ext_id) REFERENCES \
{pk_table_name}(id));"
),
(),
None,
)
.unwrap();
let iter = conn
.foreign_keys("", "", "", "", "", &fk_table_name)
.unwrap();
let rows: Vec<ForeignKeysRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(1, rows.len());
let row = &rows[0];
assert_eq!(Some(pk_table_name.as_str()), row.pk_table.as_str().unwrap());
assert_eq!(Some(fk_table_name.as_str()), row.fk_table.as_str().unwrap());
assert_eq!(Some("id"), row.pk_column.as_str().unwrap());
assert_eq!(Some("ext_id"), row.fk_column.as_str().unwrap());
assert_eq!(1, row.key_seq);
}
#[test_case(MSSQL, Some("master"), Some("dbo"); "Microsoft SQL Server")]
#[test_case(MARIADB, Some("test_db"), None; "Maria DB")]
#[test_case(SQLITE_3, Some(""), Some(""); "SQLite 3")]
#[test_case(POSTGRES, Some("test"), Some("public"); "PostgreSQL")]
fn list_private_keys_with_preallocated(
profile: &Profile,
catalog: Option<&str>,
schema: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
let statement =
&format!("CREATE TABLE {table_name} (a INTEGER, b INTEGER, c INTEGER, PRIMARY KEY (a,b))");
conn.execute(&statement, (), None).unwrap();
let mut stmt = conn.preallocate().unwrap();
let iter = stmt.primary_keys(None, None, &table_name).unwrap();
let primary_keys_rows: Vec<_> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(2, primary_keys_rows.len());
assert_eq!(catalog, primary_keys_rows[0].catalog.as_str().unwrap());
assert_eq!(catalog, primary_keys_rows[1].catalog.as_str().unwrap());
assert_eq!(schema, primary_keys_rows[0].schema.as_str().unwrap());
assert_eq!(schema, primary_keys_rows[1].schema.as_str().unwrap());
assert_eq!(
Some(table_name.as_str()),
primary_keys_rows[0].table.as_str().unwrap()
);
assert_eq!(
Some(table_name.as_str()),
primary_keys_rows[1].table.as_str().unwrap()
);
assert_eq!(Some("a"), primary_keys_rows[0].column.as_str().unwrap());
assert_eq!(Some("b"), primary_keys_rows[1].column.as_str().unwrap());
assert_eq!(1, primary_keys_rows[0].key_seq);
assert_eq!(2, primary_keys_rows[1].key_seq);
eprintln!(
"{}",
primary_keys_rows[0]
.pk_name
.as_str()
.unwrap()
.unwrap_or("NULL")
);
eprintln!(
"{}",
primary_keys_rows[1]
.pk_name
.as_str()
.unwrap()
.unwrap_or("NULL")
);
}
#[test_case(MSSQL, Some("master"), Some("dbo"); "Microsoft SQL Server")]
#[test_case(MARIADB, Some("test_db"), None; "Maria DB")]
#[test_case(SQLITE_3, Some(""), Some(""); "SQLite 3")]
#[test_case(POSTGRES, Some("test"), Some("public"); "PostgreSQL")]
fn list_private_keys_with_connection(
profile: &Profile,
catalog: Option<&str>,
schema: Option<&str>,
) {
let table_name = table_name!();
let conn = profile.connection().unwrap();
conn.execute(&format!("DROP TABLE IF EXISTS {table_name}"), (), None)
.unwrap();
let statement =
&format!("CREATE TABLE {table_name} (a INTEGER, b INTEGER, c INTEGER, PRIMARY KEY (a,b))");
conn.execute(&statement, (), None).unwrap();
let iter = conn.primary_keys(None, None, &table_name).unwrap();
let primary_keys_rows: Vec<PrimaryKeysRow> = iter.collect::<Result<_, _>>().unwrap();
assert_eq!(2, primary_keys_rows.len());
assert_eq!(catalog, primary_keys_rows[0].catalog.as_str().unwrap());
assert_eq!(catalog, primary_keys_rows[1].catalog.as_str().unwrap());
assert_eq!(schema, primary_keys_rows[0].schema.as_str().unwrap());
assert_eq!(schema, primary_keys_rows[1].schema.as_str().unwrap());
assert_eq!(
Some(table_name.as_str()),
primary_keys_rows[0].table.as_str().unwrap()
);
assert_eq!(
Some(table_name.as_str()),
primary_keys_rows[1].table.as_str().unwrap()
);
assert_eq!(Some("a"), primary_keys_rows[0].column.as_str().unwrap());
assert_eq!(Some("b"), primary_keys_rows[1].column.as_str().unwrap());
assert_eq!(1, primary_keys_rows[0].key_seq);
assert_eq!(2, primary_keys_rows[1].key_seq);
eprintln!(
"{}",
primary_keys_rows[0]
.pk_name
.as_str()
.unwrap()
.unwrap_or("NULL")
);
eprintln!(
"{}",
primary_keys_rows[1]
.pk_name
.as_str()
.unwrap()
.unwrap_or("NULL")
);
}
#[test_case(MSSQL, 128, 254, 4000, 254; "Microsoft SQL Server")]
#[test_case(MARIADB, 16777216, 1024, 196596, 3; "Maria DB")]
#[test_case(SQLITE_3, 50, 50, 50, 50; "SQLite 3")]
#[test_case(POSTGRES, 128, 254, 254, 254; "PostgreSQL")]
fn columns_varchar_column_sizes(
profile: &Profile,
expected_type_name_display_size: usize,
expected_remarks_display_size: usize,
expected_column_def_display_size: usize,
expected_is_nullable_display_size: usize,
) {
let table_name = table_name!();
let conn = profile
.setup_empty_table(&table_name, &["INTEGER"])
.unwrap();
let mut stmt = conn.preallocate().unwrap();
let mut cursor = stmt.columns_cursor("", "", &table_name, "").unwrap();
const TYPE_NAME: u16 = 6;
const REMARKS: u16 = 12;
const COLUMN_DEF: u16 = 13;
const IS_NULLABLE: u16 = 18;
let mut ds = |col: u16| -> usize {
cursor
.col_display_size(col)
.unwrap()
.map(|n: std::num::NonZeroUsize| n.get())
.unwrap_or(0)
};
assert_eq!(expected_type_name_display_size, ds(TYPE_NAME), "TYPE_NAME");
assert_eq!(expected_remarks_display_size, ds(REMARKS), "REMARKS");
assert_eq!(
expected_column_def_display_size,
ds(COLUMN_DEF),
"COLUMN_DEF"
);
assert_eq!(
expected_is_nullable_display_size,
ds(IS_NULLABLE),
"IS_NULLABLE"
);
}