Skip to content

Commit

Permalink
Added support for readonly SQL function columns.
Browse files Browse the repository at this point in the history
  • Loading branch information
Acshi Haggenmiller committed May 3, 2012
1 parent 9ef964b commit ea4b736
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 118 deletions.
2 changes: 1 addition & 1 deletion build.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# restsql build.properties
build.version=0.7.4

compile.classpath=lib/servlet-api.jar;lib/jersey-server-1.5.jar;lib/jsr311-api-1.1.1.jar;lib/commons-logging-1.1.1.jar;../restsql/lib/postgresql-9.0-801.jdbc4.jar
compile.classpath=lib/servlet-api.jar;lib/jersey-server-1.5.jar;lib/jsr311-api-1.1.1.jar;lib/commons-logging-1.1.1.jar;lib/commons-lang3-3.1.jar;../restsql/lib/postgresql-9.0-801.jdbc4.jar

javadoc.copyright=Copyright &\#169; 2011 restSQL Project Contributors
javadoc.javase6.href=http\://java.sun.com/javase/6/docs/api
Expand Down
3 changes: 2 additions & 1 deletion src/org/restsql/core/InvalidRequestException.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ public class InvalidRequestException extends SqlResourceException {
public static final String MESSAGE_SQLRESOURCE_REQUIRED = "Requests require SQL Resource name";
public static final String MESSAGE_INVALID_PARAMS = "No valid parameters found";
public static final String MESSAGE_LIMIT_REQUIRED = Request.PARAM_NAME_LIMIT + " parameter required";
public static final String MESSAGE_OFFSET_REQUIRED = Request.PARAM_NAME_OFFSET + " parameter required";
public static final String MESSAGE_OFFSET_REQUIRED = Request.PARAM_NAME_OFFSET + " parameter required";
public static final String MESSAGE_READONLY_PARAM = "Readonly parameters cannot be written to";

private static final long serialVersionUID = 1L;

Expand Down
2 changes: 2 additions & 0 deletions src/org/restsql/core/SqlResourceMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
public interface SqlResourceMetaData {

public List<ColumnMetaData> getAllReadColumns();

public List<ColumnMetaData> getReadOnlyColumns();

public TableMetaData getChild();

Expand Down
181 changes: 103 additions & 78 deletions src/org/restsql/core/impl/AbstractSqlResourceMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public abstract class AbstractSqlResourceMetaData implements SqlResourceMetaData
private static final int DEFAULT_NUMBER_DATABASES = 5;
private static final int DEFAULT_NUMBER_TABLES = 10;

private List<ColumnMetaData> allReadColumns, parentReadColumns, childReadColumns;
private List<ColumnMetaData> allReadColumns, parentReadColumns, childReadColumns, readOnlyColumns;
private TableMetaData childTable, parentTable, joinTable;
private SqlResourceDefinition definition;
private boolean hierarchical;
Expand All @@ -49,6 +49,10 @@ public abstract class AbstractSqlResourceMetaData implements SqlResourceMetaData
public List<ColumnMetaData> getAllReadColumns() {
return allReadColumns;
}

public List<ColumnMetaData> getReadOnlyColumns() {
return readOnlyColumns;
}

public TableMetaData getChild() {
return childTable;
Expand Down Expand Up @@ -338,82 +342,103 @@ private void buildPrimaryKeys(final Connection connection) throws SQLException {
*/
@SuppressWarnings("fallthrough")
private void buildTablesAndColumns(final ResultSet resultSet) throws SQLException, SqlResourceException {
final ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
final int columnCount = resultSetMetaData.getColumnCount();

allReadColumns = new ArrayList<ColumnMetaData>(columnCount);
parentReadColumns = new ArrayList<ColumnMetaData>(columnCount);
childReadColumns = new ArrayList<ColumnMetaData>(columnCount);
tableMap = new HashMap<String, TableMetaData>(DEFAULT_NUMBER_TABLES);
tables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
childPlusExtTables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
parentPlusExtTables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
final HashSet<String> databases = new HashSet<String>(DEFAULT_NUMBER_DATABASES);

for (int colNumber = 1; colNumber <= columnCount; colNumber++) {
final String databaseName = getColumnDatabaseName(definition, resultSetMetaData, colNumber);
databases.add(databaseName);
final String qualifiedTableName = getQualifiedTableName(definition, resultSetMetaData, colNumber);
final String tableName = getColumnTableName(definition, resultSetMetaData, colNumber);
final ColumnMetaDataImpl column = new ColumnMetaDataImpl(colNumber, databaseName,
qualifiedTableName, tableName, getColumnName(definition, resultSetMetaData, colNumber),
resultSetMetaData.getColumnLabel(colNumber), resultSetMetaData
.getColumnTypeName(colNumber), resultSetMetaData.getColumnType(colNumber));

TableMetaDataImpl table = (TableMetaDataImpl) tableMap.get(column.getQualifiedTableName());
if (table == null) {
// Create table metadata object and add to special references
final Table tableDef = SqlResourceDefinitionUtils.getTable(definition, column);
if (tableDef == null) {
throw new SqlResourceException("Definition requires table element for " + column.getTableName() + ", referenced by column " + column.getColumnLabel());
}
table = new TableMetaDataImpl(tableName, qualifiedTableName, databaseName, TableRole.valueOf(tableDef.getRole()));
tableMap.put(column.getQualifiedTableName(), table);
tables.add(table);

switch (table.getTableRole()) {
case Parent:
parentTable = table;
if (tableDef.getAlias() != null) {
table.setTableAlias(tableDef.getAlias());
}
// fall through
case ParentExtension:
parentPlusExtTables.add(table);
break;
case Child:
childTable = table;
if (tableDef.getAlias() != null) {
table.setTableAlias(tableDef.getAlias());
}
// fall through
case ChildExtension:
childPlusExtTables.add(table);
break;
case Join: // unlikely to be in the select columns, but just in case
joinTable = table;
joinList = new ArrayList<TableMetaData>(1);
joinList.add(joinTable);
break;
default: // Unknown
}
}
table.addColumn(column);

// Add column to special column lists
allReadColumns.add(column);
switch (table.getTableRole()) {
case Parent:
case ParentExtension:
parentReadColumns.add(column);
break;
case Child:
case ChildExtension:
childReadColumns.add(column);
break;
}
}

multipleDatabases = databases.size() > 1;
final ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
final int columnCount = resultSetMetaData.getColumnCount();

allReadColumns = new ArrayList<ColumnMetaData>(columnCount);
parentReadColumns = new ArrayList<ColumnMetaData>(columnCount);
childReadColumns = new ArrayList<ColumnMetaData>(columnCount);
tableMap = new HashMap<String, TableMetaData>(DEFAULT_NUMBER_TABLES);
tables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
childPlusExtTables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
parentPlusExtTables = new ArrayList<TableMetaData>(DEFAULT_NUMBER_TABLES);
final HashSet<String> databases = new HashSet<String>(DEFAULT_NUMBER_DATABASES);

readOnlyColumns = new ArrayList<ColumnMetaData>(columnCount);

for (int colNumber = 1; colNumber <= columnCount; colNumber++) {
final String databaseName = getColumnDatabaseName(definition, resultSetMetaData, colNumber);
databases.add(databaseName);
final String qualifiedTableName = getQualifiedTableName(definition, resultSetMetaData, colNumber);
final String tableName = getColumnTableName(definition, resultSetMetaData, colNumber);
final ColumnMetaDataImpl column = new ColumnMetaDataImpl(colNumber, databaseName,
qualifiedTableName, tableName, getColumnName(definition, resultSetMetaData, colNumber),
resultSetMetaData.getColumnLabel(colNumber), resultSetMetaData.getColumnTypeName(colNumber),
resultSetMetaData.getColumnType(colNumber));

//If we are a read only property, then we would expect to not find a table,
//but should plan on using the parent, to be a read only extension to it.
if (resultSetMetaData.isReadOnly(colNumber)) {
//We may not actually have the parentTable yet,
//So we will defer updating related things until we do.
readOnlyColumns.add(column);
} else {
TableMetaDataImpl table = (TableMetaDataImpl) tableMap.get(column.getQualifiedTableName());
if (table == null) {
// Create table metadata object and add to special references
final Table tableDef = SqlResourceDefinitionUtils.getTable(definition, column);
if (tableDef == null) {
//throw new SqlResourceException("Definition requires table element for " + column.getTableName() + ", referenced by column " + column.getColumnLabel());
System.err.println("Definition requires table element for " + column.getTableName() + ", referenced by column " + column.getColumnLabel());
}
table = new TableMetaDataImpl(tableName, qualifiedTableName, databaseName, TableRole.valueOf(tableDef.getRole()));
tableMap.put(column.getQualifiedTableName(), table);
tables.add(table);

switch (table.getTableRole()) {
case Parent:
parentTable = table;
if (tableDef.getAlias() != null) {
table.setTableAlias(tableDef.getAlias());
}
// fall through
case ParentExtension:
parentPlusExtTables.add(table);
break;
case Child:
childTable = table;
if (tableDef.getAlias() != null) {
table.setTableAlias(tableDef.getAlias());
}
// fall through
case ChildExtension:
childPlusExtTables.add(table);
break;
case Join: // unlikely to be in the select columns, but just in case
joinTable = table;
joinList = new ArrayList<TableMetaData>(1);
joinList.add(joinTable);
break;
default: // Unknown
}
}
table.addColumn(column);

// Add column to special column lists
allReadColumns.add(column);
switch (table.getTableRole()) {
case Parent:
case ParentExtension:
parentReadColumns.add(column);
break;
case Child:
case ChildExtension:
childReadColumns.add(column);
break;
}
}
}

//Add the readOnly columns in appropriately
if (parentTable != null) {
TableMetaDataImpl table = (TableMetaDataImpl) parentTable;
for (ColumnMetaData column : readOnlyColumns) {
table.addColumn(column);
allReadColumns.add(column);
parentReadColumns.add(column);
}
}

multipleDatabases = databases.size() > 1;
}
}
52 changes: 29 additions & 23 deletions src/org/restsql/core/impl/SqlBuilderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,29 +167,33 @@ private Map<String, SqlStruct> buildInsertSql(final SqlResourceMetaData metaData
for (final TableMetaData table : tables) {
final ColumnMetaData column = table.getColumns().get(param.getName());
if (column != null) {
final String qualifiedTableName = column.getQualifiedTableName();
SqlStruct sql = sqls.get(qualifiedTableName);
if (sql == null) {
// Create new sql holder
sql = new SqlStruct(DEFAULT_INSERT_SIZE, DEFAULT_INSERT_SIZE / 2);
sqls.put(qualifiedTableName, sql);
sql.getMain().append("INSERT INTO ");
sql.getMain().append(qualifiedTableName);
sql.getMain().append(" (");

sql.getClause().append(" VALUES (");
} else {
sql.getMain().append(',');
sql.getClause().append(',');
}
sql.getMain().append(column.getColumnName()); // since parameter may use column label
if (column.isCharType() || column.isDateTimeType()) {
sql.getClause().append('\'');
}
sql.getClause().append(param.getValue());
if (column.isCharType() || column.isDateTimeType()) {
sql.getClause().append('\'');
}
if (metaData.getReadOnlyColumns().contains(column)) {
throw new InvalidRequestException(InvalidRequestException.MESSAGE_READONLY_PARAM);
} else {
final String qualifiedTableName = column.getQualifiedTableName();
SqlStruct sql = sqls.get(qualifiedTableName);
if (sql == null) {
// Create new sql holder
sql = new SqlStruct(DEFAULT_INSERT_SIZE, DEFAULT_INSERT_SIZE / 2);
sqls.put(qualifiedTableName, sql);
sql.getMain().append("INSERT INTO ");
sql.getMain().append(qualifiedTableName);
sql.getMain().append(" (");

sql.getClause().append(" VALUES (");
} else {
sql.getMain().append(',');
sql.getClause().append(',');
}
sql.getMain().append(column.getColumnName()); // since parameter may use column label
if (column.isCharType() || column.isDateTimeType()) {
sql.getClause().append('\'');
}
sql.getClause().append(param.getValue());
if (column.isCharType() || column.isDateTimeType()) {
sql.getClause().append('\'');
}
}
}
}
}
Expand Down Expand Up @@ -277,6 +281,8 @@ private Map<String, SqlStruct> buildUpdateSql(final SqlResourceMetaData metaData
if (column.isPrimaryKey()) {
// Add this to the res Ids - assume resIds is non null
resIds.add(param);
} else if (metaData.getReadOnlyColumns().contains(column)) {
throw new InvalidRequestException(InvalidRequestException.MESSAGE_READONLY_PARAM);
} else if (!column.isNonqueriedForeignKey()) {
SqlStruct sql = sqls.get(column.getQualifiedTableName());
if (sql == null) {
Expand Down
32 changes: 18 additions & 14 deletions src/org/restsql/core/impl/SqlResourceFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,24 @@ private void getSqlResourceNames(final List<String> resNames, final String dirNa
final String packageName) {
final File dir = new File(dirName);
Config.logger.info("listing files for " + dirName);
for (final File file : dir.listFiles()) {
if (file.isFile()) {
final int extIndex = file.getName().indexOf(".xml");
if (extIndex > 0) {
resNames.add(packageName + file.getName().substring(0, extIndex));
}
}
}
for (final File subDir : dir.listFiles()) {
if (subDir.isDirectory()) {
String subPackageName = packageName.length() == 0 ? subDir.getName() + "." : packageName
+ subDir.getName() + ".";
getSqlResourceNames(resNames, subDir.getAbsolutePath(), subPackageName);
}
if (dir.listFiles() == null) {
Config.logger.error("IO Error trying to get xml resources for " + dirName);
} else {
for (final File file : dir.listFiles()) {
if (file.isFile()) {
final int extIndex = file.getName().indexOf(".xml");
if (extIndex > 0) {
resNames.add(packageName + file.getName().substring(0, extIndex));
}
}
}
for (final File subDir : dir.listFiles()) {
if (subDir.isDirectory()) {
String subPackageName = packageName.length() == 0 ? subDir.getName() + "." : packageName
+ subDir.getName() + ".";
getSqlResourceNames(resNames, subDir.getAbsolutePath(), subPackageName);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ public static Table getTable(final SqlResourceDefinition definition, final Colum
Table target = null;
for (final Table table : definition.getMetadata().getTable()) {
//SQL is not case-sensitive, so we shouldn't be either
if (table.getName().equalsIgnoreCase(column.getQualifiedTableName())) {
//Also, use the first table as default for any column that is not
//a direct descendant of a table. (that is a function, for example)
/*if (column.getTableName().isEmpty()) {
target = table;
break;
} else*/ if (table.getName().equalsIgnoreCase(column.getQualifiedTableName())) {
target = table;
break;
} else if (table.getName().equalsIgnoreCase(column.getTableName())) {
Expand Down

0 comments on commit ea4b736

Please sign in to comment.