Skip to main content

odbc_api/catalog/
foreign_keys.rs

1use crate::{
2    CursorImpl, Error, Nullable, TruncationInfo,
3    buffers::{FetchRow, FetchRowMember as _},
4    handles::{AsStatementRef, SqlText, Statement, StatementRef},
5    parameter::VarCharArray,
6};
7
8/// A row returned by [`crate::Preallocated::foreign_keys`]. The members are associated with the
9/// columns of the result set returned by [`crate::Preallocated::foreign_keys_cursor`].
10///
11/// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlforeignkeys-function>
12#[derive(Clone, Copy, Default)]
13pub struct ForeignKeysRow {
14    /// Binds to the `PKTABLE_CAT` column. Primary key table catalog name. NULL if not applicable.
15    pub pk_catalog: VarCharArray<128>,
16    /// Binds to the `PKTABLE_SCHEM` column. Primary key table schema name. NULL if not applicable.
17    pub pk_schema: VarCharArray<128>,
18    /// Binds to the `PKTABLE_NAME` column. Primary key table name.
19    pub pk_table: VarCharArray<255>,
20    /// Binds to the `PKCOLUMN_NAME` column. Primary key column name.
21    pub pk_column: VarCharArray<255>,
22    /// Binds to the `FKTABLE_CAT` column. Foreign key table catalog name. NULL if not applicable.
23    pub fk_catalog: VarCharArray<128>,
24    /// Binds to the `FKTABLE_SCHEM` column. Foreign key table schema name. NULL if not applicable.
25    pub fk_schema: VarCharArray<128>,
26    /// Binds to the `FKTABLE_NAME` column. Foreign key table name.
27    pub fk_table: VarCharArray<255>,
28    /// Binds to the `FKCOLUMN_NAME` column. Foreign key column name.
29    pub fk_column: VarCharArray<255>,
30    /// Binds to the `KEY_SEQ` column. Column sequence number in key (starting with 1).
31    pub key_seq: i16,
32    /// Binds to the `UPDATE_RULE` column. Action applied to the foreign key when the SQL operation
33    /// is UPDATE.
34    pub update_rule: Nullable<i16>,
35    /// Binds to the `DELETE_RULE` column. Action applied to the foreign key when the SQL operation
36    /// is DELETE.
37    pub delete_rule: Nullable<i16>,
38    /// Binds to the `FK_NAME` column. Foreign key name. NULL if not applicable.
39    pub fk_name: VarCharArray<128>,
40    /// Binds to the `PK_NAME` column. Primary key name. NULL if not applicable.
41    pub pk_name: VarCharArray<128>,
42    /// Binds to the `DEFERRABILITY` column. One of: `SQL_INITIALLY_DEFERRED`,
43    /// `SQL_INITIALLY_IMMEDIATE`, `SQL_NOT_DEFERRABLE`.
44    pub deferrability: Nullable<i16>,
45}
46
47unsafe impl FetchRow for ForeignKeysRow {
48    unsafe fn bind_columns_to_cursor(&mut self, mut cursor: StatementRef<'_>) -> Result<(), Error> {
49        unsafe {
50            self.pk_catalog.bind_to_col(1, &mut cursor)?;
51            self.pk_schema.bind_to_col(2, &mut cursor)?;
52            self.pk_table.bind_to_col(3, &mut cursor)?;
53            self.pk_column.bind_to_col(4, &mut cursor)?;
54            self.fk_catalog.bind_to_col(5, &mut cursor)?;
55            self.fk_schema.bind_to_col(6, &mut cursor)?;
56            self.fk_table.bind_to_col(7, &mut cursor)?;
57            self.fk_column.bind_to_col(8, &mut cursor)?;
58            self.key_seq.bind_to_col(9, &mut cursor)?;
59            self.update_rule.bind_to_col(10, &mut cursor)?;
60            self.delete_rule.bind_to_col(11, &mut cursor)?;
61            self.fk_name.bind_to_col(12, &mut cursor)?;
62            self.pk_name.bind_to_col(13, &mut cursor)?;
63            self.deferrability.bind_to_col(14, &mut cursor)?;
64            Ok(())
65        }
66    }
67
68    fn find_truncation(&self) -> Option<TruncationInfo> {
69        if let Some(t) = self.pk_catalog.find_truncation(0) {
70            return Some(t);
71        }
72        if let Some(t) = self.pk_schema.find_truncation(1) {
73            return Some(t);
74        }
75        if let Some(t) = self.pk_table.find_truncation(2) {
76            return Some(t);
77        }
78        if let Some(t) = self.pk_column.find_truncation(3) {
79            return Some(t);
80        }
81        if let Some(t) = self.fk_catalog.find_truncation(4) {
82            return Some(t);
83        }
84        if let Some(t) = self.fk_schema.find_truncation(5) {
85            return Some(t);
86        }
87        if let Some(t) = self.fk_table.find_truncation(6) {
88            return Some(t);
89        }
90        if let Some(t) = self.fk_column.find_truncation(7) {
91            return Some(t);
92        }
93        if let Some(t) = self.key_seq.find_truncation(8) {
94            return Some(t);
95        }
96        if let Some(t) = self.update_rule.find_truncation(9) {
97            return Some(t);
98        }
99        if let Some(t) = self.delete_rule.find_truncation(10) {
100            return Some(t);
101        }
102        if let Some(t) = self.fk_name.find_truncation(11) {
103            return Some(t);
104        }
105        if let Some(t) = self.pk_name.find_truncation(12) {
106            return Some(t);
107        }
108        if let Some(t) = self.deferrability.find_truncation(13) {
109            return Some(t);
110        }
111        None
112    }
113}
114
115pub fn execute_foreign_keys<S>(
116    mut statement: S,
117    pk_catalog_name: &str,
118    pk_schema_name: &str,
119    pk_table_name: &str,
120    fk_catalog_name: &str,
121    fk_schema_name: &str,
122    fk_table_name: &str,
123) -> Result<CursorImpl<S>, Error>
124where
125    S: AsStatementRef,
126{
127    let mut stmt = statement.as_stmt_ref();
128
129    stmt.foreign_keys(
130        &SqlText::new(pk_catalog_name),
131        &SqlText::new(pk_schema_name),
132        &SqlText::new(pk_table_name),
133        &SqlText::new(fk_catalog_name),
134        &SqlText::new(fk_schema_name),
135        &SqlText::new(fk_table_name),
136    )
137    .into_result(&stmt)?;
138
139    // We assume foreign keys always creates a result set, since it works like a SELECT statement.
140    debug_assert_ne!(stmt.num_result_cols().unwrap(), 0);
141
142    // Safe: `statement` is in Cursor state.
143    let cursor = unsafe { CursorImpl::new(statement) };
144
145    Ok(cursor)
146}