Skip to content

Commit 02043a3

Browse files
committed
Update cpp libpq samples and README.md
1 parent 0abf123 commit 02043a3

2 files changed

Lines changed: 113 additions & 35 deletions

File tree

cpp/libpq/README.md

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,52 @@
1-
# Aurora DSQL Libpq code examples
1+
# Libpq with Aurora DSQL
22

33
## Overview
44

5-
The code examples in this topic show you how to use the Libpq with Aurora DSQL.
5+
This code example demonstrates how to use the Libpq library to interact with Amazon Aurora DSQL (DSQL). The example shows you how
6+
to connect to an Aurora DSQL cluster and perform basic database operations.
7+
8+
Aurora DSQL is a distributed SQL database service that provides high availability and scalability for
9+
your PostgreSQL-compatible applications. Libpq is a popular PostgreSQL library that allows
10+
you to interact with PostgreSQL databases using c/cpp code.
11+
12+
## About the code example
13+
14+
The example demonstrates a flexible connection approach that works for both admin and non-admin users:
15+
16+
* When connecting as an **admin user**, the example uses the `public` schema and generates an admin authentication
17+
token.
18+
* When connecting as a **non-admin user**, the example uses a custom `myschema` schema and generates a standard
19+
authentication token. The `myschema` schema needs to be created prior to running the example and the **non-admin user** needs to be granted access to the schema.
20+
21+
The code automatically detects the user type and adjusts its behavior accordingly.
22+
The example contains comments explaining the code and the operations being performed.
23+
24+
## ⚠️ Important
25+
26+
* Running this code might result in charges to your AWS account.
27+
* We recommend that you grant your code least privilege. At most, grant only the
28+
minimum permissions required to perform the task. For more information, see
29+
[Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
30+
* This code is not tested in every AWS Region. For more information, see
31+
[AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
32+
33+
34+
## Run the example
35+
36+
### Prerequisites
37+
38+
* You must have an AWS account, and have your default credentials and AWS Region
39+
configured as described in the
40+
[Globally configuring AWS SDKs and tools](https://docs.aws.amazon.com/credref/latest/refdocs/creds-config-files.html)
41+
guide.
42+
* You must have an Aurora DSQL cluster. For information about creating an Aurora DSQL cluster, see the
43+
[Getting started with Aurora DSQL](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/getting-started.html)
44+
guide.
45+
* If connecting as a non-admin user, ensure the user is linked to an IAM role and is granted access to the `myschema`
46+
schema. See the
47+
[Using database roles with IAM roles](https://docs.aws.amazon.com/aurora-dsql/latest/userguide/using-database-and-iam-roles.html)
48+
guide.
649

7-
## Prerequisites
850

951
#### C++ compiler
1052
A c++ compiler that supports c++11 standard or newer.
@@ -21,17 +63,19 @@ If you're building the SDK from source and you only need it for dsql you may use
2163
For example:
2264

2365
```
24-
cmake ../aws-sdk-cpp -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/ -DCMAKE_INSTALL_PREFIX=/usr/local/ -DBUILD_ONLY="dsql"
25-
```
66+
cmake <your_path>/aws-sdk-cpp -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=<your_path_to_aws-sdk-install> -DBUILD_ONLY="dsql"
2667
68+
# Note: Follow build and installation instructions on the official website.
69+
# This example is meant to point to the -DBUILD_ONLY="dsql" flag.
70+
```
2771

2872
#### Libpq library and Postgres include files
2973

3074
- The path to the Libpq library and include files will need to be specified for compilation
3175
- The path to the Libpq library will need to be specified for execution
3276
- Obtaining Libpq library
3377
- It is installed with postgres installation. Therefore, if postgres is installed on the system the libpq is present in ../postgres_install_dir/lib, ../postgres_install_dir/include
34-
- It is installed when psql client program is installed, similarily as with postgres installation
78+
- It is installed when psql client program is installed, similarly as with postgres installation
3579
- On some systems libpq can be installed through package manager (if the package exists for the system) e.g.
3680
```
3781
sudo yum install libpq-devel
@@ -55,9 +99,6 @@ cmake ../aws-sdk-cpp -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/
5599
- On some systems the SSL libraries can be installed using package managers
56100
- They can be downloaded from the [official website](https://openssl-library.org/source/index.html)
57101
58-
59-
## Run the examples
60-
61102
### Build the example program
62103
63104
#### Edit the Makefile file
@@ -69,11 +110,10 @@ The Makefile is located in the libpq/src directory.
69110
Update the following variables with the paths to the aws-sdk-cpp include and library files on your computer:
70111
71112
```
72-
AWS_INC_DIR=-I ../aws-sdk-install/include
73-
AWS_LIB_DIR=-L ../aws-sdk-install/lib
113+
AWS_INC_DIR=-I <your_path_to_aws-sdk-install>/include
114+
AWS_LIB_DIR=-L <your_path_to_aws-sdk-install>/lib
74115
```
75116
76-
77117
##### Linux
78118
79119
Edit the variables specifying path to the postgres include files and path to the location of the libpq library.
@@ -96,16 +136,26 @@ pg_config --libdir
96136
97137
##### Mac
98138
99-
The Mac related variables are in the 'Mac' section of the make file.
100-
If necessary, adjust the directory locations.
101-
For example:
139+
The Mac related variables are in the 'Mac' section of the Makefile.
140+
Edit the variables specifying path to the postgres include files and path to the location of the libpq library as well as the compiler include directory.
141+
142+
**Note:** These are examples only. Replace them with your path.
102143
103144
```
104-
COMPILER_INC_DIR_MAC=-I /Library/Developer/CommandLineTools/SDKs/MacOSX14.5.sdk/usr/include/c++/v1
145+
(x86)
146+
PG_INC_DIR_MAC=-I /usr/local/opt/libpq/include
147+
LIBPQ_DIR_MAC=-L /usr/local/opt/libpq/lib
148+
OR
149+
(brew)
150+
PG_INC_DIR_MAC=-I /opt/homebrew/opt/postgresql@16/include
151+
LIBPQ_DIR_MAC=-L /opt/homebrew/opt/postgresql@16/lib
152+
153+
COMPILER_INC_DIR_MAC=-I /Library/Developer/CommandLineTools/SDKs/MacOSX14.x.sdk/usr/include/c++/v1
154+
155+
# or could be
105156

106-
# could be
157+
COMPILER_INC_DIR_MAC=-I /Library/Developer/CommandLineTools/SDKs/MacOSX15.x.sdk/usr/include/c++/v1
107158

108-
COMPILER_INC_DIR_MAC=-I /Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/include/c++/v1
109159
```
110160
111161
#### Build the program
@@ -124,7 +174,7 @@ make libpq_example
124174
make libpq_example_mac
125175
```
126176
127-
This should result in the libpq_example executable program
177+
This should result in the **libpq_example** executable program
128178
129179
### Run the example program
130180
@@ -140,19 +190,25 @@ Replace the paths in the commands below with the path on your computer.
140190
141191
```
142192
export LD_LIBRARY_PATH="/usr/local/pgsql/lib:$LD_LIBRARY_PATH"
143-
export LD_LIBRARY_PATH="<your_path>/aws-sdk-install/lib:$LD_LIBRARY_PATH"
193+
export LD_LIBRARY_PATH="<your_path_to_aws-sdk-install>/lib:$LD_LIBRARY_PATH"
144194
```
145195
146196
##### Mac
147197
148198
```
149-
export DYLD_FALLBACK_LIBRARY_PATH=<your_path>/aws-sdk-install/lib
199+
export DYLD_FALLBACK_LIBRARY_PATH=<your_path_to_aws-sdk-install>/lib
150200
```
151201
152202
#### Set environment variables specifying cluster endpoint and region
153203
154204
```
205+
# e.g. 'admin' or a custom user
206+
export CLUSTER_USER=<your cluster user>
207+
208+
# e.g. "foo0bar1baz2quux3quuux4.dsql.us-east-1.on.aws"
155209
export CLUSTER_ENDPOINT="<your cluster endpoint>"
210+
211+
# e.g. "us-east-1"
156212
export REGION="<your cluster region>"
157213
```
158214
@@ -170,7 +226,7 @@ From the libpq/src directory run:
170226
171227
Aurora DSQL requires SSL when connecting to it. Therefore, the libpq library must have been built with SSL support.
172228
173-
If this is not the case, you may see the following error messgage while executing the libpq_example program:
229+
If this is not the case, you may see the following error message while executing the libpq_example program:
174230
175231
>
176232
>Error while connecting to the database server: sslmode value "require" invalid when SSL support is not compiled in.

cpp/libpq/src/libpq_example.cpp

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ using namespace Aws;
88
using namespace Aws::DSQL;
99
using namespace Aws::DSQL::Model;
1010

11-
std::string generateDBAuthToken(const std::string endpoint, const std::string region) {
11+
std::string generateDBAuthToken(const std::string clusterUser, const std::string clusterEndpoint, const std::string region) {
1212
Aws::SDKOptions options;
1313
Aws::InitAPI(options);
1414
DSQLClientConfiguration clientConfig;
@@ -17,8 +17,9 @@ std::string generateDBAuthToken(const std::string endpoint, const std::string re
1717
std::string token = "";
1818

1919
// The token expiration time is optional, and the default value 900 seconds
20-
// If you aren not using admin role to connect, use GenerateDBConnectAuthToken instead
21-
const auto presignedString = client.GenerateDBConnectAdminAuthToken(endpoint, region);
20+
const auto presignedString = clusterUser == "admin" ? client.GenerateDBConnectAdminAuthToken(clusterEndpoint, region) :
21+
client.GenerateDBConnectAuthToken(clusterEndpoint, region);
22+
2223
if (presignedString.IsSuccess()) {
2324
token = presignedString.GetResult();
2425
} else {
@@ -29,36 +30,42 @@ std::string generateDBAuthToken(const std::string endpoint, const std::string re
2930
return token;
3031
}
3132

32-
PGconn* connectToCluster(std::string clusterEndpoint, std::string region) {
33-
std::string password = generateDBAuthToken(clusterEndpoint, region);
34-
33+
PGconn* connectToCluster(std::string clusterUser, std::string clusterEndpoint, std::string region) {
3534
std::string dbname = "postgres";
36-
std::string user = "admin";
3735
std::string sslmode = "require";
3836
int port = 5432;
3937

40-
if (password.empty()) {
38+
// Generate a fresh password token for each connection, to ensure the token is not expired
39+
// when the connection is established
40+
std::string password_token = generateDBAuthToken(clusterUser, clusterEndpoint, region);
41+
42+
if (password_token.empty()) {
4143
std::cerr << "Failed to generate token." << std::endl;
4244
return NULL;
4345
}
4446

4547
char conninfo[4096];
4648
sprintf(conninfo, "dbname=%s user=%s host=%s port=%i sslmode=%s password=%s",
47-
dbname.c_str(), user.c_str(), clusterEndpoint.c_str(), port, sslmode.c_str(), password.c_str());
49+
dbname.c_str(), clusterUser.c_str(), clusterEndpoint.c_str(), port, sslmode.c_str(), password_token.c_str());
4850

4951
PGconn *conn = PQconnectdb(conninfo);
5052

5153
if (PQstatus(conn) != CONNECTION_OK) {
5254
std::cerr << "Error while connecting to the database server: " << PQerrorMessage(conn) << std::endl;
5355
PQfinish(conn);
54-
return NULL;
56+
return NULL;
5557
}
5658

5759
std::cout << std::endl << "Connection Established: " << std::endl;
5860
std::cout << "Port: " << PQport(conn) << std::endl;
59-
// std::cout << "Host: " << PQhost(conn) << std::endl;
6061
std::cout << "DBName: " << PQdb(conn) << std::endl;
6162

63+
if (clusterUser != "admin")
64+
{
65+
PGresult *result = PQexec(conn, "SET search_path = myschema");
66+
PQclear(result);
67+
}
68+
6269
return conn;
6370
}
6471

@@ -91,7 +98,7 @@ int example(PGconn *conn) {
9198
}
9299

93100
// Read the data we inserted
94-
std::string select = "SELECT * FROM owner";
101+
std::string select = "SELECT * FROM owner where name='John Doe'";
95102

96103
PGresult *selectResponse = PQexec(conn, select.c_str());
97104
ExecStatusType selectStatus = PQresultStatus(selectResponse);
@@ -116,19 +123,28 @@ int example(PGconn *conn) {
116123
}
117124
std::cout << std::endl;
118125
}
126+
if (rows == 0) {
127+
std::cerr << "No rows returned" << std::endl;
128+
retVal = -1;
129+
}
119130
}
120131
else {
121132
std::cerr << "Select failed - " << PQerrorMessage(conn) << std::endl;
122133
retVal = -1;
123134
}
124135
PQclear(selectResponse);
125136

137+
PGresult *clearResponse = PQexec(conn, "DELETE FROM owner where name='John Doe'");
138+
PQclear(clearResponse);
139+
140+
126141
return retVal;
127142
}
128143

129144
int main(int argc, char *argv[]) {
130145
std::string region = "";
131146
std::string clusterEndpoint = "";
147+
std::string clusterUser = "";
132148

133149
if (const char* env_var = std::getenv("CLUSTER_ENDPOINT")) {
134150
clusterEndpoint = env_var;
@@ -142,10 +158,16 @@ int main(int argc, char *argv[]) {
142158
std::cout << "Please set the REGION environment variable" << std::endl;
143159
return -1;
144160
}
161+
if (const char* env_var = std::getenv("CLUSTER_USER")) {
162+
clusterUser = env_var;
163+
} else {
164+
std::cout << "Please set the CLUSTER_USER environment variable" << std::endl;
165+
return -1;
166+
}
145167

146168
int testStatus = 0;
147169

148-
PGconn *conn = connectToCluster(clusterEndpoint, region);
170+
PGconn *conn = connectToCluster(clusterUser, clusterEndpoint, region);
149171

150172
if (conn == NULL) {
151173
std::cerr << "Failed to get connection." << std::endl;

0 commit comments

Comments
 (0)