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
|
# frozen-string-literal: true
#
# The identifier_mangling extension adds support for to change
# the default identifier mangling for datasets, as well as all
# datasets for a given database.
#
# # Use uppercase identifiers in database, and lowercase in ruby.
# # Default behavior of Sequel, as the SQL standard behavior
# # folds unquoted identifiers to uppercase.
# DB.identifier_input_method = :upcase
# DB.identifier_output_method = :downcase
#
# # Don't modify identifiers.
# # Default behavior of Sequel on PostgreSQL, MySQL, SQLite,
# # as they fold unquoted identifiers to lowercase.
# DB.identifier_input_method = nil
# DB.identifier_output_method = nil
#
# You can also choose to turn on or off identifier quoting:
#
# # Quote identifiers. Sequel's default behavior.
# DB.quote_identifiers = true
#
# # Don't quote identifiers. Sequel's default behavior on DB2.
# DB.quote_identifiers = false
#
# To modify the identifiers on a per-dataset basis:
#
# ds = DB[:a].with_input_indentifier(:upcase).
# with_output_identifier(:downcase).
# with_quote_identifiers(true)
#
# To load the extension into the database:
#
# DB.extension :identifier_mangling
#
# Related modules: Sequel::IdentifierMangling::DatabaseMethods,
# Sequel::IdentifierMangling::DatasetMethods
#
module Sequel
module IdentifierMangling
module DatabaseMethods
def self.extended(db)
db.instance_exec do
@identifier_input_method = nil
@identifier_output_method = nil
@quote_identifiers = nil
reset_identifier_mangling
extend_datasets(DatasetMethods)
end
end
# The identifier input method to use by default for this database (default: adapter default)
attr_reader :identifier_input_method
# The identifier output method to use by default for this database (default: adapter default)
attr_reader :identifier_output_method
# Set the method to call on identifiers going into the database:
#
# DB[:items] # SELECT * FROM items
# DB.identifier_input_method = :upcase
# DB[:items] # SELECT * FROM ITEMS
def identifier_input_method=(v)
reset_default_dataset
@identifier_input_method = v
end
# Set the method to call on identifiers coming from the database:
#
# DB[:items].first # {:id=>1, :name=>'foo'}
# DB.identifier_output_method = :upcase
# DB[:items].first # {:ID=>1, :NAME=>'foo'}
def identifier_output_method=(v)
reset_default_dataset
@identifier_output_method = v
end
# Set whether to quote identifiers (columns and tables) for this database:
#
# DB[:items] # SELECT * FROM items
# DB.quote_identifiers = true
# DB[:items] # SELECT * FROM "items"
def quote_identifiers=(v)
reset_default_dataset
@quote_identifiers = v
end
# Returns true if the database quotes identifiers.
def quote_identifiers?
@quote_identifiers
end
private
# Return a dataset that uses the default identifier input and output methods
# for this database. Used when parsing metadata so that column symbols are
# returned as expected.
def _metadata_dataset
super.
with_identifier_input_method(identifier_input_method_default).
with_identifier_output_method(identifier_output_method_default)
end
# Upcase identifiers on input if database folds unquoted identifiers to
# uppercase.
def identifier_input_method_default
return super if defined?(super)
:upcase if folds_unquoted_identifiers_to_uppercase?
end
# Downcase identifiers on output if database folds unquoted identifiers to
# uppercase.
def identifier_output_method_default
return super if defined?(super)
:downcase if folds_unquoted_identifiers_to_uppercase?
end
# Reset the identifier mangling options. Overrides any already set on
# the instance. Only for internal use by shared adapters.
def reset_identifier_mangling
@quote_identifiers = @opts.fetch(:quote_identifiers, quote_identifiers_default)
@identifier_input_method = @opts.fetch(:identifier_input_method, identifier_input_method_default)
@identifier_output_method = @opts.fetch(:identifier_output_method, identifier_output_method_default)
reset_default_dataset
end
end
module DatasetMethods
# The String instance method to call on identifiers before sending them to
# the database.
def identifier_input_method
@opts.fetch(:identifier_input_method, db.identifier_input_method)
end
# The String instance method to call on identifiers before sending them to
# the database.
def identifier_output_method
@opts.fetch(:identifier_output_method, db.identifier_output_method)
end
# Check with the database to see if identifier quoting is enabled
def quote_identifiers?
@opts.fetch(:quote_identifiers, db.quote_identifiers?)
end
# Return a modified dataset with identifier_input_method set.
def with_identifier_input_method(meth)
clone(:identifier_input_method=>meth, :skip_symbol_cache=>true)
end
# Return a modified dataset with identifier_output_method set.
def with_identifier_output_method(meth)
clone(:identifier_output_method=>meth)
end
private
# Convert the identifier to the version used in the database via
# identifier_input_method.
def input_identifier(v)
(i = identifier_input_method) ? v.to_s.public_send(i) : v.to_s
end
# Modify the identifier returned from the database based on the
# identifier_output_method.
def output_identifier(v)
v = 'untitled' if v == ''
(i = identifier_output_method) ? v.to_s.public_send(i).to_sym : v.to_sym
end
def non_sql_option?(key)
super || key == :identifier_input_method || key == :identifier_output_method
end
end
end
Database.register_extension(:identifier_mangling, IdentifierMangling::DatabaseMethods)
end
|