-
Notifications
You must be signed in to change notification settings - Fork 77
Expand file tree
/
Copy pathPgSavepoint.c
More file actions
207 lines (191 loc) · 5.21 KB
/
PgSavepoint.c
File metadata and controls
207 lines (191 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
* Copyright (c) 2004-2024 Tada AB and other contributors, as listed below.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the The BSD 3-Clause License
* which accompanies this distribution, and is available at
* http://opensource.org/licenses/BSD-3-Clause
*
* Contributors:
* Thomas Hallgren
* Chapman Flack
*/
#include <postgres.h>
#include <access/xact.h>
#include <executor/spi.h>
#include <executor/tuptable.h>
#include "org_postgresql_pljava_internal_PgSavepoint.h"
#include "pljava/PgSavepoint.h"
#include "pljava/Exception.h"
#include "pljava/Invocation.h"
#include "pljava/type/String.h"
#include "pljava/SPI.h"
/**
* \addtogroup JNI
* @{
*/
/*
* Workaround for issue #260, PostgreSQL API breakage by EnterpriseDB. They
* added a ReleaseCurrentSubTransactionEx function with an added argument, and
* made ReleaseCurrentSubTransaction call it, passing false. But instead of
* leaving ReleaseCurrentSubTransaction an actual function that does so, which
* would not have been an API break, they made it a macro instead, with the
* result that its address cannot be taken. The reporter of the issue had an
* inquiry open with EDB for four months trying to get specifics on what
* versions have that issue, with no useful response. So this workaround is just
* conditioned on finding ReleaseCurrentSubTransaction defined as a macro.
*/
#ifdef ReleaseCurrentSubTransaction
static void addressableRelease(void);
static void addressableRelease()
{
ReleaseCurrentSubTransaction();
}
#undef ReleaseCurrentSubTransaction
#define ReleaseCurrentSubTransaction addressableRelease
#endif
static jclass s_PgSavepoint_class;
static jmethodID s_forId;
static jfieldID s_nestLevel;
extern void PgSavepoint_initialize(void);
static void unwind(void (*f)(void), jint xid, jint nestingLevel);
static void assertXid(SubTransactionId);
jobject pljava_PgSavepoint_forId(SubTransactionId subId)
{
return JNI_callStaticObjectMethodLocked(s_PgSavepoint_class, s_forId,
(jint)subId);
}
void PgSavepoint_initialize(void)
{
JNINativeMethod methods[] =
{
{
"_set",
"(Ljava/lang/String;)I",
Java_org_postgresql_pljava_internal_PgSavepoint__1set
},
{
"_release",
"(II)V",
Java_org_postgresql_pljava_internal_PgSavepoint__1release
},
{
"_rollback",
"(II)V",
Java_org_postgresql_pljava_internal_PgSavepoint__1rollback
},
{ 0, 0, 0 }
};
PgObject_registerNatives("org/postgresql/pljava/internal/PgSavepoint",
methods);
/*
* I would rather put this at the top, but it counts as a statement, and
* would trigger a declaration-after-statement warning.
*/
StaticAssertStmt(sizeof(SubTransactionId) <= sizeof(jint),
"SubTransactionId wider than jint?!");
s_PgSavepoint_class = JNI_newGlobalRef(PgObject_getJavaClass(
"org/postgresql/pljava/internal/PgSavepoint"));
s_forId =
PgObject_getStaticJavaMethod(s_PgSavepoint_class, "forId",
"(I)Lorg/postgresql/pljava/internal/PgSavepoint;");
s_nestLevel =
PgObject_getJavaField(s_PgSavepoint_class, "m_nestLevel", "I");
}
static void unwind(void (*f)(void), jint xid, jint nestingLevel)
{
while ( nestingLevel < GetCurrentTransactionNestLevel() )
f();
if ( nestingLevel == GetCurrentTransactionNestLevel() )
{
assertXid((SubTransactionId)xid);
f();
}
}
static void assertXid(SubTransactionId xid)
{
if(xid != GetCurrentSubTransactionId())
{
/* Oops. Rollback to top level transaction.
*/
ereport(ERROR, (
errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
errmsg("Subtransaction mismatch at txlevel %d",
GetCurrentTransactionNestLevel())));
}
}
/****************************************
* JNI methods
****************************************/
/*
* Class: org_postgresql_pljava_internal_PgSavepoint
* Method: _set
* Signature: (Ljava/lang/String;)I;
*/
JNIEXPORT jint JNICALL
Java_org_postgresql_pljava_internal_PgSavepoint__1set(JNIEnv* env, jobject this, jstring jname)
{
jint xid = 0;
BEGIN_NATIVE
PG_TRY();
{
char* name = String_createNTS(jname);
Invocation_assertConnect();
JNI_setIntField(this, s_nestLevel, 1+GetCurrentTransactionNestLevel());
BeginInternalSubTransaction(name);
xid = GetCurrentSubTransactionId();
if ( NULL != name )
pfree(name);
}
PG_CATCH();
{
Exception_throw_ERROR("setSavepoint");
}
PG_END_TRY();
END_NATIVE
return xid;
}
/*
* Class: org_postgresql_pljava_internal_PgSavepoint
* Method: _release
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_org_postgresql_pljava_internal_PgSavepoint__1release(JNIEnv* env, jclass clazz, jint xid, jint nestLevel)
{
BEGIN_NATIVE
PG_TRY();
{
unwind(ReleaseCurrentSubTransaction, xid, nestLevel);
}
PG_CATCH();
{
Exception_throw_ERROR("releaseSavepoint");
}
PG_END_TRY();
END_NATIVE
}
/*
* Class: org_postgresql_pljava_internal_PgSavepoint
* Method: _rollback
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_org_postgresql_pljava_internal_PgSavepoint__1rollback(JNIEnv* env, jclass clazz, jint xid, jint nestLevel)
{
BEGIN_NATIVE
PG_TRY();
{
unwind(RollbackAndReleaseCurrentSubTransaction, xid, nestLevel);
#if PG_VERSION_NUM < 100000
SPI_restore_connection();
#endif
}
PG_CATCH();
{
Exception_throw_ERROR("rollbackSavepoint");
}
PG_END_TRY();
END_NATIVE
}
/** @} */