What is the correct syntax to pass a null value for a timestamp column using Npgsql? - amazon-redshift

I have an AWS Redshift table with a timestamp type column.
create table if not exists testtimestampfields
(
testid varchar(50) not null,
creationdate timestamp null
);
Since the creationdate field permits null, I thought I could pass a null DateTime as the parameter value when inserting records.
[Fact]
public async Task Can_insert_null_timestamp_field()
{
// Custom class that calls GetClusterCredentials and returns a connection with the user and password...
await using var conn = await _redshiftConnectionManager.GetConnectionAsync();
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand(
#"insert into testtimestampfields(testid, creationdate) values (#testid, #creationdate);", conn);
var testidParam = cmd.Parameters.Add("#testid", NpgsqlDbType.Varchar);
var creationDateParam = cmd.Parameters.Add("#creationdate", NpgsqlDbType.Timestamp);
testidParam.Value = Guid.NewGuid().ToString();
creationDateParam.Value = null;
await cmd.ExecuteNonQueryAsync();
}
But when I try this await cmd.ExecuteNonQueryAsync(); fails with:
System.InvalidCastException: Parameter #creationdate must be set
System.InvalidCastException
Parameter #creationdate must be set
at Npgsql.NpgsqlParameter.ValidateAndGetLength()
at Npgsql.NpgsqlParameterCollection.ValidateAndBind(ConnectorTypeMapper typeMapper)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior, Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.ExecuteNonQuery(Boolean async, CancellationToken cancellationToken)
...
What is the correct syntax to pass a null value for a timestamp column using Npgsql?

Changing null to DBNull.Value works.
creationDateParam.Value = DBNull.Value;
Using a .NET DateTime? variable to set the parameter value conditionally:
creationDateParam.Value = dateTime.HasValue ? dateTime.Value : DBNull.Value;

Related

mybatis passing in the datatype on dynamic update query

I am trying to create a dynamic update statement using dynamic-sql method and sql-builder method but I only manage it get it work for string datatype. I'm not exactly sure how to "cast" to the correct datatype when constructing the update statement.
What I want to achieve is to generate the update statement using Map<String, Object> or the actual pojo Post
Post look like this
public class Post {
private Integer id;
private String title;
private String body;
private LocalDateTime createdAt;
private String createdBy;
private LocalDateTime updatedAt;
private String updatedBy;
}
Reason for Map<String, Object> is so that it's easier to iterate through the collection and construct the statement. Using the pojo would require me to use reflection which I try not to.
Before getting into how I did it
This is how when using a normal update statement with the pojo looks like
#PutMapping("/{id}")
public Post updateById(#PathVariable Integer id, #RequestBody Post post) {
return this.postService.updateById(id, post);
}
#Update("UPDATE POST SET title = #{p.title}, body = #{p.body}, createdAt = #{p.createdAt}, createdBy = #{p.createdBy}, updatedAt = #{p.updatedAt}, updatedBy = #{p.updatedBy} WHERE id = #{id}")
public boolean updateById(#Param("id") Integer id, #Param("p") Post post);
That would result in
2021-10-30 12:03:15.037 DEBUG 15988 --- [nio-8080-exec-2] c.b.s.s.post.PostMapper.updateById : ==> Preparing: UPDATE POST SET title = ?, body = ?, createdAt = ?, createdBy = ?, updatedAt = ?, updatedBy = ? WHERE id = ?
2021-10-30 12:03:15.064 DEBUG 15988 --- [nio-8080-exec-2] c.b.s.s.post.PostMapper.updateById : ==> Parameters: jsonpatch1(String), bo21(String), 2021-10-30T12:03:14.954483(LocalDateTime), stackoverflow(String), 2021-10-30T12:03:14.954483(LocalDateTime), stackoverflow(String), 65(Integer)
So with that, I tried to do this
// What this does is to strip off all the null values, and keep only those with value
// and convert into a map to pass and run in the dynamic sql later
#PatchMapping(path = "/{id}")
public Post patchById(#PathVariable Integer id, #RequestBody Post post) {
ObjectMapper om = new ObjectMapper();
om.setSerializationInclusion(Include.NON_NULL);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
Map<String, Object> mp = om.convertValue(post, new TypeReference<Map<String, Object>>(){});
return this.postService.patchById(id, mp);
}
Where it goes to a mapper that looks something like this
#Update({
"<script>",
"UPDATE POST",
"<set>",
"<foreach item='item' index='index' collection='p.entrySet()'>",
"${index} = #{item},",
"</foreach>",
"</set>",
"WHERE id = #{id}",
"</script>"
})
public boolean update(#Param("id") Integer id, #Param("p") Map<String, Object> post);
This works if all the values are string. However, if there is a field of LocalDateTime createdAt, the createdAt field is deem as a string type
021-10-30 15:21:27.666 DEBUG 12324 --- [nio-8080-exec-2] c.b.s.s.post.PostUpdateMapper.update : ==> Preparing: UPDATE POST SET createdAt = ?, title = ?, body = ?
WHERE id = ?
2021-10-30 15:21:27.669 DEBUG 12324 --- [nio-8080-exec-2] c.b.s.s.post.PostUpdateMapper.update : ==> Parameters: 2021-09-10T11:31:07.5306869(String), jsonpatch1(String), bo221(String), 65(Integer)
I believe, that is because I switch it to Map<String, Object> and hence the type (LocalDateTime) is loss with the conversion. However, if I were to do it using the pojo Bean
I would have something like this
#PatchMapping(path = "/{id}")
public Post patchById(#PathVariable Integer id, #RequestBody Post post) {
return this.postService.patchById(id, post);
}
#UpdateProvider(type=SQLUpdate.class, method = "update")
public boolean update(Integer id, Post post);
// just a poc to see if it works
public String update(Integer id, Post post) throws IllegalArgumentException, IllegalAccessException {
Field[] f = post.getClass().getDeclaredFields();
return new SQL() {{
UPDATE("POST");
for(Field field: f) {
field.setAccessible(true);
if (field.get(post) != null) {
SET(field.getName() + " = '" + field.get(post) + "'");
}
}
WHERE("id = " + id);
}}.toString();
}
So either way, I'm not sure how to pass in the correct type so that it can intercept and run correctly
This would be the more ideal solution if I can achieve this
#Update({
"<script>",
"UPDATE POST",
"<set>",
// being able to check if the value is null and set the field and value dynamically
"<if test='#{p.value} != null>p.fieldname = #{p.value}",
"</set>",
"WHERE id = #{id}",
"</script>"
})
public boolean update(#Param("id") Integer id, #Param("p") Post post);
Let me know if more information is needed, or if there is a better way to achieve what I want to do
Thanks!
P.S: I know and I got it working with mybatis-dynamic-sql lib but interested to know if it cab work without using the lib

i want to enter data in a complete column without according to row data

i'm using sq lite database in android
i already have data in two columns i want to change data in column 2 without condition of where
where ever i searched where condition is used
this is how i stored
public boolean updateData(String name, String pass){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(col2,pass);
db.update(TABLE_NAME,contentValues, col1+" =?", new String[]{name});
return true;
}
Pass null for the 3d and 4th arguments:
public boolean updateData(String pass) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(col2, pass);
db.update(TABLE_NAME, contentValues, null, null);
db.close();
return true;
}
I guess you don't need the parameter name since there is no WHERE clause, so I removed it.

EFCore.BulkExtensions fails to do insert with Guid PK

I am using EFCore.BulkExtensions to insert an entity with a GUID / UniqueIdentifier primary key into my database, but I am receiving the following exception when I call this line:
System.InvalidOperationException HResult=0x80131509 Message=The
given value of type String from the data source cannot be converted to
type uniqueidentifier of the specified target column.
Source=EFCore.BulkExtensions StackTrace: at
EFCore.BulkExtensions.SqlBulkOperation.Insert[T](DbContext context,
IList1 entities, TableInfo tableInfo, Action1 progress) at
EFCore.BulkExtensions.DbContextBulkExtensions.BulkInsert[T](DbContext
context, IList1 entities, BulkConfig bulkConfig, Action1 progress)
at
MyCompany.Abp.EfCore.BulkExtensions.BulkInsert[TEntity,TPrimaryKey](IRepository2
repository, IList1 entities, BulkConfig bulkConfig, Action1
progress) in
D:\Projects\MySystem\Utilities\MyCompany.Abp.EfCore\BulkExtensions.BulkInsert.cs:line
21 at
s.SearchServices.Infrastructure.UnscConsolidatedList.Infrastructure.s.MonitoringBatch.MonitoringBatchSearchJob.PerformScreening(Guid
sourceId, IList1 Records) in
D:\Projects\MySystem\Modules\s\s.SearchServices.Infrastructure.UnscConsolidatedList\Infrastructure\s\MonitoringBatch\MonitoringBatchSearchJob.cs:line
96 at
s.SearchServices.Infrastructure.UnscConsolidatedList.Infrastructure.s.MonitoringBatch.MonitoringBatchSearchJob.ExecuteJob(MonitoringBatchSearhJobArgs
args) in
D:\Projects\MySystem\Modules\s\s.SearchServices.Infrastructure.UnscConsolidatedList\Infrastructure\s\MonitoringBatch\MonitoringBatchSearchJob.cs:line
82 at
s.SearchServices.Infrastructure.UnscConsolidatedList.Core.BatchSearchJobBase`1.Execute(TBatchSearhJobArgs
args) in
D:\Projects\MySystem\Modules\s\s.SearchServices.Infrastructure.UnscConsolidatedList\Core\BatchSearchJobBase.cs:line
28
Inner Exception 1: InvalidCastException: Failed to convert parameter
value from a String to a Guid.
Inner Exception 2: InvalidCastException: Invalid cast from
'System.String' to 'System.Guid'.
The code that throws the exception:
public static void BulkInsert<TEntity, TPrimaryKey>(
this IRepository<TEntity, TPrimaryKey> repository,
IList<TEntity> entities,
BulkConfig bulkConfig = null,
Action<decimal> progress = null)
where TEntity : class, IEntity<TPrimaryKey>
{
var db = repository.GetDbContext();
db.BulkInsert(entities, bulkConfig, progress); // <== throws exception
}
What am I doing wrong?

return a boolean - jdbcTemplate

I would like to return a boolean value using in this method:
public Boolean isSizeOk(String transactionId){
String sqlQuery = "SELECT true FROM customer_pool WHERE id = "+ transactionID + " AND level = 13)";
//The next line is the problem.
//If I am returning a Boolean List, I can write
List <Boolean> sizeResult = jdbcTemplate.queryForList(sqlQuery, Boolean.class, transactionId);
//But since I only want a boolean value, what will the statement be?
Boolean sizeResult = jdbcTemplate......?
return sizeResult;
}
Kindly help. Thanks.
If you want to write a method that checks that a record exists in the database you can use the following code:
Integer cnt = jdbcTemplate.queryForObject(
"SELECT count(*) FROM customer_pool WHERE id = ? AND level = 13)", Integer.class, id);
return cnt != null && cnt > 0
Counting rows in SQL just in order to get simple information about non-emptiness of result may be unnecessary overkill, you want just ask result set for first row and finish. For simple queries by primary key or other index the performance might be similar, however, for complex queries, or full table scan queries it might be slow. In Spring I prefer simple utility method
public boolean exists(String sql, Object... args) {
boolean result = query(sql, args, new ResultSetExtractor<Boolean>() {
#Override
public Boolean extractData(ResultSet rs) throws SQLException,DataAccessException {
boolean result = rs.next();
return result;
}
});
return result;
}
(Google "sql exists vs count" for more info.)
What about
// Change query accordingly
String query = "SELECT 1 FROM " + tableName + " WHERE " + idColumnName + " = ? LIMIT 1";
try {
jdbcTemplate.queryForObject(query, new Object[]{id}, Long.class);
return true;
} catch (EmptyResultDataAccessException e) {
return false;
}
Case 1: In case you are returning boolean:
Just check the size of sizeResult List, if the size is greater than 0 return true else return false.
Case 2: If you are returning boolean list then return type must be a boolean List.You must
write the method as:
public List<Boolean> isSizeOk(String transactionId, int sizeLimit){
String sqlQuery = "SELECT true FROM customer_pool WHERE id = ? AND level = 13)";
List <Boolean> sizeResult = jdbcTemplate.queryForList(sqlQuery, Boolean.class, transactionId);
Boolean sizeResult = jdbcTemplate......?
return sizeResult;
}

.NET 4.5 SessionAuthenticationModule - Issue with "validFrom"

I'm using SWT in Windows Azure ACS and custom SwtHandler is used in Relying Party applicaiton to handle incoming SWT token. While it re-creates the SWT token at relying party, I get an error with creation of SessionSecurityToken related to value of validFrom attribute.
I've tried out following values for ValidFrom, but didn't fix the problem.
DateTime SwtBaseTime = new DateTime( 1970, 1, 1, 0, 0, 0, 0 );
DateTime.UtcNow
DateTime.MinValue
Specified argument was out of the range of valid values.
Parameter name: validFrom
[ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: validFrom]
System.IdentityModel.Tokens.SessionSecurityToken..ctor(ClaimsPrincipal claimsPrincipal, UniqueId contextId, String id, String context, Byte[] key, String endpointId, Nullable1 validFrom, Nullable1 validTo, UniqueId keyGeneration, Nullable1 keyEffectiveTime, Nullable1 keyExpirationTime, SctAuthorizationPolicy sctAuthorizationPolicy, Uri securityContextSecurityTokenWrapperSecureConversationVersion) +1009610
System.IdentityModel.Tokens.SessionSecurityToken..ctor(ClaimsPrincipal claimsPrincipal, UniqueId contextId, String context, String endpointId, Nullable1 validFrom, Nullable1 validTo, SymmetricSecurityKey key) +317
System.IdentityModel.Tokens.SessionSecurityTokenHandler.CreateSessionSecurityToken(ClaimsPrincipal principal, String context, String endpointId, DateTime validFrom, DateTime validTo) +306
System.IdentityModel.Services.SessionAuthenticationModule.CreateSessionSecurityToken(ClaimsPrincipal principal, String context, DateTime validFrom, DateTime validTo, Boolean isPersistent) +313
System.IdentityModel.Services.WSFederationAuthenticationModule.SignInWithResponseMessage(HttpRequestBase request) +1079
System.IdentityModel.Services.WSFederationAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs args) +123924
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
I ran into a similar problem recreating the session security token while trying to implement sliding expiration for the SessionAuthenticationModule.
protected void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
DateTime now = DateTime.UtcNow;
DateTime validFrom = e.SessionToken.ValidFrom;
DateTime validTo = e.SessionToken.ValidTo;
TimeSpan sessionLifetime = validTo.Subtract(e.SessionToken.ValidFrom);
bool sessionTimeHasExpired = now > validTo;
bool sessionTimeIsHalfExpired = now > validFrom.AddMinutes(sessionLifetime.TotalMinutes / 2);
// http://www.michael-mckenna.com/Blog/2013/2/the-problem-with-absolute-token-expiration-in-windows-identity-foundation-wif
if (!sessionTimeHasExpired && sessionTimeIsHalfExpired)
{
// If the session has not expired but the session lifetime is already half spent, reissue the cookie.
e.SessionToken = (sender as SessionAuthenticationModule).CreateSessionSecurityToken(e.SessionToken.ClaimsPrincipal, e.SessionToken.Context,
now, now.AddMinutes(sessionLifetime.TotalMinutes), e.SessionToken.IsPersistent);
e.ReissueCookie = true;
}
}
The CreateSessionSecurityToken method takes a value for validFrom and validTo. If these two values are equal, it throws an ArgumentOutOfRange exception.
I ran into the problem because originally I was using sessionLifetime.Minutes (which was 0) rather than sessionLifetime.TotalMinutes (which was 100).
Try using the same KeyEffectiveTime
e.SessionToken = sam.CreateSessionSecurityToken(
e.SessionToken.ClaimsPrincipal,
e.SessionToken.Context,
e.SessionToken.KeyEffectiveTime,
e.SessionToken.KeyExpirationTime.AddHours(8),
e.SessionToken.IsPersistent);