sequel-pg-comment 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.yardopts +1 -0
- data/Gemfile +3 -0
- data/Guardfile +15 -0
- data/LICENCE +674 -0
- data/README.md +209 -0
- data/Rakefile +39 -0
- data/lib/sequel/extensions/pg_comment.rb +73 -0
- data/lib/sequel/extensions/pg_comment/alter_table_generator_methods.rb +126 -0
- data/lib/sequel/extensions/pg_comment/create_table_generator_methods.rb +159 -0
- data/lib/sequel/extensions/pg_comment/database_methods.rb +175 -0
- data/lib/sequel/extensions/pg_comment/dataset_methods.rb +22 -0
- data/lib/sequel/extensions/pg_comment/sql_generator.rb +257 -0
- data/sequel-pg-comment.gemspec +36 -0
- data/spec/alter_table_comment_spec.rb +124 -0
- data/spec/comment_for_spec.rb +34 -0
- data/spec/comment_on_spec.rb +56 -0
- data/spec/create_join_table_comment_spec.rb +20 -0
- data/spec/create_table_comment_spec.rb +193 -0
- data/spec/create_view_comment_spec.rb +18 -0
- data/spec/extension_spec.rb +11 -0
- data/spec/normalise_comment_spec.rb +48 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/sql_generator_spec.rb +96 -0
- metadata +242 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
#:nodoc:
|
2
|
+
# Enhancements to the standard schema modification methods in a
|
3
|
+
# block-form `create_table` method, to support setting comments via the
|
4
|
+
# `:comment` option.
|
5
|
+
#
|
6
|
+
module Sequel::Extension::PgComment::CreateTableGeneratorMethods
|
7
|
+
# An array of all the comments that this generator has seen fit to
|
8
|
+
# create.
|
9
|
+
#
|
10
|
+
# @return [Array<SqlGenerator>]
|
11
|
+
#
|
12
|
+
attr_reader :comments
|
13
|
+
|
14
|
+
include Sequel::Extension::PgComment
|
15
|
+
|
16
|
+
# Enhanced version of the `column` table definition method, which
|
17
|
+
# supports setting a comment on the column.
|
18
|
+
#
|
19
|
+
# @option [String] :comment The comment to set on the column that is
|
20
|
+
# being defined.
|
21
|
+
#
|
22
|
+
def column(*args)
|
23
|
+
super
|
24
|
+
|
25
|
+
if args.last.is_a?(Hash) && args.last[:comment]
|
26
|
+
comments << SqlGenerator.create(:column, args.first, args.last[:comment])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Enhanced version of the `primary_key` table definition method, which
|
31
|
+
# supports setting a comment on either the column or constraint.
|
32
|
+
#
|
33
|
+
# If the primary key is composite (`name` is an array), then the comment
|
34
|
+
# will be placed on the index. Otherwise, the comment will be set
|
35
|
+
# on the column itself.
|
36
|
+
#
|
37
|
+
# @option [String] :comment The comment to set on the column or
|
38
|
+
# index that is being defined.
|
39
|
+
#
|
40
|
+
def primary_key(name, *args)
|
41
|
+
if args.last.is_a?(Hash) && args.last[:comment] and !name.is_a? Array
|
42
|
+
# The composite primary key case will be handled by the
|
43
|
+
# `composite_primary_key` method, so we don't have to deal with it
|
44
|
+
# here.
|
45
|
+
comments << SqlGenerator.create(:column, name, args.last[:comment])
|
46
|
+
end
|
47
|
+
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Enhanced version of the `composite_primary_key` table definition method,
|
52
|
+
# which supports setting a comment on the primary key index.
|
53
|
+
#
|
54
|
+
# @option [String] :comment The comment to set on the primary key index.
|
55
|
+
#
|
56
|
+
def composite_primary_key(columns, *args)
|
57
|
+
if args.last.is_a?(Hash) and args.last[:comment]
|
58
|
+
if args.last[:name]
|
59
|
+
comments << SqlGenerator.create(
|
60
|
+
:index,
|
61
|
+
args.last[:name],
|
62
|
+
args.last[:comment]
|
63
|
+
)
|
64
|
+
else
|
65
|
+
comments << PrefixSqlGenerator.new(:index, :_pkey, args.last[:comment])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
# Enhanced version of the `composite_foreign_key` table definition method,
|
73
|
+
# which supports setting a comment on the FK constraint.
|
74
|
+
#
|
75
|
+
# @option [String] :comment The comment to set on the foreign key constraint.
|
76
|
+
#
|
77
|
+
def composite_foreign_key(columns, opts)
|
78
|
+
if opts.is_a?(Hash) and opts[:comment] and opts[:table]
|
79
|
+
if opts[:name]
|
80
|
+
comments << SqlGenerator.create(
|
81
|
+
:constraint,
|
82
|
+
opts[:name],
|
83
|
+
opts[:comment]
|
84
|
+
)
|
85
|
+
else
|
86
|
+
comments << SqlGenerator.create(
|
87
|
+
:constraint,
|
88
|
+
"#{opts[:table]}_#{columns.first}_fkey".to_sym,
|
89
|
+
opts[:comment]
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
super
|
95
|
+
end
|
96
|
+
|
97
|
+
# Enhanced version of the `index` table definition method,
|
98
|
+
# which supports setting a comment on the index.
|
99
|
+
#
|
100
|
+
# @option [String] :comment The comment to set on the index that is being
|
101
|
+
# defined.
|
102
|
+
#
|
103
|
+
def index(columns, opts = OPTS)
|
104
|
+
if opts[:comment]
|
105
|
+
if opts[:name]
|
106
|
+
comments << SqlGenerator.create(:index, opts[:name], opts[:comment])
|
107
|
+
else
|
108
|
+
comments << PrefixSqlGenerator.new(
|
109
|
+
:index,
|
110
|
+
("_" + [columns].flatten.map(&:to_s).join('_') + "_index").to_sym,
|
111
|
+
opts[:comment]
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
# Enhanced version of the `unique` table definition method,
|
120
|
+
# which supports setting a comment on the unique index.
|
121
|
+
#
|
122
|
+
# @option [String] :comment The comment to set on the index that will be
|
123
|
+
# defined.
|
124
|
+
#
|
125
|
+
def unique(columns, opts = OPTS)
|
126
|
+
if opts[:comment]
|
127
|
+
if opts[:name]
|
128
|
+
comments << SqlGenerator.create(:index, opts[:name], opts[:comment])
|
129
|
+
else
|
130
|
+
comments << PrefixSqlGenerator.new(
|
131
|
+
:index,
|
132
|
+
("_" + [columns].flatten.map(&:to_s).join('_') + "_key").to_sym,
|
133
|
+
opts[:comment]
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
super
|
139
|
+
end
|
140
|
+
|
141
|
+
# Enhanced version of the `constraint` table definition method,
|
142
|
+
# which supports setting a comment on the constraint.
|
143
|
+
#
|
144
|
+
# @option [String] :comment The comment to set on the constraint that is
|
145
|
+
# being defined.
|
146
|
+
#
|
147
|
+
def constraint(name, *args, &block)
|
148
|
+
opts = name.is_a?(Hash) ? name : (args.last.is_a?(Hash) ? args.last : {})
|
149
|
+
|
150
|
+
if opts[:comment]
|
151
|
+
if name
|
152
|
+
comments << SqlGenerator.create(:constraint, name, opts[:comment])
|
153
|
+
else
|
154
|
+
raise RuntimeError,
|
155
|
+
"Setting comments on unnamed or check constraints is not supported"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# Support for setting and retrieving comments on all object types
|
2
|
+
# in a PostgreSQL database.
|
3
|
+
#
|
4
|
+
module Sequel::Extension::PgComment::DatabaseMethods
|
5
|
+
# Set the comment for a database object.
|
6
|
+
#
|
7
|
+
# @param type [#to_s] The type of object that you wish to comment on.
|
8
|
+
# This can either be a string or symbol. Any object type that PgSQL
|
9
|
+
# knows about should be fair game. The current list of object types
|
10
|
+
# that this plugin knows about (and hence will accept) is listed in
|
11
|
+
# the {Sequel::Extension::PgComment::OBJECT_TYPES} array.
|
12
|
+
#
|
13
|
+
# @param id [#to_s] The name of the object that you wish to comment on.
|
14
|
+
# For most types of object, this should be the literal name of the
|
15
|
+
# object. However, for columns in a table or view, you should separate
|
16
|
+
# the table/view name from the column name with a double underscore
|
17
|
+
# (ie `__`). This is the standard Sequel convention for such things.
|
18
|
+
#
|
19
|
+
# @param comment [String] The comment you wish to set for the database
|
20
|
+
# object.
|
21
|
+
#
|
22
|
+
# @see {Sequel::Extension::PgComment.normalise_comment} for details on
|
23
|
+
# how the comment string is interpreted.
|
24
|
+
#
|
25
|
+
def comment_on(type, id, comment)
|
26
|
+
gen = begin
|
27
|
+
Sequel::Extension::PgComment::SqlGenerator.create(type, id, comment)
|
28
|
+
rescue ArgumentError
|
29
|
+
raise ArgumentError,
|
30
|
+
"Invalid object type: #{type.inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
execute(gen.generate)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Retrieve the comment for a database object.
|
37
|
+
#
|
38
|
+
# @param object [#to_s] The name of the database object to retrieve. For
|
39
|
+
# most objects, this should be the literal name of the object.
|
40
|
+
# However, for columns on tables and views, the name of the table/view
|
41
|
+
# should be a separated from the name of the column by a double
|
42
|
+
# underscore (ie `__`).
|
43
|
+
#
|
44
|
+
# @return [String, NilClass] The comment on the object, or `nil` if no
|
45
|
+
# comment has been defined for the object.
|
46
|
+
#
|
47
|
+
def comment_for(object)
|
48
|
+
object = object.to_s
|
49
|
+
|
50
|
+
if object.index("__")
|
51
|
+
tbl, col = object.split("__", 2)
|
52
|
+
|
53
|
+
execute("SELECT col_description(c.oid, a.attnum) " +
|
54
|
+
"FROM pg_class c JOIN pg_attribute a " +
|
55
|
+
"ON (c.oid=a.attrelid) " +
|
56
|
+
"WHERE c.relname=#{literal(tbl)} " +
|
57
|
+
"AND a.attname=#{literal(col)}"
|
58
|
+
)
|
59
|
+
else
|
60
|
+
execute("SELECT obj_description(#{literal(object.to_s)}::regclass, 'pg_class')")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# An enhanced form of the standard `create_table` method, which supports
|
65
|
+
# setting a comment in the `create_table` call when the `:comment` option
|
66
|
+
# is provided.
|
67
|
+
#
|
68
|
+
# @option [String] :comment The comment to set on the newly-created table.
|
69
|
+
#
|
70
|
+
# @see [Sequel::Database#create_table](http://sequel.jeremyevans.net/rdoc/classes/Sequel/Database.html#method-i-create_table)
|
71
|
+
#
|
72
|
+
def create_table(*args)
|
73
|
+
super
|
74
|
+
|
75
|
+
if args.last.is_a?(Hash) && args.last[:comment]
|
76
|
+
comment_on(:table, args.first, args.last[:comment])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#:nodoc:
|
81
|
+
# Enhanced version to support setting comments on objects created in a
|
82
|
+
# block-form `create_table` statement.
|
83
|
+
#
|
84
|
+
def create_table_generator(&block)
|
85
|
+
super do
|
86
|
+
extend Sequel::Extension::PgComment::CreateTableGeneratorMethods
|
87
|
+
@comments = []
|
88
|
+
instance_eval(&block) if block
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#:nodoc:
|
93
|
+
# Enhanced version to support setting comments on objects created in a
|
94
|
+
# block-form `create_table` statement.
|
95
|
+
#
|
96
|
+
# If you're wondering why we override the
|
97
|
+
# create_table_indexes_from_generator method, rather than
|
98
|
+
# create_table_from_generator, it's because the indexes method runs last,
|
99
|
+
# and we can only create our comments after the objects we're commenting
|
100
|
+
# on have been created. We *could* set some comments in
|
101
|
+
# create_table_from_generator, and then set index comments in
|
102
|
+
# create_table_indexes_from_generator, but why override two methods when
|
103
|
+
# you can just override one to get the same net result?
|
104
|
+
#
|
105
|
+
def create_table_indexes_from_generator(name, generator, options)
|
106
|
+
super
|
107
|
+
|
108
|
+
generator.comments.each do |sql_gen|
|
109
|
+
if sql_gen.respond_to? :table_name
|
110
|
+
sql_gen.table_name = name
|
111
|
+
end
|
112
|
+
|
113
|
+
execute(sql_gen.generate)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#:nodoc:
|
118
|
+
# Enhanced version to support setting comments on objects created in a
|
119
|
+
# block-form `alter_table` statement.
|
120
|
+
#
|
121
|
+
def alter_table_generator(&block)
|
122
|
+
super do
|
123
|
+
extend Sequel::Extension::PgComment::AlterTableGeneratorMethods
|
124
|
+
@comments = []
|
125
|
+
instance_eval(&block) if block
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#:nodoc:
|
130
|
+
# Enhanced version to support setting comments on objects created in a
|
131
|
+
# block-form `alter_table` statement.
|
132
|
+
#
|
133
|
+
def apply_alter_table_generator(name, generator)
|
134
|
+
super
|
135
|
+
|
136
|
+
generator.comments.each do |sql_gen|
|
137
|
+
if sql_gen.respond_to?(:table_name=)
|
138
|
+
sql_gen.table_name = name
|
139
|
+
end
|
140
|
+
|
141
|
+
execute(sql_gen.generate)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# An enhanced form of the standard `create_view` method, which supports
|
146
|
+
# setting a comment in the `create_view` call when the `:comment` option
|
147
|
+
# is provided.
|
148
|
+
#
|
149
|
+
# @option [String] :comment The comment to set on the newly-created view.
|
150
|
+
#
|
151
|
+
# @see [Sequel::Database#create_view](http://sequel.jeremyevans.net/rdoc/classes/Sequel/Database.html#method-i-create_view)
|
152
|
+
#
|
153
|
+
def create_view(*args)
|
154
|
+
super
|
155
|
+
|
156
|
+
if args.last.is_a?(Hash) && args.last[:comment]
|
157
|
+
comment_on(:view, args.first, args.last[:comment])
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
# Quote an object name, handling the double underscore convention
|
164
|
+
# for separating a column name from its containing object.
|
165
|
+
#
|
166
|
+
def quote_comment_identifier(id)
|
167
|
+
id = id.to_s
|
168
|
+
if id.index("__")
|
169
|
+
tbl, col = id.split("__", 2)
|
170
|
+
quote_identifier(tbl) + "." + quote_identifier(col)
|
171
|
+
else
|
172
|
+
quote_identifier(id)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Support for retrieving column comments from a PostgreSQL database
|
2
|
+
# via a dataset. For example:
|
3
|
+
#
|
4
|
+
# DB[:foo_tbl].comment_for(:some_column)
|
5
|
+
#
|
6
|
+
# Will retrieve the comment for `foo_tbl.some_column`, if such a
|
7
|
+
# column exists.
|
8
|
+
#
|
9
|
+
module Sequel::Extension::PgComment::DatasetMethods
|
10
|
+
# Retrieve the comment for the column named `col` in the "primary" table
|
11
|
+
# for this dataset.
|
12
|
+
#
|
13
|
+
# @param col [#to_s] The name of the column for which to retrieve the
|
14
|
+
# comment.
|
15
|
+
#
|
16
|
+
# @return [String, NilClass] The comment defined for the column, or
|
17
|
+
# `nil` if there is no defined comment.
|
18
|
+
#
|
19
|
+
def comment_for(col)
|
20
|
+
db.comment_for("#{first_source_table}__#{col}")
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
module Sequel::Extension::PgComment
|
2
|
+
#:nodoc:
|
3
|
+
# Generate SQL to set a comment.
|
4
|
+
#
|
5
|
+
class SqlGenerator
|
6
|
+
# The PostgreSQL object types which this class knows how to generate
|
7
|
+
# comment SQL for.
|
8
|
+
#
|
9
|
+
OBJECT_TYPES = %w{AGGREGATE
|
10
|
+
CAST
|
11
|
+
COLLATION
|
12
|
+
CONVERSION
|
13
|
+
DATABASE
|
14
|
+
DOMAIN
|
15
|
+
EXTENSION
|
16
|
+
EVENT\ TRIGGER
|
17
|
+
FOREIGN\ DATA\ WRAPPER
|
18
|
+
FOREIGN\ TABLE
|
19
|
+
FUNCTION
|
20
|
+
INDEX
|
21
|
+
LARGE\ OBJECT
|
22
|
+
MATERIALIZED\ VIEW
|
23
|
+
OPERATOR
|
24
|
+
OPERATOR\ CLASS
|
25
|
+
OPERATOR\ FAMILY
|
26
|
+
PROCEDURAL\ LANGUAGE
|
27
|
+
LANGUAGE
|
28
|
+
ROLE
|
29
|
+
SCHEMA
|
30
|
+
SEQUENCE
|
31
|
+
SERVER
|
32
|
+
TABLE
|
33
|
+
TABLESPACE
|
34
|
+
TEXT\ SEARCH\ CONFIGURATION
|
35
|
+
TEXT\ SEARCH\ DICTIONARY
|
36
|
+
TEXT\ SEARCH\ PARSER
|
37
|
+
TEXT\ SEARCH\ TEMPLATE
|
38
|
+
TYPE
|
39
|
+
VIEW
|
40
|
+
}
|
41
|
+
|
42
|
+
# Find the correct class for a given object type, and instantiate a
|
43
|
+
# new one of them.
|
44
|
+
#
|
45
|
+
# @param object_type [String, Symbol] The type of object we're going
|
46
|
+
# to comment on. Strings and symbols are both fine, and any case
|
47
|
+
# is fine, too. Any underscores get turned into spaces. Apart from
|
48
|
+
# that, it needs to be the exact name that PostgreSQL uses for the given
|
49
|
+
# type.
|
50
|
+
#
|
51
|
+
# @param object_name [String, Symbol] The name of the database object to
|
52
|
+
# set the comment on. A string is considered "already quoted", and hence
|
53
|
+
# is not escaped any further. A symbol is run through the usual Sequel
|
54
|
+
# identifier escaping code before being unleashed on the world.
|
55
|
+
#
|
56
|
+
# @param comment [String] The comment to set.
|
57
|
+
#
|
58
|
+
# @return [SqlGenerator] Some sort of `SqlGenerator` object, or a subclass.
|
59
|
+
#
|
60
|
+
# @raise [ArgumentError] if you passed in an `object_type` that we don't
|
61
|
+
# know about.
|
62
|
+
#
|
63
|
+
def self.create(object_type, object_name, comment)
|
64
|
+
generators.each do |gclass|
|
65
|
+
if gclass.handles?(object_type)
|
66
|
+
return gclass.new(object_type, object_name, comment)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
raise ArgumentError,
|
71
|
+
"Unrecognised object type #{object_type.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return whether or not this class supports the specified object type.
|
75
|
+
#
|
76
|
+
# @param object_type [String, Symbol] @see {.create}
|
77
|
+
#
|
78
|
+
# @return [TrueClass, FalseClass] whether or not this class can handle
|
79
|
+
# the object type you passed.
|
80
|
+
#
|
81
|
+
def self.handles?(object_type)
|
82
|
+
self.const_get(:OBJECT_TYPES).include?(object_type.to_s.upcase.gsub('_', ' '))
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
# Return all known `SqlGenerator` classes.
|
88
|
+
#
|
89
|
+
def self.generators
|
90
|
+
@generators ||= ObjectSpace.each_object(Class).select do |klass|
|
91
|
+
klass.ancestors.include?(self)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# We just need this so we can quote things.
|
96
|
+
def self.mock_db
|
97
|
+
@mock_db ||= Sequel.connect("mock://postgres")
|
98
|
+
end
|
99
|
+
|
100
|
+
public
|
101
|
+
|
102
|
+
# The canonicalised string (that is, all-uppercase, with words
|
103
|
+
# separated by spaces) for the object type of this SQL generator.
|
104
|
+
#
|
105
|
+
attr_reader :object_type
|
106
|
+
|
107
|
+
# The raw (might-be-a-symbol, might-be-a-string) object name that
|
108
|
+
# was passed to us originally.
|
109
|
+
#
|
110
|
+
attr_reader :object_name
|
111
|
+
|
112
|
+
# The comment.
|
113
|
+
attr_reader :comment
|
114
|
+
|
115
|
+
# Spawn a new SqlGenerator.
|
116
|
+
#
|
117
|
+
# @see {.create}
|
118
|
+
#
|
119
|
+
def initialize(object_type, object_name, comment)
|
120
|
+
@object_type = object_type.to_s.upcase.gsub('_', ' ')
|
121
|
+
@object_name = object_name
|
122
|
+
@comment = comment
|
123
|
+
end
|
124
|
+
|
125
|
+
# SQL to set a comment on the object of our affection.
|
126
|
+
#
|
127
|
+
# @return [String] The SQL needed to set the comment.
|
128
|
+
#
|
129
|
+
def generate
|
130
|
+
quoted_object_name = case object_name
|
131
|
+
when Symbol
|
132
|
+
literal object_name
|
133
|
+
else
|
134
|
+
object_name
|
135
|
+
end
|
136
|
+
|
137
|
+
"COMMENT ON #{object_type} #{quoted_object_name} IS #{literal comment.to_s}"
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Quote the provided database object (a symbol) or string value
|
143
|
+
# (a string).
|
144
|
+
#
|
145
|
+
def literal(s)
|
146
|
+
self.class.mock_db.literal(s)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
#:nodoc:
|
151
|
+
# A specialised generator for object types that live "inside" a
|
152
|
+
# table. Specifically, those types are columns, constraints,
|
153
|
+
# rules, and triggers.
|
154
|
+
#
|
155
|
+
# They get their own subclass because these object types can be
|
156
|
+
# manipulated inside a `create_table` or `alter_table` block, and at the
|
157
|
+
# time the block is evaluated, the code doesn't know the name of the
|
158
|
+
# table in which they are contained. So, we just stuff what we *do* know
|
159
|
+
# into these generators, and then when all's done, we can go to each of
|
160
|
+
# these generators, say "this is your table name", and then ask for the
|
161
|
+
# generated SQL.
|
162
|
+
#
|
163
|
+
class TableObjectSqlGenerator < SqlGenerator
|
164
|
+
# The few object types that this class handles.
|
165
|
+
OBJECT_TYPES = %w{COLUMN CONSTRAINT RULE TRIGGER}
|
166
|
+
|
167
|
+
# The name of the object which contains the object which is the direct
|
168
|
+
# target of this SQL generator. Basically, it's the table name.
|
169
|
+
attr_accessor :table_name
|
170
|
+
|
171
|
+
# Overridden constructor to deal with the double-underscore-separated
|
172
|
+
# names that we all know and love.
|
173
|
+
#
|
174
|
+
# @see {SqlGenerator#initialize}
|
175
|
+
#
|
176
|
+
def initialize(object_type, object_name, comment)
|
177
|
+
super
|
178
|
+
|
179
|
+
if object_name.is_a?(Symbol) and object_name.to_s.index("__")
|
180
|
+
@table_name, @object_name = object_name.to_s.split("__", 2).map(&:to_sym)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Generate special SQL.
|
185
|
+
#
|
186
|
+
# @see {SqlGenerator#generate}
|
187
|
+
#
|
188
|
+
def generate
|
189
|
+
if table_name.nil?
|
190
|
+
raise ArgumentError,
|
191
|
+
"Cannot generate SQL for #{object_type} #{object_name} " +
|
192
|
+
"without a table_name"
|
193
|
+
end
|
194
|
+
|
195
|
+
qualified_object_name = case object_type
|
196
|
+
when "COLUMN"
|
197
|
+
"#{maybe_escape table_name}.#{maybe_escape object_name}"
|
198
|
+
when "CONSTRAINT", "RULE", "TRIGGER"
|
199
|
+
"#{maybe_escape object_name} ON #{maybe_escape table_name}"
|
200
|
+
end
|
201
|
+
|
202
|
+
"COMMENT ON #{object_type} #{qualified_object_name} IS #{literal comment}"
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
# Handle with the vagaries of having both strings and symbols as
|
208
|
+
# possible names -- we escape symbols, but leave strings to their own
|
209
|
+
# devices.
|
210
|
+
#
|
211
|
+
def maybe_escape(s)
|
212
|
+
Symbol === s ? literal(s) : s
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
#:nodoc:
|
217
|
+
# This is an annoying corner-case generator -- it doesn't handle any
|
218
|
+
# types by default, but it will handle any *other* type where the name of
|
219
|
+
# a table needs to be prefixed by a name. The only known use case for
|
220
|
+
# this at present is "implicit" (that is, automatically generated by the
|
221
|
+
# database) constraints and indexes that get prefixed by the table name,
|
222
|
+
# and which are generated at a time when the calling code doesn't know the
|
223
|
+
# name of the table that it is generating SQL for.
|
224
|
+
#
|
225
|
+
class PrefixSqlGenerator < SqlGenerator
|
226
|
+
# This class doesn't handle any object types directly, and must be
|
227
|
+
# instantiated directly when needed
|
228
|
+
OBJECT_TYPES = %w{}
|
229
|
+
|
230
|
+
# The name of the table which should be prefixed to the object name
|
231
|
+
# that was specified when this instance was created.
|
232
|
+
#
|
233
|
+
attr_accessor :table_name
|
234
|
+
|
235
|
+
# Generate super-dooper special SQL.
|
236
|
+
#
|
237
|
+
# @see {SqlGenerator#generate}
|
238
|
+
#
|
239
|
+
def generate
|
240
|
+
if table_name.nil?
|
241
|
+
raise ArgumentError,
|
242
|
+
"Cannot generate SQL for #{object_type} #{object_name} " +
|
243
|
+
"without a table_name"
|
244
|
+
end
|
245
|
+
|
246
|
+
prefixed_object_name = "#{table_name}#{object_name}"
|
247
|
+
|
248
|
+
if Symbol === table_name || Symbol === object_name
|
249
|
+
prefixed_object_name = prefixed_object_name.to_sym
|
250
|
+
end
|
251
|
+
|
252
|
+
g = SqlGenerator.create(object_type, prefixed_object_name, comment)
|
253
|
+
g.table_name = table_name if g.respond_to? :table_name
|
254
|
+
g.generate
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|