Skip to content
This repository has been archived by the owner on Aug 25, 2018. It is now read-only.

Commit

Permalink
Merge pull request #204 from mloskot/ml/catalog-table-privileges
Browse files Browse the repository at this point in the history
Add catalog::find_table_privileges function
  • Loading branch information
mloskot committed Aug 22, 2016
2 parents 1667d74 + 796707d commit 4e3801a
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 2 deletions.
83 changes: 83 additions & 0 deletions src/nanodbc.cpp
Expand Up @@ -3702,6 +3702,58 @@ string_type catalog::tables::table_remarks() const
return result_.get<string_type>(4, string_type());
}

catalog::table_privileges::table_privileges(result& find_result)
: result_(find_result)
{
}

bool catalog::table_privileges::next()
{
return result_.next();
}

string_type catalog::table_privileges::table_catalog() const
{
// TABLE_CAT might be NULL
return result_.get<string_type>(0, string_type());
}

string_type catalog::table_privileges::table_schema() const
{
// TABLE_SCHEM might be NULL
return result_.get<string_type>(1, string_type());
}

string_type catalog::table_privileges::table_name() const
{
// TABLE_NAME column is never NULL
return result_.get<string_type>(2);
}

string_type catalog::table_privileges::grantor() const
{
// GRANTOR might be NULL
return result_.get<string_type>(3, string_type());
}

string_type catalog::table_privileges::grantee() const
{
// GRANTEE column is never NULL
return result_.get<string_type>(4);
}

string_type catalog::table_privileges::privilege() const
{
// PRIVILEGE column is never NULL
return result_.get<string_type>(5);
}

string_type catalog::table_privileges::is_grantable() const
{
// IS_GRANTABLE might be NULL
return result_.get<string_type>(6, string_type());
}

catalog::primary_keys::primary_keys(result& find_result)
: result_(find_result)
{
Expand Down Expand Up @@ -3905,6 +3957,37 @@ catalog::tables catalog::find_tables(
return catalog::tables(find_result);
}

catalog::table_privileges catalog::find_table_privileges(
const string_type& catalog,
const string_type& table,
const string_type& schema)
{
// Passing a null pointer to a search pattern argument does not
// constrain the search for that argument; that is, a null pointer and
// the search pattern % (any characters) are equivalent.
// However, a zero-length search pattern - that is, a valid pointer to
// a string of length zero - matches only the empty string ("").
// See https://msdn.microsoft.com/en-us/library/ms710171.aspx

statement stmt(conn_);
RETCODE rc;
NANODBC_CALL_RC(
NANODBC_FUNC(SQLTablePrivileges),
rc,
stmt.native_statement_handle(),
(NANODBC_SQLCHAR*)(catalog.empty() ? nullptr : catalog.c_str()),
(catalog.empty() ? 0 : SQL_NTS),
(NANODBC_SQLCHAR*)(schema.empty() ? nullptr : schema.c_str()),
(schema.empty() ? 0 : SQL_NTS),
(NANODBC_SQLCHAR*)(table.empty() ? nullptr : table.c_str()),
(table.empty() ? 0 : SQL_NTS));
if (!success(rc))
NANODBC_THROW_DATABASE_ERROR(stmt.native_statement_handle(), SQL_HANDLE_STMT);

result find_result(stmt, 1);
return catalog::table_privileges(find_result);
}

catalog::columns catalog::find_columns(
const string_type& column,
const string_type& table,
Expand Down
39 changes: 37 additions & 2 deletions src/nanodbc.h
Expand Up @@ -1487,8 +1487,6 @@ class catalog
/// \note MSDN: This column returns a zero-length string if nullability is unknown.
/// ISO rules are followed to determine nullability.
/// An ISO SQL-compliant DBMS cannot return an empty string.
///
/// TODO: Translate "YES","NO", <empty> strings to IsNullable enum?
string_type is_nullable() const;

private:
Expand Down Expand Up @@ -1522,6 +1520,26 @@ class catalog
result result_;
};

/// \brief Result set for a list of tables and the privileges associated with each table.
class table_privileges
{
public:
bool next(); ///< Move to the next result in the result set
string_type table_catalog() const; ///< Fetch table catalog.
string_type table_schema() const; ///< Fetch table schema.
string_type table_name() const; ///< Fetch table name.
string_type grantor() const; ///< Fetch name of user who granted the privilege.
string_type grantee() const; ///< Fetch name of user whom the privilege was granted.
string_type privilege() const; ///< Fetch the table privilege.
/// Fetch indicator whether the grantee is permitted to grant the privilege to other users.
string_type is_grantable() const;

private:
friend class nanodbc::catalog;
table_privileges(result& find_result);
result result_;
};

/// \brief Creates catalog operating on database accessible through the specified connection.
catalog(connection& conn);

Expand All @@ -1540,6 +1558,23 @@ class catalog
const string_type& schema = string_type(),
const string_type& catalog = string_type());

/// \brief Creates result set with tables and the privileges associated with each table.
/// Tables information is obtained by executing `SQLTablePrivileges` function within
/// scope of the connected database accessible with the specified connection.
/// Since this function is implemented in terms of the `SQLTablePrivileges`s, it returns
/// result set ordered by TABLE_CAT, TABLE_SCHEM, TABLE_NAME, PRIVILEGE, and GRANTEE.
///
/// \param catalog The table catalog. It cannot contain a string search pattern.
/// \param schema String search pattern for schema names, treated as the Pattern Value Arguments.
/// \param table String search pattern for table names, treated as the Pattern Value Arguments.
///
/// \note Due to the fact catalog cannot is not the Pattern Value Argument,
/// order of parameters is different than in the other catalog look-up functions.
catalog::table_privileges find_table_privileges(
const string_type& catalog,
const string_type& table = string_type(),
const string_type& schema = string_type());

/// \brief Creates result set with columns in one or more tables.
///
/// Columns information is obtained by executing `SQLColumns` function within
Expand Down
48 changes: 48 additions & 0 deletions test/base_test_fixture.h
Expand Up @@ -864,6 +864,54 @@ struct base_test_fixture
}
}

void catalog_table_privileges_test()
{
nanodbc::connection connection = connect();
nanodbc::catalog catalog(connection);

// create several tables
create_table(
connection, NANODBC_TEXT("catalog_table_privileges_test"), NANODBC_TEXT("i int"));

// Check we can iterate over any tables
{
auto tables = catalog.find_table_privileges(NANODBC_TEXT(""));
long count = 0;
while (tables.next())
{
// These values must not be NULL (returned as empty string)
REQUIRE(!tables.table_name().empty());
REQUIRE(!tables.privilege().empty());
count++;
}
REQUIRE(count > 0);
}

// Check we can find a particular table
{
auto tables = catalog.find_table_privileges(
NANODBC_TEXT(""), NANODBC_TEXT("catalog_table_privileges_test"));
long count = 0;
std::set<nanodbc::string_type> privileges;
while (tables.next())
{
// These two values must not be NULL (returned as empty string)
REQUIRE(tables.table_name() == NANODBC_TEXT("catalog_table_privileges_test"));
privileges.insert(tables.privilege());
count++;
}
REQUIRE(count > 0);

// verify expected privileges
REQUIRE(!privileges.empty());
REQUIRE(privileges.count(NANODBC_TEXT("SELECT")));
REQUIRE(privileges.count(NANODBC_TEXT("INSERT")));
REQUIRE(privileges.count(NANODBC_TEXT("UPDATE")));
REQUIRE(privileges.count(NANODBC_TEXT("DELETE")));
// there can be more
}
}

void column_descriptor_test()
{
auto connection = connect();
Expand Down
5 changes: 5 additions & 0 deletions test/mssql_test.cpp
Expand Up @@ -282,6 +282,11 @@ TEST_CASE_METHOD(mssql_fixture, "catalog_tables_test", "[mssql][catalog][tables]
catalog_tables_test();
}

TEST_CASE_METHOD(mssql_fixture, "catalog_table_privileges_test", "[mssql][catalog][tables]")
{
catalog_table_privileges_test();
}

TEST_CASE_METHOD(mssql_fixture, "column_descriptor_test", "[mssql][columns]")
{
column_descriptor_test();
Expand Down
2 changes: 2 additions & 0 deletions test/mysql_test.cpp
Expand Up @@ -99,6 +99,8 @@ TEST_CASE_METHOD(mysql_fixture, "catalog_tables_test", "[mysql][catalog][tables]
catalog_tables_test();
}

// TODO: Add catalog_table_privileges_test - SQLTablePrivileges returns empty result set

TEST_CASE_METHOD(mysql_fixture, "column_descriptor_test", "[mysql][columns]")
{
column_descriptor_test();
Expand Down
8 changes: 8 additions & 0 deletions test/postgresql_test.cpp
Expand Up @@ -58,6 +58,14 @@ TEST_CASE_METHOD(postgresql_fixture, "catalog_tables_test", "[postgresql][catalo
catalog_tables_test();
}

TEST_CASE_METHOD(
postgresql_fixture,
"catalog_table_privileges_test",
"[postgresql][catalog][tables]")
{
catalog_table_privileges_test();
}

TEST_CASE_METHOD(postgresql_fixture, "column_descriptor_test", "[postgresql][columns]")
{
column_descriptor_test();
Expand Down
6 changes: 6 additions & 0 deletions test/sqlite_test.cpp
Expand Up @@ -153,6 +153,12 @@ TEST_CASE_METHOD(sqlite_fixture, "catalog_tables_test", "[sqlite][catalog][table
catalog_tables_test();
}

TEST_CASE_METHOD(sqlite_fixture, "catalog_table_privileges_test", "[sqlite][catalog][tables]")
{
before_catalog_test();
catalog_table_privileges_test();
}

TEST_CASE_METHOD(sqlite_fixture, "column_descriptor_test", "[sqlite][columns]")
{
column_descriptor_test();
Expand Down

0 comments on commit 4e3801a

Please sign in to comment.