Skip to content

Commit 0cf9cdf

Browse files
committed
Postgresql impersonation in convex-db
1 parent e6e67aa commit 0cf9cdf

File tree

11 files changed

+598
-83
lines changed

11 files changed

+598
-83
lines changed

convex-db/src/main/java/convex/db/calcite/ConvexTable.java

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,17 @@ public ConvexSchema getSchema() {
158158
* Execute INSERT - called from ConvexTableModify generated code.
159159
*/
160160
public long executeInsert(Enumerable<Object[]> input) {
161-
long count = 0;
162-
for (Object[] row : input) {
163-
if (row != null && insertRow(row)) {
164-
count++;
161+
try {
162+
long count = 0;
163+
for (Object[] row : input) {
164+
if (row != null && insertRow(row)) {
165+
count++;
166+
}
165167
}
168+
return count;
169+
} catch (ExceptionInInitializerError e) {
170+
throw wrapTypeError(e, "INSERT");
166171
}
167-
return count;
168172
}
169173

170174
/**
@@ -177,73 +181,96 @@ public long executeInsert(Enumerable<Object[]> input) {
177181
* <p>Type validation is performed on updated columns.
178182
*/
179183
public long executeUpdate(Enumerable<Object[]> input, int columnCount, int[] updateIndices) {
180-
// Check if PK column (index 0) is being updated
181-
boolean pkBeingUpdated = false;
182-
for (int idx : updateIndices) {
183-
if (idx == 0) {
184-
pkBeingUpdated = true;
185-
break;
184+
try {
185+
// Check if PK column (index 0) is being updated
186+
boolean pkBeingUpdated = false;
187+
for (int idx : updateIndices) {
188+
if (idx == 0) {
189+
pkBeingUpdated = true;
190+
break;
191+
}
186192
}
187-
}
188193

189-
ConvexColumnType[] types = getColumnTypes();
194+
ConvexColumnType[] types = getColumnTypes();
190195

191-
long count = 0;
192-
for (Object[] row : input) {
193-
if (row == null) continue;
196+
long count = 0;
197+
for (Object[] row : input) {
198+
if (row == null) continue;
194199

195-
// Reconstruct updated row from Calcite's format:
196-
// [original columns..., new values for updated columns...]
197-
Object[] updatedRow = new Object[columnCount];
198-
for (int i = 0; i < columnCount; i++) {
199-
updatedRow[i] = row[i];
200-
}
201-
for (int i = 0; i < updateIndices.length; i++) {
202-
int targetIdx = updateIndices[i];
203-
if (targetIdx >= 0 && targetIdx < columnCount) {
204-
// Validate type before assignment
205-
Object newValue = row[columnCount + i];
206-
ConvexColumnType type = (types != null && targetIdx < types.length) ? types[targetIdx] : ConvexColumnType.of(ConvexType.ANY);
207-
type.toCell(newValue); // Validates type, throws if invalid
208-
updatedRow[targetIdx] = newValue;
200+
// Reconstruct updated row from Calcite's format:
201+
// [original columns..., new values for updated columns...]
202+
Object[] updatedRow = new Object[columnCount];
203+
for (int i = 0; i < columnCount; i++) {
204+
updatedRow[i] = row[i];
205+
}
206+
for (int i = 0; i < updateIndices.length; i++) {
207+
int targetIdx = updateIndices[i];
208+
if (targetIdx >= 0 && targetIdx < columnCount) {
209+
// Validate type before assignment
210+
Object newValue = row[columnCount + i];
211+
ConvexColumnType type = (types != null && targetIdx < types.length) ? types[targetIdx] : ConvexColumnType.of(ConvexType.ANY);
212+
type.toCell(newValue); // Validates type, throws if invalid
213+
updatedRow[targetIdx] = newValue;
214+
}
209215
}
210-
}
211216

212-
ACell oldPk = toCell(row[0], 0);
213-
ACell newPk = toCell(updatedRow[0], 0);
217+
ACell oldPk = toCell(row[0], 0);
218+
ACell newPk = toCell(updatedRow[0], 0);
214219

215-
// If PK is being changed, check for uniqueness violation
216-
if (pkBeingUpdated && !oldPk.equals(newPk)) {
217-
// Check if new PK already exists
218-
if (schema.getTables().selectByKey(tableName, newPk) != null) {
219-
throw new RuntimeException("Unique constraint violation: primary key '" +
220-
newPk + "' already exists in table '" + tableName + "'");
220+
// If PK is being changed, check for uniqueness violation
221+
if (pkBeingUpdated && !oldPk.equals(newPk)) {
222+
// Check if new PK already exists
223+
if (schema.getTables().selectByKey(tableName, newPk) != null) {
224+
throw new RuntimeException("Unique constraint violation: primary key '" +
225+
newPk + "' already exists in table '" + tableName + "'");
226+
}
221227
}
222-
}
223228

224-
// Delete old row by ORIGINAL PK, then insert with new values
225-
schema.getTables().deleteByKey(tableName, oldPk);
226-
if (insertRow(updatedRow)) {
227-
count++;
229+
// Delete old row by ORIGINAL PK, then insert with new values
230+
schema.getTables().deleteByKey(tableName, oldPk);
231+
if (insertRow(updatedRow)) {
232+
count++;
233+
}
228234
}
235+
return count;
236+
} catch (ExceptionInInitializerError e) {
237+
throw wrapTypeError(e, "UPDATE");
229238
}
230-
return count;
231239
}
232240

233241
/**
234242
* Execute DELETE - called from ConvexTableModify generated code.
235243
*/
236244
public long executeDelete(Enumerable<Object[]> input) {
237-
long count = 0;
238-
for (Object[] row : input) {
239-
if (row != null && row.length > 0) {
240-
ACell pk = toCell(row[0], 0);
241-
if (schema.getTables().deleteByKey(tableName, pk)) {
242-
count++;
245+
try {
246+
long count = 0;
247+
for (Object[] row : input) {
248+
if (row != null && row.length > 0) {
249+
ACell pk = toCell(row[0], 0);
250+
if (schema.getTables().deleteByKey(tableName, pk)) {
251+
count++;
252+
}
243253
}
244254
}
255+
return count;
256+
} catch (ExceptionInInitializerError e) {
257+
throw wrapTypeError(e, "DELETE");
258+
}
259+
}
260+
261+
/**
262+
* Wraps type conversion errors from Calcite's generated code into a RuntimeException
263+
* with a clear SQL error message. Avatica will convert this to SQLException.
264+
*/
265+
private RuntimeException wrapTypeError(ExceptionInInitializerError e, String operation) {
266+
Throwable cause = e.getCause();
267+
String message = "Type conversion error in " + operation + " on table '" + tableName + "'";
268+
if (cause instanceof NumberFormatException nfe) {
269+
message += ": invalid number format - " + nfe.getMessage();
270+
} else if (cause != null) {
271+
message += ": " + cause.getMessage();
245272
}
246-
return count;
273+
return new RuntimeException(message, e);
247274
}
248275

249276
private boolean insertRow(Object[] row) {

convex-db/src/main/java/convex/db/calcite/ConvexType.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ public enum ConvexType {
2929
/** Arbitrary precision integer. SQL: BIGINT, Convex: AInteger */
3030
BIGINT(SqlTypeName.BIGINT, AInteger.class),
3131

32-
/** 64-bit signed integer. SQL: INTEGER, Convex: CVMLong */
33-
INTEGER(SqlTypeName.INTEGER, CVMLong.class),
32+
/** 64-bit signed integer. SQL: BIGINT, Convex: CVMLong */
33+
INTEGER(SqlTypeName.BIGINT, CVMLong.class),
3434

3535
/** Exact decimal number. SQL: DECIMAL, Convex: CVMDouble (approximation) */
3636
DECIMAL(SqlTypeName.DECIMAL, CVMDouble.class),
@@ -148,7 +148,7 @@ public Object toJava(ACell cell) {
148148

149149
return switch (this) {
150150
case BIGINT -> cell instanceof AInteger i ? i.big() : null;
151-
case INTEGER -> cell instanceof CVMLong l ? (int) l.longValue() : null;
151+
case INTEGER -> cell instanceof CVMLong l ? l.longValue() : null;
152152
case DECIMAL -> cell instanceof CVMDouble d ? java.math.BigDecimal.valueOf(d.doubleValue()) : null;
153153
case DOUBLE -> cell instanceof CVMDouble d ? d.doubleValue() : null;
154154
case CHAR, VARCHAR -> cell instanceof AString s ? s.toString() : null;

0 commit comments

Comments
 (0)