-
Notifications
You must be signed in to change notification settings - Fork 6
C Client
Client library distribution is source-based, copy resql.h and resql.c to your project.
C++ projects can use C client as well.
Resql provides minimal API for clients. A quick example:
#include <stdio.h>
#include "resql.h"
int main()
{
resql *client;
struct resql_result *rs;
struct resql_column *row;
resql_create(&client, &(struct resql_config){0});
resql_put_sql(client, "SELECT 'Hello World!';");
resql_exec(client, true, &rs);
row = resql_row(rs);
printf("%s \n", row[0].text);
resql_shutdown(client);
return 0;
}-
client_name: Unique client name, if not provided a random string will be assigned. -
cluster_name: Cluster name must match with configured cluster name on servers. This is just a simple security check to prevent clients from connecting to the wrong cluster. -
timeout_millis: Timeout for any operation of the client. This timeout applies to connection/reconnection time and query execution time. -
urls: Server urls. It is okay to give just one URL. Client will get other nodes' urls when it's connected to one node. -
outgoing_addr: Set outgoing address of the client, null for automatic address selection. -
outgoing_port: Set outgoing port of the client, null for automatic port selection.
#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *client;
struct resql_result *rs;
struct resql_column *row;
struct resql_config conf = {
.client_name = "testclient",
.cluster_name = "cluster",
.timeout_millis = 4000,
.urls = "tcp://127.0.0.1:7600",
};
rc = resql_create(&client, &conf);
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(client, "SELECT 'Hello World!';");
resql_exec(client, true, &rs);
row = resql_row(rs);
printf("%s \n", row[0].text);
rc = resql_shutdown(client);
if (rc != RESQL_OK) {
printf("Failed to shutdown client \n");
exit(1);
}
return 0;
}Client has two functions to define operations: put_prepared() and put_sql(). These methods will put your operations into a buffer, you may call them more than once, then call resql_exec(). Basically, put_**() will batch your operations, resql_exec() will send it to the server and wait for a response.
#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *client;
struct resql_result *rs;
rc = resql_create(&client, &(struct resql_config){0});
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(client, "CREATE TABLE test (key TEXT, value TEXT);");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Clean-up
resql_put_sql(client, "DROP TABLE test;");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_shutdown(client);
}As you can see, resql_exec() has a boolean parameter.
If your operation is read-only, e.g SELECT, set this parameter to true. This is an optimization. Readonly operations don't change the state of the database, so no need to write these operations to WAL file. If you set read-only to true even your operation is not, the server will reject your operation and you'll catch that error while developing your application.
#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *client;
struct resql_result *rs;
rc = resql_create(&client, &(struct resql_config){0});
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(client, "CREATE TABLE test (key TEXT, value TEXT);");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Option-1, with parameter name
resql_put_sql(client, "INSERT INTO test VALUES(:name,:lastname);");
resql_bind_param_text(client, ":name", "jane");
resql_bind_param_text(client, ":lastname", "doe");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Option-2, with parameter index
resql_put_sql(client, "INSERT INTO test VALUES(?, ?);");
resql_bind_index_text(client, 0, "jane");
resql_bind_index_text(client, 1, "doe");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Clean-up
resql_put_sql(client, "DROP TABLE test;");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_shutdown(client);
}#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *client;
struct resql_result *rs;
rc = resql_create(&client, &(struct resql_config){0});
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(client, "CREATE TABLE test (key TEXT, value TEXT);");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_put_sql(client, "INSERT INTO test VALUES('jane','doe');");
resql_put_sql(client, "INSERT INTO test VALUES('jack','doe');");
resql_put_sql(client, "INSERT INTO test VALUES('joe','doe');");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_put_sql(client, "SELECT * FROM test;");
rc = resql_exec(client, true, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
struct resql_column* row;
while ((row = resql_row(rs)) != NULL) {
printf("name : %s, lastname : %s \n", row[0].text, row[1].text);
}
// Clean-up
resql_put_sql(client, "DROP TABLE test;");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_shutdown(client);
}If you're going to execute a statement many times, prepare the statement and use it for better performance. Prepared statements are kept on servers. They can be deleted by calling resql_del_prepared() method of the client or they will be freed automatically when the client closes the connection gracefully.
#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *client;
resql_stmt stmt;
struct resql_result *rs;
rc = resql_create(&client, &(struct resql_config){0});
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(client, "CREATE TABLE test (key TEXT, value TEXT);");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Option-1, indexes
rc = resql_prepare(client, "INSERT INTO test VALUES(?, ?);", &stmt);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_put_prepared(client, &stmt);
resql_bind_index_text(client, 0, "jane");
resql_bind_index_text(client, 1, "doe");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Clean-up if you're not going to use prepared statement again.
rc = resql_del_prepared(client, &stmt);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Option-2, parameter names
rc = resql_prepare(client, "INSERT INTO test VALUES(:name, :lastname);", &stmt);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_put_prepared(client, &stmt);
resql_bind_param_text(client, ":name", "jane");
resql_bind_param_text(client, ":lastname", "doe");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Clean-up if you're not going to use prepared statement again.
rc = resql_del_prepared(client, &stmt);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
// Clean-up
resql_put_sql(client, "DROP TABLE test;");
rc = resql_exec(client, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(client));
exit(1);
}
resql_shutdown(client);
}If you put_**() multiple operations and call resql_exec(), these operations will be processed atomically. Either all succeed or fail. You can even combine INSERT's with SELECT's.
#include <stdio.h>
#include "resql.h"
int main()
{
int rc;
resql *r;
struct resql_result *rs;
rc = resql_create(&r, &(struct resql_config){0});
if (rc != RESQL_OK) {
printf("Failed to create client \n");
exit(1);
}
resql_put_sql(r, "CREATE TABLE test (key TEXT, value TEXT);");
resql_put_sql(r, "INSERT INTO test VALUES('mykey', 1000);");
rc = resql_exec(r, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(r));
exit(1);
}
// Demo for getAndIncrement atomically.
resql_put_sql(r, "SELECT * FROM test WHERE key = 'mykey';");
resql_put_sql(r, "UPDATE test SET value = value + 1 WHERE key = 'mykey'");
resql_put_sql(r, "SELECT * FROM test WHERE key = 'mykey';");
rc = resql_exec(r, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(r));
exit(1);
}
// rs has three result sets, each corresponds to operations
// that we added into the batch.
// First operation was SELECT
struct resql_column *row = resql_row(rs);
printf("Value was : %s \n", row[1].text);
// Advance to the next result set which is for UPDATE.
resql_next(rs);
printf("Line changed : %d \n", resql_changes(rs));
// Advance to the next result set which is for SELECT again.
resql_next(rs);
row = resql_row(rs);
printf("Value is now : %s \n", row[1].text);
// Clean-up
resql_put_sql(r, "DROP TABLE test;");
rc = resql_exec(r, false, &rs);
if (rc != RESQL_OK) {
printf("Operation failed : %s \n", resql_errstr(r));
exit(1);
}
resql_shutdown(r);
}- Get Started
- Administration
- Clients