Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for insert if any of the primary key column is null. #1705

Merged
merged 9 commits into from
Nov 14, 2024
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.stargate.sgv2.jsonapi.service.operation.tables;

import static io.stargate.sgv2.jsonapi.exception.ErrorFormatters.*;
import static io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil.COLUMN_METADATA_COMPARATOR;

import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata;
Expand Down Expand Up @@ -71,7 +72,7 @@ public WriteableTableRow build(JsonNamedValueContainer source) {
source.forEach((key, value) -> cqlIdentifierToJsonValue.put(createCqlIdentifier(key), value));

// the validation steps
checkAllPrimaryKeys(cqlIdentifierToJsonValue.keySet());
checkAllPrimaryKeys(cqlIdentifierToJsonValue);
checkUnknownColumns(cqlIdentifierToJsonValue.keySet());
var decoded = encodeJsonToCql(cqlIdentifierToJsonValue);

Expand Down Expand Up @@ -104,18 +105,26 @@ private static CqlIdentifier createCqlIdentifier(JsonPath name) {
*
* <p>Throws a {@link DocumentException.Code#MISSING_PRIMARY_KEY_COLUMNS}
*/
private void checkAllPrimaryKeys(Collection<CqlIdentifier> suppliedColumns) {
private void checkAllPrimaryKeys(Map<CqlIdentifier, JsonNamedValue> suppliedColumns) {

// dont worry about set, there is normally only 1 to 3 primary key columns in a table
var missingPrimaryKeys =
tableMetadata.getPrimaryKey().stream()
.filter(column -> !suppliedColumns.contains(column.getName()))
.filter(
column ->
(!suppliedColumns.containsKey(column.getName())
|| (suppliedColumns.containsKey(column.getName())
&& suppliedColumns.get(column.getName()).value().value() == null)))
.sorted(COLUMN_METADATA_COMPARATOR)
.toList();

if (!missingPrimaryKeys.isEmpty()) {
var suppliedPrimaryKeys =
tableMetadata.getPrimaryKey().stream()
.filter(column -> suppliedColumns.contains(column.getName()))
.filter(
column ->
suppliedColumns.containsKey(column.getName())
&& suppliedColumns.get(column.getName()).value().value() != null)
.toList();
throw DocumentException.Code.MISSING_PRIMARY_KEY_COLUMNS.get(
errVars(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata;
import io.stargate.sgv2.jsonapi.fixtures.CqlFixture;
import io.stargate.sgv2.jsonapi.fixtures.data.AllNullValues;
import io.stargate.sgv2.jsonapi.fixtures.data.DefaultData;
import io.stargate.sgv2.jsonapi.fixtures.types.CqlTypesForTesting;
import io.stargate.sgv2.jsonapi.service.shredding.JsonNamedValue;
import io.stargate.sgv2.jsonapi.service.shredding.JsonNamedValueContainer;
Expand Down Expand Up @@ -67,18 +69,34 @@ protected abstract List<JsonContainerFixture> getInternal(
List<ColumnMetadata> nonKeyMetadata);

/** Generate values for the given list of columns using the {@link CqlFixture} data generator. */
protected Map<ColumnMetadata, JsonNamedValue> columnValues(List<ColumnMetadata> columns) {
protected Map<ColumnMetadata, JsonNamedValue> columnValues(
Set<ColumnMetadata> primaryKeyColumns, List<ColumnMetadata> columns) {
// Collectors.toMap does not handle a null value in a map.

Map<ColumnMetadata, JsonNamedValue> values = new HashMap<>();
columns.forEach(
metadata -> {
var jsonNamedValue =
new JsonNamedValue(
// get the asInternal - we do not want any quotes, this is the value that would be
// pulled from JSON doc
JsonPath.rootBuilder().property(metadata.getName().asInternal()).build(),
cqlFixture.data().fromJSON(metadata.getType()));
JsonNamedValue jsonNamedValue = null;
// For AllNullValues FixtureData, we don't want to set primaryKey columns value as null or
// ignore,
// That will cause exception DocumentException.Code.MISSING_PRIMARY_KEY_COLUMNS
// So when this situation is tested, switch to DefaultData to get the non-null JsonLiteral
if (cqlFixture.data() instanceof AllNullValues allNullValues
&& primaryKeyColumns.contains(metadata)) {
jsonNamedValue =
new JsonNamedValue(
JsonPath.rootBuilder().property(metadata.getName().asInternal()).build(),
new DefaultData().fromJSON(metadata.getType()));
} else {
jsonNamedValue =
new JsonNamedValue(
// get the asInternal - we do not want any quotes, this is the value that would
// be
// pulled from JSON doc
JsonPath.rootBuilder().property(metadata.getName().asInternal()).build(),
cqlFixture.data().fromJSON(metadata.getType()));
}

values.put(metadata, jsonNamedValue);
});
return values;
Expand All @@ -97,7 +115,7 @@ protected JsonNamedValueContainer jsonContainer(
List<ColumnMetadata> setKeys, List<ColumnMetadata> setNonKeyColumns) {

var allSetColumns = join(setKeys, setNonKeyColumns);
var columnValues = columnValues(allSetColumns);
var columnValues = columnValues(new HashSet<>(setKeys), allSetColumns);

return new JsonNamedValueContainer(columnValues.values());
}
Expand Down