0001 LIBNDRXXAECPG(8)
0002 ================
0003 :doctype: manpage
0004
0005
0006 NAME
0007 ----
0008 libndrxxaecpg - Enduro/X PostgreSQL ECPG XA Driver
0009
0010
0011 SYNOPSIS
0012 --------
0013 libndrxxaecpg.so
0014 libndrxxaecpg.dylib
0015
0016 --------------------------------------------------------------------------------
0017 NDRX_XA_RES_ID=1
0018 NDRX_XA_OPEN_STR={"url":"tcp:postgresql://${EX_PG_HOST}/${EX_PG_DB}"
0019 ,"user":"${EX_PG_USER}"
0020 ,"password":"${EX_PG_PASS}"
0021 ,"compat":"PGSQL|INFORMIX|INFORMIX_SE"
0022 }"
0023 NDRX_XA_CLOSE_STR=$NDRX_XA_OPEN_STR
0024 NDRX_XA_DRIVERLIB=libndrxxaecpg.so
0025 NDRX_XA_RMLIB=-
0026 NDRX_XA_LAZY_INIT=1
0027 --------------------------------------------------------------------------------
0028
0029 DESCRIPTION
0030 -----------
0031 This is XA driver specifically written Enduro/X needs. It provides an XA switch
0032 emulation on top of PostgreSQL prepared transactions. PostgreSQL by default
0033 does not support XA switch. Also when transaction start, there is no possibility
0034 to identify the work unit performed. The identification of work done by some
0035 process on particular connection can be done by preparing the transaction. Thus
0036 there is no such thing as "active" transaction in terms of XA specification.
0037 Also there is no possibility for other processes to join the existing work and
0038 see work done by other session. Thus for example if one server process in same
0039 transaction performs some insert and other process tries insert on table which
0040 has foreign key to first insert, it will fail, as FK will not be seen. Thus
0041 Enduro/X needs to work on branch-transactions mode without join feature. The
0042 mode of PostgreSQL driver is the same as enabled by *NDRX_XA_FLAG* with value
0043 *NOJOIN*.
0044
0045 The emulation of XA protocol is done by following steps and assumptions:
0046
0047 1. When process joins to global transaction, new branch-transaction-id is
0048 acquired. The TMSRV logs the branch with status 'u'-unknown.
0049
0050 2. When process completes the work unit (server does tpreturn(3) or tpforward(3))
0051 or initiating process performs tpcommit(3), the xa_end() is called which in-turn
0052 runs PostgreSQL "PREPARE TRANSACTION <XID_STR>". "XID_STR" is based on PosgreSQL
0053 JDBC driver format. Thus JDBC version on PQ versions of tmsrv drivers can be mixed.
0054 When transaction is prepared OK, the TMSRV is reported with 'p'-prepared status.
0055 If prepare fails, this means that transaction is aborted by PosgreSQL, and in
0056 this case 'a'-aborted status is reported to TMSRV.
0057
0058 3. When TMSRV tries to commit, and branch is in 'u'-unknown or 'a'-aborted status,
0059 the global transaction is aborted and tpcommit caller receives *TPEABORT* error.
0060 If transaction is in status 'p', the prepare phase of global transaction is
0061 skipped/no operation and then commit is executed.
0062
0063 4. In case if work unit performs too long operation and tries to xa_end() after
0064 when TMSRV is already timed-out/rolled back the global transaction, the xa_end()
0065 status call to TMSRV fails with error *TPEMATCH*. In this case local process
0066 rolls back the prepared transaction.
0067
0068 5. When tpabort is executed, the xa_end() within tpabort() process performs
0069 abort without executing prepare.
0070
0071 The driver can be used for standard libpq programming and for using ECPG
0072 precompiler. Internally the libndrxxaecpg driver manages connection by help of
0073 ECPG routines. But thanks to their API, the connection handler can be extracted
0074 to used by libPQ.
0075
0076 The connection details are encoded in JSON based string which contains the
0077 database URL, user name and password.
0078
0079 To get connection handler for PG sessions, used *tpgetconn(3)* function.
0080 Which is available for this driver.
0081
0082 CONNECTION PARAMETERS
0083 ---------------------
0084 *url*::
0085 This is standard PostgreSQL connection URL. Typically it contains database
0086 host, port and database name. The Enduro/X standard environment variable
0087 substitution is used here.
0088
0089 *user*::
0090 This is PosgreSQL connection user name. Enduro/X standard environment
0091 substitution applies. Used where applies.
0092
0093 *password*::
0094 Password used for establishing connection. Enduro/X standard environment
0095 substitution applies. Used where applies.
0096
0097 *compat*::
0098 Compatibility mode. The default is *PGSQL*. Other options are *INFORMIX*
0099 and *INFORMIX_SE*.
0100
0101 EXTERNAL SYMBOLS
0102 ----------------
0103 *ndrx_G_PG_conname*::
0104 This is connection name currently associated with thread. It is thread is
0105 stored in thread local storage (TLS). Definition is
0106 *__thread char ndrx_G_PG_conname[65]*.
0107
0108 CONFIGURATION EXAMPLE
0109 ---------------------
0110 When starting to use Enduro/X PQ XA Driver, ensure that PosgreSQL PQ and ECPG
0111 libraries are installed. Particularity *libecpg* and *libpq* must be installed.
0112
0113 The typical configuration is done as a standard Enduro/X XA resource configuration,
0114 which can be set directly in environment variables or in *[@global]* section in
0115 application configuration (e.g. app.ini). This gives example of app.ini configuration.
0116
0117 Sample configuration *app.ini* for CCTAG *DB1_PQ*:
0118
0119 --------------------------------------------------------------------------------
0120
0121 [@global/DB1_PQ]
0122 NDRX_XA_RES_ID=1
0123 NDRX_XA_OPEN_STR={"url":"tcp:postgresql://localhost:5432/testdb",
0124 "user":"testuser", "password":"testuser1"}
0125 NDRX_XA_CLOSE_STR=${NDRX_XA_OPEN_STR}
0126 NDRX_XA_DRIVERLIB=libndrxxaecpg.so
0127 NDRX_XA_RMLIB=-
0128 NDRX_XA_LAZY_INIT=1
0129
0130 --------------------------------------------------------------------------------
0131
0132 Sample configuration of transaction manager in *ndrxconfig.xml* for CCTAG *DB1_PQ*:
0133
0134 --------------------------------------------------------------------------------
0135 <servers>
0136 ...
0137 <server name="tmsrv">
0138 <max>1</max>
0139 <srvid>1650</srvid>
0140 <cctag>DB1_PQ</cctag>
0141 <sysopt>-e /tmp/tmsrv-dom1.log -r -- -t1 -l/tmp</sysopt>
0142 </server>
0143 ...
0144 </servers>
0145 --------------------------------------------------------------------------------
0146
0147 LIBPQ C EXAMPLE
0148 ---------------
0149
0150 This is example of programming database with libpq.
0151
0152 File: test_expq.c
0153 --------------------------------------------------------------------------------
0154 #include <string.h>
0155 #include <stdio.h>
0156 #include <stdlib.h>
0157
0158 #include <atmi.h>
0159 #include <libpq-fe.h>
0160
0161 #define FAIL -1
0162 #define SUCCEED 0
0163
0164 int main(int argc, char** argv)
0165 {
0166 PGconn * conn;
0167 PGresult *res = NULL;
0168 ExecStatusType estat;
0169 int ret = SUCCEED;
0170
0171 /* open connection */
0172 if (EXSUCCEED!=tpopen())
0173 {
0174 fprintf(stderr, "Failed to open: %s\n", tpstrerror(tperrno));
0175 ret = FAIL;
0176 goto out;
0177 }
0178
0179 /* get the connection which was open by tpopen() */
0180 conn = (PGconn *)tpgetconn();
0181
0182 /* create some table... */
0183
0184 res = PQexec(conn, "CREATE TABLE manextest(userid integer UNIQUE NOT NULL);");
0185
0186 estat = PQresultStatus(res);
0187
0188 if (PGRES_COMMAND_OK != estat)
0189 {
0190 char *state = PQresultErrorField(res, PG_DIAG_SQLSTATE);
0191 char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
0192
0193 fprintf(stderr, "Failed to create table: state: [%s]: %s\n", state, msg);
0194
0195 if (0==strcmp(state, "42P07"))
0196 {
0197 fprintf(stderr, "Table already exist - ignore error\n");
0198 }
0199 else
0200 {
0201 ret = FAIL;
0202 goto out;
0203 }
0204 }
0205
0206 /* start transaction */
0207 if (EXSUCCEED!=tpbegin(60, 0))
0208 {
0209 fprintf(stderr, "Failed to begin: %s\n", tpstrerror(tperrno));
0210 ret = FAIL;
0211 goto out;
0212 }
0213
0214
0215 /* insert data */
0216
0217 PQclear(res);
0218
0219 res = PQexec(conn, "insert into manextest(userid) values ((select COALESCE(max(userid), 1)+1 from manextest));");
0220
0221 estat =PQresultStatus(res);
0222
0223 if (PGRES_COMMAND_OK != estat)
0224 {
0225 char *state = PQresultErrorField(res, PG_DIAG_SQLSTATE);
0226 char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
0227
0228 fprintf(stderr, "Failed to create table: state: [%s]: %s\n", state, msg);
0229 ret = FAIL;
0230 goto out;
0231 }
0232
0233 if (SUCCEED!=tpcommit(0))
0234 {
0235 fprintf(stderr, "Commit failed: %s\n", tpstrerror(tperrno));
0236 ret = FAIL;
0237 goto out;
0238 }
0239
0240 out:
0241 if (SUCCEED!=ret)
0242 {
0243 tpabort(0);
0244 }
0245 tpclose();
0246 tpterm();
0247
0248 }
0249
0250 --------------------------------------------------------------------------------
0251
0252 Build the program with:
0253
0254 --------------------------------------------------------------------------------
0255
0256 $ cc test_expq.c -o expqtest -I/usr/include/postgresql -lpq -lecpg -latmi -lnstd -lubf -lrt
0257
0258 --------------------------------------------------------------------------------
0259
0260 Run and test:
0261
0262 --------------------------------------------------------------------------------
0263
0264 $ ./expqtest
0265 Failed to create table: state: [42P07]: relation "manextest" already exists
0266 Table already exist - ignore error
0267
0268 $ psql -U testuser -d testdb -h localhost
0269
0270 testdb=> select * from manextest;
0271 userid
0272 ========
0273 2
0274 (1 row)
0275
0276 --------------------------------------------------------------------------------
0277
0278 ECPG C EXAMPLE
0279 --------------
0280
0281 The same code example above can be written in PosgreSQL embedded C code. Assuming
0282 that table is already created by previous example.
0283
0284 File: test_exec.pgc
0285 --------------------------------------------------------------------------------
0286 #include <stdio.h>
0287 #include <stdlib.h>
0288 #include <ndebug.h>
0289 #include <atmi.h>
0290 #include <ecpglib.h>
0291
0292 #define FAIL -1
0293 #define SUCCEED 0
0294
0295 int main(int argc, char** argv)
0296 {
0297 int ret = SUCCEED;
0298 EXEC SQL BEGIN DECLARE SECTION;
0299 long id;
0300 EXEC SQL END DECLARE SECTION;
0301
0302
0303 /* open connection */
0304 if (EXSUCCEED!=tpopen())
0305 {
0306 fprintf(stderr, "Failed to open: %s\n", tpstrerror(tperrno));
0307 ret = FAIL;
0308 goto out;
0309 }
0310
0311 /* start transaction */
0312 if (EXSUCCEED!=tpbegin(60, 0))
0313 {
0314 fprintf(stderr, "Failed to begin: %s\n", tpstrerror(tperrno));
0315 ret = FAIL;
0316 goto out;
0317 }
0318
0319 EXEC SQL SELECT COALESCE(MAX(USERID), 1)+1 into :id from manextest;
0320
0321 if ((sqlca.sqlcode < 0) || (sqlca.sqlcode == 100))
0322 {
0323
0324 fprintf(stderr, "failed to get max: error code [%ld] message [%s] rows %ld, warning %c\n",
0325 sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrd[2], sqlca.sqlwarn[0]);
0326 ret = FAIL;
0327 goto out;
0328 }
0329
0330
0331 EXEC SQL INSERT INTO manextest(USERID) VALUES (:id);
0332
0333 if ((sqlca.sqlcode < 0) || (sqlca.sqlcode == 100))
0334 {
0335
0336 fprintf(stderr, "Failed to insert: error code [%ld] message [%s] rows %ld, warning %c\n",
0337 sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, sqlca.sqlerrd[2], sqlca.sqlwarn[0]);
0338 ret = FAIL;
0339 goto out;
0340 }
0341
0342 if (SUCCEED!=tpcommit(0))
0343 {
0344 fprintf(stderr, "Commit failed: %s\n", tpstrerror(tperrno));
0345 ret = FAIL;
0346 goto out;
0347 }
0348
0349 out:
0350
0351 if (SUCCEED!=ret)
0352 {
0353 tpabort(0);
0354 }
0355
0356 tpclose();
0357 tpterm();
0358 }
0359
0360 --------------------------------------------------------------------------------
0361
0362 Build program with:
0363
0364 --------------------------------------------------------------------------------
0365
0366 $ ecpg test_exec.pgc
0367 $ cc test_exec.c -o exectest -I/usr/include/postgresql -lecpg -latmi -lnstd -lubf -lrt
0368 --------------------------------------------------------------------------------
0369
0370 Execute the test:
0371
0372 --------------------------------------------------------------------------------
0373 $ ./exectest
0374 Failed to create table: state: [42P07]: relation "manextest" already exists
0375 Table already exist - ignore error
0376
0377 $ psql -U testuser -d testdb -h localhost
0378
0379 testdb=> select * from manextest;
0380 userid
0381 ========
0382 2
0383 3
0384 (1 row)
0385
0386
0387 --------------------------------------------------------------------------------
0388
0389 For more unit tests please see 'atmitest/test067_postgres' unit test folder for
0390 ECPG, PQ source examples and configuration.
0391
0392 BUGS
0393 ----
0394 Report bugs to support@mavimax.com
0395
0396 SEE ALSO
0397 --------
0398 *libndrxxpq(8)* *ndrxconfig.xml(5)* *tmsrv(8)* *libndrxxawsmqs(8)* *libndrxxaoras(8)*
0399 *tpgetconn(3)*
0400
0401 COPYING
0402 -------
0403 (C) Mavimax, Ltd
0404