1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
# frozen-string-literal: true
Sequel::JDBC.load_driver('org.h2.Driver', :H2)
module Sequel
module JDBC
Sequel.synchronize do
DATABASE_SETUP[:h2] = proc do |db|
db.extend(Sequel::JDBC::H2::DatabaseMethods)
db.dataset_class = Sequel::JDBC::H2::Dataset
org.h2.Driver
end
end
module H2
module DatabaseMethods
def commit_prepared_transaction(transaction_id, opts=OPTS)
run("COMMIT TRANSACTION #{transaction_id}", opts)
end
def database_type
:h2
end
def freeze
h2_version
version2?
super
end
def h2_version
@h2_version ||= get(Sequel.function(:H2VERSION))
end
def rollback_prepared_transaction(transaction_id, opts=OPTS)
run("ROLLBACK TRANSACTION #{transaction_id}", opts)
end
# H2 uses an IDENTITY type for primary keys
def serial_primary_key_options
{:primary_key => true, :type => :identity, :identity=>true}
end
# H2 supports CREATE TABLE IF NOT EXISTS syntax
def supports_create_table_if_not_exists?
true
end
# H2 supports prepared transactions
def supports_prepared_transactions?
true
end
# H2 supports savepoints
def supports_savepoints?
true
end
private
# H2 does not allow adding primary key constraints to NULLable columns.
def can_add_primary_key_constraint_on_nullable_columns?
false
end
# If the :prepare option is given and we aren't in a savepoint,
# prepare the transaction for a two-phase commit.
def commit_transaction(conn, opts=OPTS)
if (s = opts[:prepare]) && savepoint_level(conn) <= 1
log_connection_execute(conn, "PREPARE COMMIT #{s}")
else
super
end
end
def alter_table_sql(table, op)
case op[:op]
when :add_column
if (pk = op.delete(:primary_key)) || (ref = op.delete(:table))
if pk
op[:null] = false
end
sqls = [super(table, op)]
if pk && (h2_version >= '1.4' || op[:type] != :identity)
# H2 needs to add a primary key column as a constraint in this case
sqls << "ALTER TABLE #{quote_schema_table(table)} ADD PRIMARY KEY (#{quote_identifier(op[:name])})"
end
if ref
op[:table] = ref
constraint_name = op[:foreign_key_constraint_name]
sqls << "ALTER TABLE #{quote_schema_table(table)} ADD#{" CONSTRAINT #{quote_identifier(constraint_name)}" if constraint_name} FOREIGN KEY (#{quote_identifier(op[:name])}) #{column_references_sql(op)}"
end
sqls
else
super(table, op)
end
when :rename_column
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} RENAME TO #{quote_identifier(op[:new_name])}"
when :set_column_null
"ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET#{' NOT' unless op[:null]} NULL"
when :set_column_type
if sch = schema(table)
if cs = sch.each{|k, v| break v if k == op[:name]; nil}
cs = cs.dup
cs[:default] = cs[:ruby_default]
op = cs.merge!(op)
end
end
sql = "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}".dup
column_definition_order.each{|m| send(:"column_definition_#{m}_sql", sql, op)}
sql
when :drop_constraint
if op[:type] == :primary_key
"ALTER TABLE #{quote_schema_table(table)} DROP PRIMARY KEY"
else
super(table, op)
end
else
super(table, op)
end
end
# Default to a single connection for a memory database.
def connection_pool_default_options
o = super
uri == 'jdbc:h2:mem:' ? o.merge(:max_connections=>1) : o
end
DATABASE_ERROR_REGEXPS = {
/Unique index or primary key violation/ => UniqueConstraintViolation,
/Referential integrity constraint violation/ => ForeignKeyConstraintViolation,
/Check constraint violation/ => CheckConstraintViolation,
/NULL not allowed for column/ => NotNullConstraintViolation,
/Deadlock detected\. The current transaction was rolled back\./ => SerializationFailure,
}.freeze
def database_error_regexps
DATABASE_ERROR_REGEXPS
end
def execute_statement_insert(stmt, sql)
stmt.executeUpdate(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS)
end
def prepare_jdbc_statement(conn, sql, opts)
opts[:type] == :insert ? conn.prepareStatement(sql, JavaSQL::Statement::RETURN_GENERATED_KEYS) : super
end
# Get the last inserted id using getGeneratedKeys, scope_identity, or identity.
def last_insert_id(conn, opts=OPTS)
if stmt = opts[:stmt]
rs = stmt.getGeneratedKeys
begin
if rs.next
begin
rs.getLong(1)
rescue
rs.getObject(1) rescue nil
end
end
ensure
rs.close
end
elsif !version2?
statement(conn) do |stmt|
sql = 'SELECT IDENTITY()'
rs = log_connection_yield(sql, conn){stmt.executeQuery(sql)}
rs.next
rs.getLong(1)
end
end
end
def primary_key_index_re
/\Aprimary_key/i
end
# H2 does not support named column constraints.
def supports_named_column_constraints?
false
end
# Use BIGINT IDENTITY for identity columns that use :Bignum type
def type_literal_generic_bignum_symbol(column)
column[:identity] ? 'BIGINT AUTO_INCREMENT' : super
end
def version2?
return @version2 if defined?(@version2)
@version2 = h2_version.to_i >= 2
end
end
class Dataset < JDBC::Dataset
ILIKE_PLACEHOLDER = ["CAST(".freeze, " AS VARCHAR_IGNORECASE)".freeze].freeze
# Emulate the case insensitive LIKE operator and the bitwise operators.
def complex_expression_sql_append(sql, op, args)
case op
when :ILIKE, :"NOT ILIKE"
super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), [SQL::PlaceholderLiteralString.new(ILIKE_PLACEHOLDER, [args[0]]), args[1]])
when :&, :|, :^, :<<, :>>, :'B~'
complex_expression_emulate_append(sql, op, args)
else
super
end
end
# H2 does not support derived column lists
def supports_derived_column_lists?
false
end
# H2 requires SQL standard datetimes
def requires_sql_standard_datetimes?
true
end
# H2 doesn't support IS TRUE
def supports_is_true?
false
end
# H2 doesn't support JOIN USING
def supports_join_using?
false
end
# H2 supports MERGE
def supports_merge?
true
end
# H2 doesn't support multiple columns in IN/NOT IN
def supports_multiple_column_in?
false
end
private
# H2 expects hexadecimal strings for blob values
def literal_blob_append(sql, v)
if db.send(:version2?)
super
else
sql << "'" << v.unpack("H*").first << "'"
end
end
def literal_false
'FALSE'
end
def literal_true
'TRUE'
end
# H2 handles fractional seconds in timestamps, but not in times
def literal_sqltime(v)
v.strftime("'%H:%M:%S'")
end
# H2 supports multiple rows in INSERT.
def multi_insert_sql_strategy
:values
end
def select_only_offset_sql(sql)
if db.send(:version2?)
super
else
sql << " LIMIT -1 OFFSET "
literal_append(sql, @opts[:offset])
end
end
# H2 supports quoted function names.
def supports_quoted_function_names?
true
end
end
end
end
end
|