Skip to content

Commit 84ae530

Browse files
author
Pedro Ribeiro
committed
Added @NotNull sproc parameter validation.
1 parent 5314208 commit 84ae530

31 files changed

+783
-92
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CREATE TABLE ztest_schema1.basic_table (
2+
bt_id serial primary key,
3+
bt_key text,
4+
bt_value text );
5+
6+
insert into ztest_schema1.basic_table ( bt_key, bt_value ) select 'key1','value1' union all select 'key2','value2';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CREATE FUNCTION get_empty_list(p_objs example_domain_object_with_inner_object[]) RETURNS setof example_domain_object_with_inner_object AS
2+
$$
3+
BEGIN
4+
IF array_length(p_objs, 1) <> 0 THEN
5+
RAISE EXCEPTION 'Array length is not 0';
6+
END IF;
7+
8+
RETURN;
9+
END;
10+
$$ LANGUAGE plpgsql SECURITY DEFINER;

database/test/21_shard1/21_schema1/05_stored_procedures/20_test_validations.sql

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,16 @@ BEGIN
7070
a.a := null;
7171
return a;
7272
END;
73-
$$ LANGUAGE plpgsql SECURITY DEFINER;
73+
$$ LANGUAGE plpgsql SECURITY DEFINER;
74+
75+
CREATE OR REPLACE FUNCTION test_sproc_call_with_multiple_parameters_validation (
76+
a example_domain_object_with_validation,
77+
parameter0 text,
78+
parameter1 text,
79+
parameter2 text)
80+
RETURNS example_domain_object_with_validation AS
81+
$$
82+
BEGIN
83+
return a;
84+
END;
85+
$$ LANGUAGE plpgsql SECURITY DEFINER;

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>de.zalando</groupId>
66
<artifactId>zalando-sprocwrapper</artifactId>
7-
<version>1.0.5</version>
7+
<version>1.0.6</version>
88
<packaging>jar</packaging>
99
<name>Stored Procedure Wrapper</name>
1010
<description>Library to make PostgreSQL stored procedures available through simple Java "*SProcService" interfaces including automatic object serialization and deserialization (using typemapper and convention-over-configuration). Supports sharding, advisory locking, statement timeouts and PostgreSQL types such as enums and hstore.</description>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package de.zalando.sprocwrapper.proxy;
2+
3+
import java.lang.reflect.Method;
4+
5+
import java.util.Arrays;
6+
7+
import com.google.common.base.Preconditions;
8+
9+
public class InvocationContext {
10+
11+
private final Object proxy;
12+
private final Method method;
13+
private final Object[] args;
14+
15+
public InvocationContext(final Object proxy, final Method method, final Object[] args) {
16+
this.proxy = Preconditions.checkNotNull(proxy, "proxy");
17+
this.method = Preconditions.checkNotNull(method, "method");
18+
this.args = args;
19+
}
20+
21+
public Object getProxy() {
22+
return proxy;
23+
}
24+
25+
public Method getMethod() {
26+
return method;
27+
}
28+
29+
public Object[] getArgs() {
30+
return args;
31+
}
32+
33+
@Override
34+
public String toString() {
35+
StringBuilder builder = new StringBuilder();
36+
builder.append("InvocationContext [proxy=");
37+
builder.append(proxy);
38+
builder.append(", method=");
39+
builder.append(method);
40+
builder.append(", args=");
41+
builder.append(Arrays.toString(args));
42+
builder.append("]");
43+
return builder.toString();
44+
}
45+
46+
}

src/main/java/de/zalando/sprocwrapper/proxy/SProcProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ public Object invoke(final Object proxy, final Method m, final Object[] args) {
4545
return null;
4646
}
4747

48-
return p.execute(dataSourceProvider, args);
48+
return p.execute(dataSourceProvider, new InvocationContext(proxy, m, args));
4949
}
5050
}

src/main/java/de/zalando/sprocwrapper/proxy/StoredProcedure.java

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ private Map<Integer, Object[]> partitionArguments(final DataSourceProvider dataS
339339

340340
// TODO: currently only implemented for single shardKey argument as first argument!
341341
final List<Object> originalArgument = (List<Object>) args[0];
342+
if (originalArgument == null || originalArgument.isEmpty()) {
343+
throw new IllegalArgumentException("ShardKey (first argument) of sproc '" + name + "' not defined");
344+
}
345+
342346
List<Object> partitionedArgument = null;
343347
Object[] partitionedArguments = null;
344348
int shardId;
@@ -383,28 +387,27 @@ private static class Call implements Callable<Object> {
383387
private final StoredProcedure sproc;
384388
private final DataSource shardDs;
385389
private final Object[] params;
386-
private final Object[] originalArgs;
390+
private final InvocationContext invocation;
387391

388392
public Call(final StoredProcedure sproc, final DataSource shardDs, final Object[] params,
389-
final Object[] originalArgs) {
393+
final InvocationContext invocation) {
390394
this.sproc = sproc;
391395
this.shardDs = shardDs;
392396
this.params = params;
393-
this.originalArgs = originalArgs;
397+
this.invocation = invocation;
394398
}
395399

396400
@Override
397401
public Object call() throws Exception {
398-
return sproc.executor.executeSProc(shardDs, sproc.getQuery(), params, sproc.getTypes(), originalArgs,
402+
return sproc.executor.executeSProc(shardDs, sproc.getQuery(), params, sproc.getTypes(), invocation,
399403
sproc.returnType);
400404
}
401405

402406
}
403407

404408
private static ExecutorService parallelThreadPool = Executors.newCachedThreadPool();
405409

406-
@SuppressWarnings({ "unchecked", "rawtypes" })
407-
public Object execute(final DataSourceProvider dp, final Object[] args) {
410+
public Object execute(final DataSourceProvider dp, final InvocationContext invocation) {
408411

409412
List<Integer> shardIds = null;
410413
Map<Integer, Object[]> partitionedArguments = null;
@@ -413,17 +416,17 @@ public Object execute(final DataSourceProvider dp, final Object[] args) {
413416
shardIds = dp.getDistinctShardIds();
414417
} else {
415418
if (autoPartition) {
416-
partitionedArguments = partitionArguments(dp, args);
419+
partitionedArguments = partitionArguments(dp, invocation.getArgs());
417420
shardIds = Lists.newArrayList(partitionedArguments.keySet());
418421
} else {
419-
shardIds = Lists.newArrayList(getShardId(args));
422+
shardIds = Lists.newArrayList(getShardId(invocation.getArgs()));
420423
}
421424
}
422425

423426
if (partitionedArguments == null) {
424427
partitionedArguments = Maps.newHashMap();
425428
for (final int shardId : shardIds) {
426-
partitionedArguments.put(shardId, args);
429+
partitionedArguments.put(shardId, invocation.getArgs());
427430
}
428431
}
429432

@@ -434,7 +437,7 @@ public Object execute(final DataSourceProvider dp, final Object[] args) {
434437

435438
} catch (final SQLException e) {
436439
throw new CannotGetJdbcConnectionException("Failed to acquire connection for virtual shard "
437-
+ shardIds.get(0) + " translates to" + dp.getDataSourceId(shardIds.get(0)) + " for " + name, e);
440+
+ shardIds.get(0) + " for " + name, e);
438441
}
439442

440443
final List<Object[]> paramValues = Lists.newArrayList();
@@ -459,7 +462,7 @@ public Object execute(final DataSourceProvider dp, final Object[] args) {
459462
}
460463

461464
// most common case: only one shard and no argument partitioning
462-
return executor.executeSProc(firstDs, getQuery(), paramValues.get(0), getTypes(), args, returnType);
465+
return executor.executeSProc(firstDs, getQuery(), paramValues.get(0), getTypes(), invocation, returnType);
463466
} else {
464467
Map<Integer, SameConnectionDatasource> transactionalDatasources = null;
465468
try {
@@ -471,11 +474,11 @@ public Object execute(final DataSourceProvider dp, final Object[] args) {
471474
Object sprocResult = null;
472475
final long start = System.currentTimeMillis();
473476
if (parallel) {
474-
sprocResult = executeInParallel(dp, args, shardIds, paramValues, transactionalDatasources, results,
475-
sprocResult);
477+
sprocResult = executeInParallel(dp, invocation, shardIds, paramValues, transactionalDatasources,
478+
results, sprocResult);
476479
} else {
477-
sprocResult = executeSequential(dp, args, shardIds, paramValues, transactionalDatasources, results,
478-
sprocResult);
480+
sprocResult = executeSequential(dp, invocation, shardIds, paramValues, transactionalDatasources,
481+
results, sprocResult);
479482
}
480483

481484
if (LOG.isTraceEnabled()) {
@@ -528,9 +531,10 @@ public Object execute(final DataSourceProvider dp, final Object[] args) {
528531
}
529532

530533
@SuppressWarnings({ "rawtypes", "unchecked" })
531-
private Object executeSequential(final DataSourceProvider dp, final Object[] args, final List<Integer> shardIds,
532-
final List<Object[]> paramValues, final Map<Integer, SameConnectionDatasource> transactionalDatasources,
533-
final List<?> results, Object sprocResult) {
534+
private Object executeSequential(final DataSourceProvider dp, final InvocationContext invocation,
535+
final List<Integer> shardIds, final List<Object[]> paramValues,
536+
final Map<Integer, SameConnectionDatasource> transactionalDatasources, final List<?> results,
537+
Object sprocResult) {
534538
DataSource shardDs;
535539
int i = 0;
536540
final List<String> exceptions = Lists.newArrayList();
@@ -543,7 +547,7 @@ private Object executeSequential(final DataSourceProvider dp, final Object[] arg
543547

544548
sprocResult = null;
545549
try {
546-
sprocResult = executor.executeSProc(shardDs, getQuery(), paramValues.get(i), getTypes(), args,
550+
sprocResult = executor.executeSProc(shardDs, getQuery(), paramValues.get(i), getTypes(), invocation,
547551
returnType);
548552
} catch (final Exception e) {
549553

@@ -568,9 +572,10 @@ private Object executeSequential(final DataSourceProvider dp, final Object[] arg
568572
}
569573

570574
@SuppressWarnings({ "rawtypes", "unchecked" })
571-
private Object executeInParallel(final DataSourceProvider dp, final Object[] args, final List<Integer> shardIds,
572-
final List<Object[]> paramValues, final Map<Integer, SameConnectionDatasource> transactionalDatasources,
573-
final List<?> results, Object sprocResult) {
575+
private Object executeInParallel(final DataSourceProvider dp, final InvocationContext invocation,
576+
final List<Integer> shardIds, final List<Object[]> paramValues,
577+
final Map<Integer, SameConnectionDatasource> transactionalDatasources, final List<?> results,
578+
Object sprocResult) {
574579
DataSource shardDs;
575580
final Map<Integer, FutureTask<Object>> tasks = Maps.newHashMapWithExpectedSize(shardIds.size());
576581
FutureTask<Object> task;
@@ -582,7 +587,7 @@ private Object executeInParallel(final DataSourceProvider dp, final Object[] arg
582587
LOG.debug(getDebugLog(paramValues.get(i)));
583588
}
584589

585-
task = new FutureTask<Object>(new Call(this, shardDs, paramValues.get(i), args));
590+
task = new FutureTask<Object>(new Call(this, shardDs, paramValues.get(i), invocation));
586591
tasks.put(shardId, task);
587592
parallelThreadPool.execute(task);
588593
i++;

src/main/java/de/zalando/sprocwrapper/proxy/executors/Executor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import javax.sql.DataSource;
44

5+
import de.zalando.sprocwrapper.proxy.InvocationContext;
6+
57
/**
68
* @author jmussler
79
*/
810
public interface Executor {
9-
Object executeSProc(DataSource ds, String sql, Object[] args, int[] types, Object[] originalArgs, Class returnType);
11+
Object executeSProc(DataSource ds, String sql, Object[] args, int[] types, InvocationContext invocationContext,
12+
Class<?> returnType);
1013
}

src/main/java/de/zalando/sprocwrapper/proxy/executors/ExecutorWrapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import de.zalando.sprocwrapper.SProcCall.AdvisoryLock;
1414
import de.zalando.sprocwrapper.dsprovider.SameConnectionDatasource;
15+
import de.zalando.sprocwrapper.proxy.InvocationContext;
1516

1617
/**
1718
* This Executor wraps stored procedure calls that use advisory locks and / or need different statement timeouts set.
@@ -96,7 +97,7 @@ private boolean unlockAdvisoryLock(final Connection conn) throws SQLException {
9697

9798
@Override
9899
public Object executeSProc(final DataSource ds, final String sql, final Object[] args, final int[] types,
99-
final Object[] originalArgs, final Class returnType) {
100+
final InvocationContext invocationContext, final Class<?> returnType) {
100101

101102
SameConnectionDatasource sameConnDs = null;
102103

@@ -110,7 +111,7 @@ public Object executeSProc(final DataSource ds, final String sql, final Object[]
110111
throw new RuntimeException("Could not acquire AdvisoryLock " + lock.name());
111112
}
112113

113-
return executor.executeSProc(sameConnDs, sql, args, types, originalArgs, returnType);
114+
return executor.executeSProc(sameConnDs, sql, args, types, invocationContext, returnType);
114115

115116
} catch (final SQLException e) {
116117

src/main/java/de/zalando/sprocwrapper/proxy/executors/GlobalTransformerExecutorWrapper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import com.google.common.collect.Lists;
1212

13+
import de.zalando.sprocwrapper.proxy.InvocationContext;
14+
1315
import de.zalando.typemapper.core.ValueTransformer;
1416
import de.zalando.typemapper.core.fieldMapper.GlobalValueTransformerRegistry;
1517

@@ -31,8 +33,8 @@ public GlobalTransformerExecutorWrapper(final Executor e) {
3133
@SuppressWarnings({ "rawtypes", "unchecked" })
3234
@Override
3335
public Object executeSProc(final DataSource ds, final String sql, final Object[] args, final int[] types,
34-
final Object[] originalArgs, final Class returnType) {
35-
final Object result = originalExecutor.executeSProc(ds, sql, args, types, originalArgs, String.class);
36+
final InvocationContext invocationContext, final Class<?> returnType) {
37+
final Object result = originalExecutor.executeSProc(ds, sql, args, types, invocationContext, String.class);
3638
if (result == null) {
3739
return null;
3840
}

0 commit comments

Comments
 (0)