/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.chimera;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.EnumSet;
import org.dcache.acl.enums.AceFlags;
import org.dcache.acl.enums.RsType;
import org.dcache.chimera.posix.Stat;
/**
* H2 database specific dialect.
*/
public class H2FsSqlDriver extends FsSqlDriver {
public H2FsSqlDriver(DataSource dataSource) throws ChimeraFsException
{
super(dataSource);
}
@Override
Stat createInode(String id, int type, int uid, int gid, int mode, int nlink, long size)
{
/* H2 uses weird names for the column with the auto-generated key, so we cannot use the code
* in the base class.
*/
Timestamp now = new Timestamp(System.currentTimeMillis());
KeyHolder keyHolder = new GeneratedKeyHolder();
_jdbc.update(
con -> {
PreparedStatement ps = con.prepareStatement(
"INSERT INTO t_inodes (ipnfsid,itype,imode,inlink,iuid,igid,isize,iio," +
"ictime,iatime,imtime,icrtime,igeneration) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)",
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, id);
ps.setInt(2, type);
ps.setInt(3, mode & UnixPermission.S_PERMS);
ps.setInt(4, nlink);
ps.setInt(5, uid);
ps.setInt(6, gid);
ps.setLong(7, size);
ps.setInt(8, _ioMode);
ps.setTimestamp(9, now);
ps.setTimestamp(10, now);
ps.setTimestamp(11, now);
ps.setTimestamp(12, now);
ps.setLong(13, 0);
return ps;
}, keyHolder);
Stat stat = new Stat();
stat.setIno((Long) keyHolder.getKey());
stat.setId(id);
stat.setCrTime(now.getTime());
stat.setGeneration(0);
stat.setSize(size);
stat.setATime(now.getTime());
stat.setCTime(now.getTime());
stat.setMTime(now.getTime());
stat.setUid(uid);
stat.setGid(gid);
stat.setMode(mode & UnixPermission.S_PERMS | type);
stat.setNlink(nlink);
stat.setDev(17);
stat.setRdev(13);
return stat;
}
@Override
long createTagInode(int uid, int gid, int mode)
{
final String CREATE_TAG_INODE_WITHOUT_VALUE = "INSERT INTO t_tags_inodes (imode, inlink, iuid, igid, isize, " +
"ictime, iatime, imtime, ivalue) VALUES (?,1,?,?,0,?,?,?,NULL)";
Timestamp now = new Timestamp(System.currentTimeMillis());
KeyHolder keyHolder = new GeneratedKeyHolder();
int rc = _jdbc.update(
con -> {
PreparedStatement ps = con.prepareStatement(
CREATE_TAG_INODE_WITHOUT_VALUE, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, mode | UnixPermission.S_IFREG);
ps.setInt(2, uid);
ps.setInt(3, gid);
ps.setTimestamp(4, now);
ps.setTimestamp(5, now);
ps.setTimestamp(6, now);
return ps;
}, keyHolder);
if (rc != 1) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(CREATE_TAG_INODE_WITHOUT_VALUE, 1, rc);
}
/* H2 uses weird names for the column with the auto-generated key, so we cannot use the code
* in the base class.
*/
return (Long) keyHolder.getKey();
}
@Override
long createTagInode(int uid, int gid, int mode, byte[] value)
{
final String CREATE_TAG_INODE_WITH_VALUE = "INSERT INTO t_tags_inodes (imode, inlink, iuid, igid, isize, " +
"ictime, iatime, imtime, ivalue) VALUES (?,1,?,?,?,?,?,?,?)";
Timestamp now = new Timestamp(System.currentTimeMillis());
KeyHolder keyHolder = new GeneratedKeyHolder();
int rc = _jdbc.update(
con -> {
PreparedStatement ps = con.prepareStatement(
CREATE_TAG_INODE_WITH_VALUE, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, mode | UnixPermission.S_IFREG);
ps.setInt(2, uid);
ps.setInt(3, gid);
ps.setLong(4, value.length);
ps.setTimestamp(5, now);
ps.setTimestamp(6, now);
ps.setTimestamp(7, now);
ps.setBinaryStream(8, new ByteArrayInputStream(value), value.length);
return ps;
}, keyHolder);
if (rc != 1) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(CREATE_TAG_INODE_WITH_VALUE, 1, rc);
}
/* H2 uses weird names for the column with the auto-generated key, so we cannot use the code
* in the base class.
*/
return (Long) keyHolder.getKey();
}
/**
* copy all directory tags from origin directory to destination. New copy marked as inherited.
*
* @param orign
* @param destination
*/
@Override
void copyTags(FsInode orign, FsInode destination) {
_jdbc.update("INSERT INTO t_tags (inumber,itagid,isorign,itagname) (SELECT " + destination.ino() + ",itagid,0,itagname from t_tags WHERE inumber=?)",
orign.ino());
}
@Override
void copyAcl(FsInode source, FsInode inode, RsType type, EnumSet<AceFlags> mask, EnumSet<AceFlags> flags) {
int msk = mask.stream().mapToInt(AceFlags::getValue).reduce(0, (a, b) -> a | b);
int flgs = flags.stream().mapToInt(AceFlags::getValue).reduce(0, (a, b) -> a | b);
_jdbc.update("INSERT INTO t_acl (inumber,rs_type,type,flags,access_msk,who,who_id,ace_order) " +
"SELECT ?, ?, type, BITXOR(BITOR(flags, ?), ?), access_msk, who, who_id, ace_order " +
"FROM t_acl WHERE inumber = ? AND BITAND(flags, ?) > 0",
ps -> {
ps.setLong(1, inode.ino());
ps.setInt(2, type.getValue());
ps.setInt(3, msk);
ps.setInt(4, msk);
ps.setLong(5, source.ino());
ps.setInt(6, flgs);
});
}
@Override
public boolean isForeignKeyError(SQLException e) {
return "23506".endsWith(e.getSQLState());
}
}