Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PostgreSQL: interface/test-cases/ci-testing #113

Closed
wants to merge 7 commits into from

Conversation

SunBeau
Copy link
Contributor

@SunBeau SunBeau commented May 7, 2024

No description provided.

@SunBeau SunBeau mentioned this pull request May 7, 2024
@wolkykim
Copy link
Owner

wolkykim commented May 13, 2024 via email

Comment on lines 373 to 374
// set auto-commit
/* do nothing */
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove these comments?

/* get affected rows */
if ((affected = pgsql_affected_rows(db->pgsql)) < 0) affected = -1;
}

Q_MUTEX_LEAVE(db->qmutex);
return affected;
#else
Copy link
Owner

@wolkykim wolkykim May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can furder reduce the code duplication like the following example:

#if defined(Q_ENABLE_MYSQL) || defined(Q_ENABLE_PGSQL)

common codes...

#ifdef Q_ENABLE_MYSQL
    if (mysql_query(db->mysql, query) == 0) {
        if ((affected = mysql_affected_rows(db->mysql)) < 0) affected = -1;
    }
#elif defined(Q_ENABLE_PGSQL)
    if (pgsql_query(db->pgsql, query, PQ_WRITE) == 0) {
        if ((affected = pgsql_affected_rows(db->pgsql)) < 0) affected = -1;
    }
#endif

common codes...

#endif

// store
qdbresult_t *result = (qdbresult_t *)malloc(sizeof(qdbresult_t));
if (result == NULL) return NULL;
result->pgsql = db->pgsql;
Copy link
Owner

@wolkykim wolkykim May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it enough to store only pgresult, not the pgsql?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchtype is supported in new commits

Comment on lines 568 to 573
/* assign methods */
result->get_str = result_get_str;
result->get_str_at = result_get_str_at;
result->get_int = result_get_int;
result->get_int_at = result_get_int_at;
result->get_next = result_get_next;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like these are common and can ignore code duplication.
I'm not trying to remove 100% of code duplication but some level that isn't very hard to do and still provides good code readability. Can you review the change from that perspective once again?

Comment on lines 639 to 643
Q_MUTEX_ENTER(db->qmutex);
if (qtype_cast(qmutex_t*, db->qmutex)->count != 1) {
Q_MUTEX_LEAVE(db->qmutex);
return false;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, I would place this in the common code section


/* Wrap the low-level functions of the libpq library */

#ifndef QLIBPQ_H
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd merge this into qdatabase.c

Comment on lines 27 to 30
*****************************************************************************/
/* This code is written and updated by following people and released under
* the same license as above qLibc license.
* Copyright (c) 2015 Zhenjiang Xie - https://github.com/Charles0429
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't apply to your test. We can remove it.

const char *default_password = "123456";
const bool default_autocommit = true;

QUNIT_START("Test qdatabase_pgsql.c");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the test. Thanks.

default_db->free(default_db);
}

QUNIT_END();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line?

Copy link
Owner

@wolkykim wolkykim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for a great PR. I think this is a great addition to qLibc. Just left some of my thoughts. Please take a look.

@SunBeau
Copy link
Contributor Author

SunBeau commented May 18, 2024

Please review new commits

@@ -176,6 +397,12 @@ qdb_t *qdb(const char *dbtype, const char *addr, int port, const char *username,
return NULL;
}

#ifdef Q_ENABLE_PGSQL
if (autocommit == false) {
return NULL;
Copy link
Owner

@wolkykim wolkykim May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function can return NULL for more than a single reason, should we set errno ? like EINVAL for users to be able to determine what failed?

const char *password, const char *database, bool autocommit)
qdb_t *qdb(const char *dbtype,
const char *addr, int port, const char *database,
const char *username, const char *password, bool autocommit)
{
// check db type
#ifdef Q_ENABLE_MYSQL
if (strcmp(dbtype, "MYSQL")) return NULL;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should set errno = ENOTSUP

{
// check db type
#ifdef Q_ENABLE_MYSQL
if (strcmp(dbtype, "MYSQL")) return NULL;
#elif defined(Q_ENABLE_PGSQL)
if (strcmp(dbtype, "PGSQL")) return NULL;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too errno = ENOTSUP

Comment on lines +371 to +378
* The pgsql server and libpq library does not provide a setting for autocommit.<br>
* The following instructions are from the official manual(https://www.postgresql.org/docs/15/sql-begin.html):<br>
* BEGIN initiates a transaction block, that is, all statements after a BEGIN command
* will be executed in a single transaction until an explicit COMMIT or ROLLBACK is given.
* By default (without BEGIN), PostgreSQL executes transactions in “autocommit” mode,
* that is, each statement is executed in its own transaction
* and a commit is implicitly performed at the end of the statement
* (if execution was successful, otherwise a rollback is done).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it

Copy link
Owner

@wolkykim wolkykim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good to me. Just left some trivia comments about errno.

@SunBeau
Copy link
Contributor Author

SunBeau commented May 22, 2024

Thanks for your review, I've set errno.

return -1;
}

static inline int pgsql_num_rows(pgsql_t* pgsql)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this strange. Since this function is scoped to handle the result, not the connection.
Ideally, I think the prototype should be "pgsql_num_rows(PGresult * pgresult)"

return -1;
}

static inline int pgsql_num_fields(pgsql_t* pgsql)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thoughts on here as well, pgsql_num_fields(PGresult * pgresult)

}

int cursor = pgsql->cursor;
if (++cursor >= pgsql->rows) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not, if (++cursor < pgsql->rows) ???

BTW, does it need to check the cursor number before it tries to fetch a row? Why not just attempt to fetch a row and return false when it's empty like below?

if (pgsql_fetch_row(pgsql) != 0) {
     return false;
}
pgsql->cursor++

Comment on lines 1178 to 1187
/* get row num */
int row_num = -1;
if (pgsql->rows == 1) {
row_num = 0;
} else {
row_num = pgsql->cursor;
}

/* get value */
const char *value = PQgetvalue(pgsql->pgresult, row_num, idx - 1);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this block be simplified as below?

const char *value = PQgetvalue(pgsql->pgresult, pgsql-rows - 1, idx - 1);


#if defined(Q_ENABLE_PGSQL)
pgsql_t* pgsql = result->pgsql;
if (pgsql == NULL || pgsql->cols <= 0) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like it is not really necessary to check pgsql->cols <= 0 as PGGetVallue() will handle out of range argument.

Comment on lines 157 to 160
if (pgsql->pgresult) {
PQclear(pgsql->pgresult);
pgsql->pgresult = NULL;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just call pgsql_query_result() instead?

Comment on lines 121 to 123
int rows;
int cols;
int cursor;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why store these in pgsql struct? Should they be in the result struct?

int rows;
int cols;
int cursor;
} pgsql_t;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have qdb_s struct already for this. Having a separate struct doesn't seem to be necessary.


#ifdef Q_ENABLE_PGSQL
/* private variables for pgsql database - do not access directly */
void *pgsql;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pgsql_t's members can be defined here rather than a separate inline struct.

PGresult *pgresult;
int rows;
int cols;
int cursor;


#ifdef Q_ENABLE_PGSQL
/* private variables for pgsql database - do not access directly */
void *pgsql;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we remove pgsql struct.

char   *emsg;
PGconn *pgconn;

@wolkykim
Copy link
Owner

I happened to leave a lot of comments. Please take a review.

@SunBeau
Copy link
Contributor Author

SunBeau commented Jun 1, 2024

I've been busy at work recently, so I spent some time today to complete the code update.
I have tried my best to keep the functionality consistent with MySQL.
To provide interfaces in the form of functions like pgsql_xyz(), some structures are necessary.

@wolkykim
Copy link
Owner

wolkykim commented Jun 1, 2024 via email

@SunBeau
Copy link
Contributor Author

SunBeau commented Jun 4, 2024

Mysql

create table test(id int);

SET autocommit = OFF;
insert into test values (1);
select * from test; -- can get 1
-- exit,"id = 1" will be discarded, because "autocommit = OFF"

-- login again
select * from test; -- can not get 1
SET autocommit = ON;
insert into test values (2);
select * from test; -- can get 2
-- exit,"id = 2" will be commit

-- login again
select * from test; -- can get 2

-- A group of statements in one transaction
begin;
update test set id = 3 where id = 2;
insert into test values (4);
insert into test values (5);
rollback; -- if want to do this, "begin" is necessary

Pgsql

create table test(id int);

-- SET autocommit = OFF; -- This is not supported
insert into test values (1); -- "id = 1" will be commit
select * from test; -- can get 1
-- exit

-- login again
select * from test; -- can get 1
-- SET autocommit = ON; -- This is not supported
insert into test values (2); -- "id = 2" will be commit
select * from test; -- can get 1&2
-- exit

-- login again
select * from test; -- can get 1&2

-- A group of statements in one transaction
begin; -- This is always necessary
delete from test where id = 1;
update test set id = 3 where id = 2;
insert into test values (4);
insert into test values (5);
rollback;

@wolkykim
Copy link
Owner

wolkykim commented Jun 4, 2024

Thanks for the update. Can you please indicate in each of the comments how it was handled? I see the updates but hard to track what was addressed.

@wolkykim
Copy link
Owner

This PR still needs to address feedback and undergo more thorough testing. Since there hasn't been any recent activity, I'm closing this PR for now. Please feel free to reopen it when it's ready for another look. I appreciate your contribution and efforts.

@wolkykim wolkykim closed this Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants