sequel-pg-comment 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sequel/extensions/pg_comment.rb +49 -9
- data/lib/sequel/extensions/pg_comment/database_methods.rb +132 -79
- data/lib/sequel/extensions/pg_comment/dataset_methods.rb +16 -1
- data/spec/alter_table_comment_spec.rb +1 -9
- data/spec/comment_for_spec.rb +10 -9
- data/spec/comment_on_spec.rb +22 -4
- data/spec/create_table_comment_spec.rb +3 -22
- data/spec/dataset_comment_spec.rb +20 -0
- data/spec/normalise_comment_spec.rb +2 -2
- metadata +3 -6
- data/lib/sequel/extensions/pg_comment/alter_table_generator_methods.rb +0 -126
- data/lib/sequel/extensions/pg_comment/create_table_generator_methods.rb +0 -159
- data/lib/sequel/extensions/pg_comment/sql_generator.rb +0 -257
- data/spec/sql_generator_spec.rb +0 -96
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3110fff14db57745785e517abfc96b0c67c8f806
|
4
|
+
data.tar.gz: 48ebb162456d9d455557dd019b6e706766bd9024
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d96a82d756893a528156fed4eeb202fc73d1b60bd7e4ad9c3f6863769427c2763216370d0093e25486191cbbd82b20cda1994363f8bcf30d5603b71b7b8b5614
|
7
|
+
data.tar.gz: 4a0d8e466cce89d873d9ea1061561ebbbc527813d35c52171925cdc76eb3215641f42fbd320787a4b76cf48fbc66a9c138bd9f16b2af8a95c76017206a2fbabe
|
@@ -1,9 +1,52 @@
|
|
1
|
-
module Sequel::
|
1
|
+
module Sequel::Postgres; end #:nodoc:
|
2
2
|
|
3
3
|
# PostgreSQL-specific extension to set and retrieve comments on
|
4
4
|
# all database objects.
|
5
|
-
#
|
6
|
-
module Sequel::
|
5
|
+
#
|
6
|
+
module Sequel::Postgres::Comment
|
7
|
+
# The types of PostgreSQL object that are commented on in their own
|
8
|
+
# right.
|
9
|
+
STANDALONE_TYPES = [
|
10
|
+
:aggregate,
|
11
|
+
:cast,
|
12
|
+
:collation,
|
13
|
+
:conversion,
|
14
|
+
:database,
|
15
|
+
:domain,
|
16
|
+
:extension,
|
17
|
+
:event_trigger,
|
18
|
+
:foreign_data_wrapper,
|
19
|
+
:foreign_table,
|
20
|
+
:function,
|
21
|
+
:index,
|
22
|
+
:large_object,
|
23
|
+
:materialized_view,
|
24
|
+
:operator,
|
25
|
+
:operator_class,
|
26
|
+
:operator_family,
|
27
|
+
:language,
|
28
|
+
:procedural_language,
|
29
|
+
:role,
|
30
|
+
:schema,
|
31
|
+
:sequence,
|
32
|
+
:server,
|
33
|
+
:table,
|
34
|
+
:tablespace,
|
35
|
+
:text_search_configuration,
|
36
|
+
:text_search_dictionary,
|
37
|
+
:text_search_parser,
|
38
|
+
:text_search_template,
|
39
|
+
:type,
|
40
|
+
:view
|
41
|
+
]
|
42
|
+
|
43
|
+
CONTAINED_TYPES = [
|
44
|
+
:column,
|
45
|
+
:constraint,
|
46
|
+
:rule,
|
47
|
+
:trigger
|
48
|
+
]
|
49
|
+
|
7
50
|
# Strip indenting whitespace from a comment string.
|
8
51
|
#
|
9
52
|
# Two rules are applied by this method:
|
@@ -49,7 +92,7 @@ module Sequel::Extension::PgComment
|
|
49
92
|
# @param comment [String] The comment to mangle for whitespace.
|
50
93
|
#
|
51
94
|
# @return [String] The normalised comment, with whitespace removed.
|
52
|
-
#
|
95
|
+
#
|
53
96
|
def self.normalise_comment(comment)
|
54
97
|
comment.tap do |s|
|
55
98
|
s.gsub!(/\A\n+/, '')
|
@@ -61,13 +104,10 @@ module Sequel::Extension::PgComment
|
|
61
104
|
end
|
62
105
|
end
|
63
106
|
|
64
|
-
require_relative 'pg_comment/sql_generator'
|
65
107
|
require_relative 'pg_comment/database_methods'
|
66
108
|
require_relative 'pg_comment/dataset_methods'
|
67
|
-
require_relative 'pg_comment/create_table_generator_methods'
|
68
|
-
require_relative 'pg_comment/alter_table_generator_methods'
|
69
109
|
|
70
110
|
Sequel::Database.register_extension(:pg_comment) do |db|
|
71
|
-
db.extend Sequel::
|
72
|
-
db.extend_datasets Sequel::
|
111
|
+
db.extend Sequel::Postgres::Comment::DatabaseMethods
|
112
|
+
db.extend_datasets Sequel::Postgres::Comment::DatasetMethods
|
73
113
|
end
|
@@ -1,42 +1,51 @@
|
|
1
1
|
# Support for setting and retrieving comments on all object types
|
2
2
|
# in a PostgreSQL database.
|
3
3
|
#
|
4
|
-
module Sequel::
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# that
|
11
|
-
# the
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
4
|
+
module Sequel::Postgres::Comment::DatabaseMethods
|
5
|
+
# @param type [Symbol] The object type you're looking to set the comment
|
6
|
+
# on. This is just the PostgreSQL type name, lowercased, with spaces
|
7
|
+
# replaced with underscores.
|
8
|
+
#
|
9
|
+
# @param id [Symbol, String, Array<Symbol, String>] The identifier of the
|
10
|
+
# object that you wish to comment on. For most types of object, this
|
11
|
+
# should be the literal name of the object. However, for objects which
|
12
|
+
# are "contained" in another object (columns in tables/views, and
|
13
|
+
# constraints, triggers, and rules in tables) you must pass the
|
14
|
+
# identifier as a two-element array, where the first element is the
|
15
|
+
# container table or view, and the second element is the contained
|
16
|
+
# object (column, constraint, rule, or trigger).
|
17
|
+
#
|
18
|
+
# In any event, when a `Symbol` is encountered, it is quoted for
|
19
|
+
# safety, and split into a schema and object name pair, if appropriate.
|
20
|
+
# If a `String` is passed, then **no escaping or quoting is done**.
|
21
|
+
# While this is a slight risk, it is necessary to allow you to
|
22
|
+
# reference complex object names which can't reasonably be described
|
23
|
+
# otherwise (`FUNCTION`, I'm looking at you).
|
18
24
|
#
|
19
25
|
# @param comment [String] The comment you wish to set for the database
|
20
26
|
# object.
|
21
27
|
#
|
22
|
-
# @
|
28
|
+
# @raise [Sequel::Error] if the specified `type` isn't recognised.
|
29
|
+
#
|
30
|
+
# @see {Sequel::Postgres::Comment.normalise_comment} for details on
|
23
31
|
# how the comment string is interpreted.
|
24
32
|
#
|
25
33
|
def comment_on(type, id, comment)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
34
|
+
if Sequel::Postgres::Comment::STANDALONE_TYPES.include?(type)
|
35
|
+
comment_on_standalone_type(type, id, comment)
|
36
|
+
elsif Sequel::Postgres::Comment::CONTAINED_TYPES.include?(type)
|
37
|
+
comment_on_contained_type(type, id, comment)
|
38
|
+
else
|
39
|
+
raise Sequel::Error,
|
40
|
+
"Unknown object type: #{type.inspect}"
|
31
41
|
end
|
32
|
-
|
33
|
-
execute(gen.generate)
|
34
42
|
end
|
35
43
|
|
44
|
+
|
36
45
|
# Retrieve the comment for a database object.
|
37
46
|
#
|
38
47
|
# @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.
|
48
|
+
# most objects, this should be the literal name of the object.
|
40
49
|
# However, for columns on tables and views, the name of the table/view
|
41
50
|
# should be a separated from the name of the column by a double
|
42
51
|
# underscore (ie `__`).
|
@@ -50,14 +59,19 @@ module Sequel::Extension::PgComment::DatabaseMethods
|
|
50
59
|
if object.index("__")
|
51
60
|
tbl, col = object.split("__", 2)
|
52
61
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
)
|
62
|
+
(select(Sequel.function(:col_description, :c__oid, :a__attnum).as(:comment)).
|
63
|
+
from(Sequel.as(:pg_class, :c)).
|
64
|
+
join(Sequel.as(:pg_attribute, :a), :c__oid => :a__attrelid).
|
65
|
+
where(:c__relname => tbl).
|
66
|
+
and(:a__attname => col).first || {})[:comment]
|
59
67
|
else
|
60
|
-
|
68
|
+
(select(
|
69
|
+
Sequel.function(
|
70
|
+
:obj_description,
|
71
|
+
Sequel.cast(object, :regclass),
|
72
|
+
"pg_class"
|
73
|
+
).as(:comment)
|
74
|
+
).first || {})[:comment]
|
61
75
|
end
|
62
76
|
end
|
63
77
|
|
@@ -78,51 +92,47 @@ module Sequel::Extension::PgComment::DatabaseMethods
|
|
78
92
|
end
|
79
93
|
|
80
94
|
#:nodoc:
|
81
|
-
# Enhanced
|
82
|
-
#
|
95
|
+
# Enhanced to support creating comments on columns, after the table
|
96
|
+
# itself (and hence all its columns) have been created.
|
83
97
|
#
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
def create_table_from_generator(name, generator, options)
|
99
|
+
generator.columns.each do |col|
|
100
|
+
if col[:comment]
|
101
|
+
comment_on(:column, [name, col[:name]], col[:comment])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
generator.constraints.each do |c|
|
106
|
+
case c[:type]
|
107
|
+
when :primary_key
|
108
|
+
c_name = c[:name] || "#{name}_pkey".to_sym
|
109
|
+
comment_on(:index, c_name, c[:comment])
|
110
|
+
when :foreign_key, :check
|
111
|
+
if c[:type] == :check && c[:name].nil?
|
112
|
+
raise Sequel::Error,
|
113
|
+
"Setting comments on unnamed check constraints is not supported"
|
114
|
+
end
|
115
|
+
c_name = c[:name] || "#{name}_#{c[:columns].first}_fkey".to_sym
|
116
|
+
comment_on(:constraint, [name, c_name], c[:comment])
|
117
|
+
when :unique
|
118
|
+
c_name = c[:name] || ([name] + c[:columns] + ["key"]).join("_").to_sym
|
119
|
+
comment_on(:index, c_name, c[:comment])
|
120
|
+
end
|
89
121
|
end
|
90
122
|
end
|
91
123
|
|
92
124
|
#:nodoc:
|
93
|
-
# Enhanced
|
94
|
-
#
|
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?
|
125
|
+
# Enhanced to support creating comments on indexes, after the indexes
|
126
|
+
# themselves have been created.
|
104
127
|
#
|
105
128
|
def create_table_indexes_from_generator(name, generator, options)
|
106
129
|
super
|
107
130
|
|
108
|
-
generator.
|
109
|
-
if
|
110
|
-
|
131
|
+
generator.indexes.each do |idx|
|
132
|
+
if idx[:comment]
|
133
|
+
i_name = idx[:name] || ([name] + idx[:columns] + ["index"]).join("_").to_sym
|
134
|
+
comment_on(:index, i_name, idx[:comment])
|
111
135
|
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
136
|
end
|
127
137
|
end
|
128
138
|
|
@@ -133,12 +143,30 @@ module Sequel::Extension::PgComment::DatabaseMethods
|
|
133
143
|
def apply_alter_table_generator(name, generator)
|
134
144
|
super
|
135
145
|
|
136
|
-
|
137
|
-
|
138
|
-
|
146
|
+
schema, table = schema_and_table(name)
|
147
|
+
fqtable = [schema, table].compact.map { |e| literal e.to_sym }.join('.')
|
148
|
+
|
149
|
+
generator.operations.each do |op|
|
150
|
+
if op[:comment]
|
151
|
+
case op[:op]
|
152
|
+
when :add_column
|
153
|
+
comment_on(:column, [name, op[:name]], op[:comment])
|
154
|
+
when :add_constraint
|
155
|
+
case op[:type]
|
156
|
+
when :primary_key
|
157
|
+
comment_on(:index, "#{name}_pkey".to_sym, op[:comment])
|
158
|
+
when :foreign_key, :check
|
159
|
+
c_name = op[:name] || "#{name}_#{op[:columns].first}_fkey".to_sym
|
160
|
+
comment_on(:constraint, [name, c_name], op[:comment])
|
161
|
+
when :unique
|
162
|
+
c_name = op[:name] || ([name] + op[:columns] + ["key"]).join("_").to_sym
|
163
|
+
comment_on(:index, c_name, op[:comment])
|
164
|
+
end
|
165
|
+
when :add_index
|
166
|
+
c_name = op[:name] || ([name] + op[:columns] + ["index"]).join("_").to_sym
|
167
|
+
comment_on(:index, c_name, op[:comment])
|
168
|
+
end
|
139
169
|
end
|
140
|
-
|
141
|
-
execute(sql_gen.generate)
|
142
170
|
end
|
143
171
|
end
|
144
172
|
|
@@ -160,16 +188,41 @@ module Sequel::Extension::PgComment::DatabaseMethods
|
|
160
188
|
|
161
189
|
private
|
162
190
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
191
|
+
def comment_on_standalone_type(type, id, comment)
|
192
|
+
run "COMMENT ON #{type.to_s.gsub("_", " ").upcase} #{quoted_schema_and_table id} IS #{literal comment}"
|
193
|
+
end
|
194
|
+
|
195
|
+
def comment_on_contained_type(type, id, comment)
|
196
|
+
unless id.is_a?(Array) and id.length == 2
|
197
|
+
raise Sequel::Error,
|
198
|
+
"Invalid ID for #{type.inspect}: must be a two-element array"
|
199
|
+
end
|
200
|
+
|
201
|
+
fqtable = quoted_schema_and_table(id[0])
|
202
|
+
fqname = if id[1].is_a?(Symbol)
|
203
|
+
quote_identifier id[1]
|
204
|
+
elsif id[1].is_a?(String)
|
205
|
+
id[1]
|
206
|
+
else
|
207
|
+
raise Sequel::Error,
|
208
|
+
"Invalid type for object ID: must be a Symbol or String"
|
209
|
+
end
|
210
|
+
|
211
|
+
if type == :column
|
212
|
+
run "COMMENT ON COLUMN #{fqtable}.#{fqname} IS #{literal comment}"
|
213
|
+
else
|
214
|
+
run "COMMENT ON #{type.to_s.gsub("_", " ").upcase} #{fqname} ON #{fqtable} IS #{literal comment}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def quoted_schema_and_table(id)
|
219
|
+
if id.is_a?(Symbol)
|
220
|
+
literal id
|
221
|
+
elsif id.is_a?(String)
|
222
|
+
id
|
171
223
|
else
|
172
|
-
|
224
|
+
raise Sequel::Error,
|
225
|
+
"Invalid type for ID: #{id.inspect} (must by symbol or string)"
|
173
226
|
end
|
174
227
|
end
|
175
228
|
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# Will retrieve the comment for `foo_tbl.some_column`, if such a
|
7
7
|
# column exists.
|
8
8
|
#
|
9
|
-
module Sequel::
|
9
|
+
module Sequel::Postgres::Comment::DatasetMethods
|
10
10
|
# Retrieve the comment for the column named `col` in the "primary" table
|
11
11
|
# for this dataset.
|
12
12
|
#
|
@@ -19,4 +19,19 @@ module Sequel::Extension::PgComment::DatasetMethods
|
|
19
19
|
def comment_for(col)
|
20
20
|
db.comment_for("#{first_source_table}__#{col}")
|
21
21
|
end
|
22
|
+
|
23
|
+
# Retrieve the comment for the "primary" table in the dataset.
|
24
|
+
#
|
25
|
+
# @return [String, NilClass] The comment for the table, or `nil` if there
|
26
|
+
# is no comment defined.
|
27
|
+
#
|
28
|
+
# @example Simple, single-table dataset
|
29
|
+
# db[:foo].comment # => comment for table foo
|
30
|
+
#
|
31
|
+
# @example Multi-table dataset
|
32
|
+
# db[:foo].join(:bar, ...).where { id < 20 }.comment # => comment for table foo
|
33
|
+
#
|
34
|
+
def comment
|
35
|
+
db.comment_for(first_source_table)
|
36
|
+
end
|
22
37
|
end
|
@@ -38,14 +38,6 @@ describe "schema modification" do
|
|
38
38
|
to eq("COMMENT ON COLUMN \"foo\".\"bar_id\" IS 'Over there!'")
|
39
39
|
end
|
40
40
|
|
41
|
-
it "sets a column comment on add_foreign_key with custom constraint name" do
|
42
|
-
db.alter_table :foo do
|
43
|
-
add_foreign_key :bar_id, :bar, :comment => "Over there!", :name => :fkr
|
44
|
-
end
|
45
|
-
expect(db.sqls.last).
|
46
|
-
to eq("COMMENT ON COLUMN \"foo\".\"bar_id\" IS 'Over there!'")
|
47
|
-
end
|
48
|
-
|
49
41
|
it "sets a constraint comment on composite add_foreign_key" do
|
50
42
|
db.alter_table :foo do
|
51
43
|
add_foreign_key [:name, :dob], :bar, :comment => "Over there!"
|
@@ -96,7 +88,7 @@ describe "schema modification" do
|
|
96
88
|
|
97
89
|
it "sets a constraint comment" do
|
98
90
|
db.alter_table :foo do
|
99
|
-
add_constraint(:min_length, :comment => "Bigger is better!") do
|
91
|
+
add_constraint(:name => :min_length, :comment => "Bigger is better!") do
|
100
92
|
char_length(name) > 2
|
101
93
|
end
|
102
94
|
end
|
data/spec/comment_for_spec.rb
CHANGED
@@ -9,26 +9,27 @@ describe "#comment_for" do
|
|
9
9
|
it "gets a table comment" do
|
10
10
|
db.comment_for(:foo)
|
11
11
|
expect(db.sqls).
|
12
|
-
to eq([
|
12
|
+
to eq([%{SELECT obj_description(CAST('foo' AS regclass), 'pg_class') } +
|
13
|
+
%{AS \"comment\" LIMIT 1}])
|
13
14
|
end
|
14
15
|
|
15
16
|
it "gets a column comment" do
|
16
17
|
db.comment_for(:foo__column)
|
17
18
|
expect(db.sqls).
|
18
|
-
to eq([
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
to eq([%{SELECT col_description("c"."oid", "a"."attnum") AS "comment" } +
|
20
|
+
%{FROM "pg_class" AS "c" } +
|
21
|
+
%{INNER JOIN "pg_attribute" AS "a" ON ("c"."oid" = "a"."attrelid") } +
|
22
|
+
%{WHERE (("c"."relname" = 'foo') AND ("a"."attname" = 'column')) LIMIT 1}
|
22
23
|
])
|
23
24
|
end
|
24
25
|
|
25
26
|
it "gets a column comment via the dataset" do
|
26
27
|
db[:foo].comment_for(:column)
|
27
28
|
expect(db.sqls).
|
28
|
-
to eq([
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
to eq([%{SELECT col_description("c"."oid", "a"."attnum") AS "comment" } +
|
30
|
+
%{FROM "pg_class" AS "c" } +
|
31
|
+
%{INNER JOIN "pg_attribute" AS "a" ON ("c"."oid" = "a"."attrelid") } +
|
32
|
+
%{WHERE (("c"."relname" = 'foo') AND ("a"."attname" = 'column')) LIMIT 1}
|
32
33
|
])
|
33
34
|
end
|
34
35
|
end
|
data/spec/comment_on_spec.rb
CHANGED
@@ -12,8 +12,8 @@ describe "#comment_on" do
|
|
12
12
|
to eq(["COMMENT ON TABLE \"foo\" IS 'Ohai!'"])
|
13
13
|
end
|
14
14
|
|
15
|
-
it "accepts a
|
16
|
-
db.comment_on(
|
15
|
+
it "accepts a symbol as object name" do
|
16
|
+
db.comment_on(:table, :foo, "Ohai!")
|
17
17
|
expect(db.sqls).
|
18
18
|
to eq(["COMMENT ON TABLE \"foo\" IS 'Ohai!'"])
|
19
19
|
end
|
@@ -39,7 +39,19 @@ describe "#comment_on" do
|
|
39
39
|
it "explodes if an invalid object type is given" do
|
40
40
|
expect do
|
41
41
|
db.comment_on(:foobooblee, :foo, "O'hai!")
|
42
|
-
end.to raise_error(
|
42
|
+
end.to raise_error(Sequel::Error)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "explodes if an invalid ID type is given for a table" do
|
46
|
+
expect do
|
47
|
+
db.comment_on(:table, [:foo, :bar], "Ohai!")
|
48
|
+
end.to raise_error(Sequel::Error)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "explodes if an invalid ID type is given for a column" do
|
52
|
+
expect do
|
53
|
+
db.comment_on(:column, :foo, "Ohai!")
|
54
|
+
end.to raise_error(Sequel::Error)
|
43
55
|
end
|
44
56
|
|
45
57
|
it "quotes the object name" do
|
@@ -49,8 +61,14 @@ describe "#comment_on" do
|
|
49
61
|
end
|
50
62
|
|
51
63
|
it "sets a column comment correctly" do
|
52
|
-
db.comment_on(:column, :
|
64
|
+
db.comment_on(:column, [:foo, :bar_id], "Ohai, column!")
|
53
65
|
expect(db.sqls).
|
54
66
|
to eq(["COMMENT ON COLUMN \"foo\".\"bar_id\" IS 'Ohai, column!'"])
|
55
67
|
end
|
68
|
+
|
69
|
+
it "sets a trigger comment correctly" do
|
70
|
+
db.comment_on(:trigger, [:foo, :bar_id], "Whoa there, trigger!")
|
71
|
+
expect(db.sqls).
|
72
|
+
to eq(["COMMENT ON TRIGGER \"bar_id\" ON \"foo\" IS 'Whoa there, trigger!'"])
|
73
|
+
end
|
56
74
|
end
|
@@ -43,17 +43,6 @@ describe "schema creation" do
|
|
43
43
|
to eq("COMMENT ON COLUMN \"foo\".\"id\" IS 'I am unique'")
|
44
44
|
end
|
45
45
|
|
46
|
-
it "sets a primary key comment on a custom constraint name" do
|
47
|
-
db.create_table :foo do
|
48
|
-
primary_key :id,
|
49
|
-
:comment => "I am unique",
|
50
|
-
:name => :custom_pk
|
51
|
-
end
|
52
|
-
|
53
|
-
expect(db.sqls.last).
|
54
|
-
to eq("COMMENT ON COLUMN \"foo\".\"id\" IS 'I am unique'")
|
55
|
-
end
|
56
|
-
|
57
46
|
it "sets a composite primary key comment" do
|
58
47
|
db.create_table :foo do
|
59
48
|
primary_key [:bar, :baz],
|
@@ -90,7 +79,7 @@ describe "schema creation" do
|
|
90
79
|
end
|
91
80
|
|
92
81
|
expect(db.sqls.last).
|
93
|
-
to eq("COMMENT ON CONSTRAINT \"
|
82
|
+
to eq("COMMENT ON CONSTRAINT \"foo_bar_name_fkey\" ON \"foo\" IS 'Over there!'")
|
94
83
|
end
|
95
84
|
|
96
85
|
it "sets a composite foreign_key comment with custom name" do
|
@@ -168,26 +157,18 @@ describe "schema creation" do
|
|
168
157
|
|
169
158
|
it "sets a constraint comment" do
|
170
159
|
db.create_table :foo do
|
171
|
-
constraint :clamp, :
|
160
|
+
constraint({ :name => :clamp, :comment => "Toight" }, :num => 1..5)
|
172
161
|
end
|
173
162
|
|
174
163
|
expect(db.sqls.last).
|
175
164
|
to eq("COMMENT ON CONSTRAINT \"clamp\" ON \"foo\" IS 'Toight'")
|
176
165
|
end
|
177
166
|
|
178
|
-
it "blows up trying to an unnamed constraint comment" do
|
179
|
-
expect do
|
180
|
-
db.create_table :foo do
|
181
|
-
constraint nil, :num => 1..5, :comment => "Kaboom"
|
182
|
-
end
|
183
|
-
end.to raise_error(/not supported/i)
|
184
|
-
end
|
185
|
-
|
186
167
|
it "blows up trying to comment on a check" do
|
187
168
|
expect do
|
188
169
|
db.create_table :foo do
|
189
170
|
check(:comment => "Kaboom") { char_length(name) > 2 }
|
190
171
|
end
|
191
|
-
end.to raise_error(/not supported/i)
|
172
|
+
end.to raise_error(Sequel::Error, /not supported/i)
|
192
173
|
end
|
193
174
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
describe "Dataset#comment" do
|
5
|
+
let(:db) { Sequel.connect("mock://postgres").extension(:pg_comment) }
|
6
|
+
|
7
|
+
it "gets the comment for the table" do
|
8
|
+
db[:foo].comment
|
9
|
+
expect(db.sqls).
|
10
|
+
to eq([%{SELECT obj_description(CAST('foo' AS regclass), 'pg_class') } +
|
11
|
+
%{AS "comment" LIMIT 1}])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "gets the comment for the first table" do
|
15
|
+
db[:foo].join(:bar, :id => :bar_id).where { foo__id < 20 }.comment
|
16
|
+
expect(db.sqls).
|
17
|
+
to eq([%{SELECT obj_description(CAST('foo' AS regclass), 'pg_class') } +
|
18
|
+
%{AS "comment" LIMIT 1}])
|
19
|
+
end
|
20
|
+
end
|
@@ -2,9 +2,9 @@ require_relative 'spec_helper'
|
|
2
2
|
require 'sequel'
|
3
3
|
require 'sequel/extensions/pg_comment'
|
4
4
|
|
5
|
-
context "Sequel::
|
5
|
+
context "Sequel::Postgres::Comment.normalise_comment" do
|
6
6
|
def nc(s)
|
7
|
-
Sequel::
|
7
|
+
Sequel::Postgres::Comment.normalise_comment(s)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "does nothing to a string with no leading whitespace" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-pg-comment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Palmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git-version-bump
|
@@ -199,11 +199,8 @@ files:
|
|
199
199
|
- README.md
|
200
200
|
- Rakefile
|
201
201
|
- lib/sequel/extensions/pg_comment.rb
|
202
|
-
- lib/sequel/extensions/pg_comment/alter_table_generator_methods.rb
|
203
|
-
- lib/sequel/extensions/pg_comment/create_table_generator_methods.rb
|
204
202
|
- lib/sequel/extensions/pg_comment/database_methods.rb
|
205
203
|
- lib/sequel/extensions/pg_comment/dataset_methods.rb
|
206
|
-
- lib/sequel/extensions/pg_comment/sql_generator.rb
|
207
204
|
- sequel-pg-comment.gemspec
|
208
205
|
- spec/alter_table_comment_spec.rb
|
209
206
|
- spec/comment_for_spec.rb
|
@@ -211,10 +208,10 @@ files:
|
|
211
208
|
- spec/create_join_table_comment_spec.rb
|
212
209
|
- spec/create_table_comment_spec.rb
|
213
210
|
- spec/create_view_comment_spec.rb
|
211
|
+
- spec/dataset_comment_spec.rb
|
214
212
|
- spec/extension_spec.rb
|
215
213
|
- spec/normalise_comment_spec.rb
|
216
214
|
- spec/spec_helper.rb
|
217
|
-
- spec/sql_generator_spec.rb
|
218
215
|
homepage: http://theshed.hezmatt.org/sequel-pg-comment
|
219
216
|
licenses: []
|
220
217
|
metadata: {}
|
@@ -1,126 +0,0 @@
|
|
1
|
-
#:nodoc:
|
2
|
-
# Enhancements to the standard schema modification methods in a
|
3
|
-
# block-form `alter_table` method, to support setting comments via the
|
4
|
-
# `:comment` option.
|
5
|
-
#
|
6
|
-
# Note that not every schema modification method is enhanced in this module;
|
7
|
-
# some modifications are implemneted in terms of more fundamental methods,
|
8
|
-
# and so do not require their own method here. For example, `add_foreign_key`
|
9
|
-
# with a single column is handled by `add_column`, and so doesn't require its
|
10
|
-
# own implementation. Rest assured that all schema modification methods
|
11
|
-
# *should* accept a `:comment` option, and set a comment in the database. If
|
12
|
-
# you find one that doesn't, please file a bug.
|
13
|
-
#
|
14
|
-
module Sequel::Extension::PgComment::AlterTableGeneratorMethods
|
15
|
-
attr_reader :comments
|
16
|
-
|
17
|
-
include Sequel::Extension::PgComment
|
18
|
-
|
19
|
-
# Enhanced version of the `add_column` schema modification method,
|
20
|
-
# which supports setting a comment on the column.
|
21
|
-
#
|
22
|
-
# @option [String] :comment The comment to set on the column
|
23
|
-
# that is being added.
|
24
|
-
#
|
25
|
-
def add_column(*args)
|
26
|
-
super
|
27
|
-
|
28
|
-
if args.last.is_a?(Hash) && args.last[:comment]
|
29
|
-
comments << SqlGenerator.create(:column, args.first, args.last[:comment])
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Enhanced version of the `add_composite_primary_key` schema modification
|
34
|
-
# method, which supports setting a comment on the index.
|
35
|
-
#
|
36
|
-
# @option [String] :comment The comment to set on the index that is being
|
37
|
-
# added.
|
38
|
-
#
|
39
|
-
def add_composite_primary_key(columns, opts)
|
40
|
-
super
|
41
|
-
|
42
|
-
if opts[:comment]
|
43
|
-
comments << PrefixSqlGenerator.new(:index, :_pkey, opts[:comment])
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Enhanced version of the `add_composite_foreign_key` schema modification
|
48
|
-
# method, which supports setting a comment on the constraint.
|
49
|
-
#
|
50
|
-
# @option [String] :comment The comment to set on the constraint that is being
|
51
|
-
# added.
|
52
|
-
#
|
53
|
-
def add_composite_foreign_key(columns, table, opts)
|
54
|
-
super
|
55
|
-
|
56
|
-
if opts[:comment]
|
57
|
-
comments << if opts[:name]
|
58
|
-
SqlGenerator.create(:constraint, opts[:name], opts[:comment])
|
59
|
-
else
|
60
|
-
PrefixSqlGenerator.new(
|
61
|
-
:constraint,
|
62
|
-
"_#{columns.first}_fkey".to_sym,
|
63
|
-
opts[:comment]
|
64
|
-
)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Enhanced version of the `add_index` schema modification method, which
|
70
|
-
# supports setting a comment on the index.
|
71
|
-
#
|
72
|
-
# @option [String] :comment The comment to set on the index that is being
|
73
|
-
# added.
|
74
|
-
#
|
75
|
-
def add_index(columns, opts = OPTS)
|
76
|
-
if opts[:comment]
|
77
|
-
comments << if opts[:name]
|
78
|
-
SqlGenerator.create(:index, opts[:name], opts[:comment])
|
79
|
-
else
|
80
|
-
PrefixSqlGenerator.new(
|
81
|
-
:index,
|
82
|
-
"_#{[columns].flatten.map(&:to_s).join("_")}_index".to_sym,
|
83
|
-
opts[:comment]
|
84
|
-
)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Enhanced version of the `add_constraint` schema modification method,
|
90
|
-
# which supports setting a comment on the constraint.
|
91
|
-
#
|
92
|
-
# @option [String] :comment The comment to set on the constraint that is
|
93
|
-
# being added.
|
94
|
-
#
|
95
|
-
def add_constraint(name, *args, &block)
|
96
|
-
super
|
97
|
-
|
98
|
-
opts = args.last.is_a?(Hash) ? args.last : {}
|
99
|
-
|
100
|
-
if opts[:comment]
|
101
|
-
comments << SqlGenerator.create(:constraint, name, opts[:comment])
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Enhanced version of the `add_unique_constraint` schema modification
|
106
|
-
# method, which supports setting a comment on the index.
|
107
|
-
#
|
108
|
-
# @option [String] :comment The comment to set on the index that is being
|
109
|
-
# added.
|
110
|
-
#
|
111
|
-
def add_unique_constraint(columns, opts = OPTS)
|
112
|
-
super
|
113
|
-
|
114
|
-
if opts[:comment]
|
115
|
-
comments << if opts[:name]
|
116
|
-
SqlGenerator.create(:index, opts[:name], opts[:comment])
|
117
|
-
else
|
118
|
-
PrefixSqlGenerator.new(
|
119
|
-
:index,
|
120
|
-
"_#{[columns].flatten.map(&:to_s).join("_")}_key".to_sym,
|
121
|
-
opts[:comment]
|
122
|
-
)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
@@ -1,159 +0,0 @@
|
|
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
|
@@ -1,257 +0,0 @@
|
|
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
|
data/spec/sql_generator_spec.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
require_relative 'spec_helper'
|
2
|
-
|
3
|
-
require 'sequel'
|
4
|
-
require 'sequel/extensions/pg_comment'
|
5
|
-
|
6
|
-
describe "SqlGenerator" do
|
7
|
-
SqlGenerator = Sequel::Extension::PgComment::SqlGenerator
|
8
|
-
PrefixSqlGenerator = Sequel::Extension::PgComment::PrefixSqlGenerator
|
9
|
-
|
10
|
-
context "a simple type expressed as a string" do
|
11
|
-
let(:generator) { SqlGenerator.create("TABLE", :foo, "Ohai!") }
|
12
|
-
|
13
|
-
it "generates quoted SQL" do
|
14
|
-
expect(generator.generate).
|
15
|
-
to eq("COMMENT ON TABLE \"foo\" IS 'Ohai!'")
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context "a simple type expressed as a crazy-case string" do
|
20
|
-
let(:generator) { SqlGenerator.create("TaBlE", :foo, "Ohai!") }
|
21
|
-
|
22
|
-
it "generates quoted SQL" do
|
23
|
-
expect(generator.generate).
|
24
|
-
to eq("COMMENT ON TABLE \"foo\" IS 'Ohai!'")
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context "a simple type expressed as a symbol" do
|
29
|
-
let(:generator) { SqlGenerator.create(:table, :foo, "Ohai!") }
|
30
|
-
|
31
|
-
it "generates quoted SQL" do
|
32
|
-
expect(generator.generate).
|
33
|
-
to eq("COMMENT ON TABLE \"foo\" IS 'Ohai!'")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context "a multi-word type expressed as a symbol" do
|
38
|
-
let(:generator) { SqlGenerator.create(:event_trigger, :foo, "Ohai!") }
|
39
|
-
|
40
|
-
it "generates correct SQL" do
|
41
|
-
expect(generator.generate).
|
42
|
-
to eq("COMMENT ON EVENT TRIGGER \"foo\" IS 'Ohai!'")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "with a string as the object name" do
|
47
|
-
let(:generator) { SqlGenerator.create(:table, "foo", "Ohai!") }
|
48
|
-
|
49
|
-
it "generates unquoted SQL" do
|
50
|
-
expect(generator.generate).
|
51
|
-
to eq("COMMENT ON TABLE foo IS 'Ohai!'")
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
it "escapes the comment" do
|
56
|
-
expect(SqlGenerator.create(:table, :foo, "O'hai!").generate).
|
57
|
-
to eq("COMMENT ON TABLE \"foo\" IS 'O''hai!'")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "explodes if an invalid object type is given" do
|
61
|
-
expect do
|
62
|
-
SqlGenerator.create(:foobooblee, :foo, "O'hai!")
|
63
|
-
end.to raise_error(ArgumentError, /unrecognised object type/i)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "sets a column comment correctly" do
|
67
|
-
expect(SqlGenerator.create(:column, :foo__bar_id, "Ohai, column!").generate).
|
68
|
-
to eq("COMMENT ON COLUMN \"foo\".\"bar_id\" IS 'Ohai, column!'")
|
69
|
-
end
|
70
|
-
|
71
|
-
it "sets a constraint comment correctly" do
|
72
|
-
g = SqlGenerator.create(:constraint, :foo__not_for_you, "Ohai, constraint!")
|
73
|
-
expect(g.generate).
|
74
|
-
to eq("COMMENT ON CONSTRAINT \"not_for_you\" ON \"foo\" IS 'Ohai, constraint!'")
|
75
|
-
end
|
76
|
-
|
77
|
-
it "sets a rule comment correctly" do
|
78
|
-
g = SqlGenerator.create(:rule, :foo__not_for_you, "Ohai, rule!")
|
79
|
-
expect(g.generate).
|
80
|
-
to eq("COMMENT ON RULE \"not_for_you\" ON \"foo\" IS 'Ohai, rule!'")
|
81
|
-
end
|
82
|
-
|
83
|
-
it "sets a trigger comment correctly" do
|
84
|
-
g = SqlGenerator.create(:trigger, :foo__spoing, "Ohai, trigger!")
|
85
|
-
expect(g.generate).
|
86
|
-
to eq("COMMENT ON TRIGGER \"spoing\" ON \"foo\" IS 'Ohai, trigger!'")
|
87
|
-
end
|
88
|
-
|
89
|
-
it "sets a comment on a prefixed name correctly" do
|
90
|
-
g = PrefixSqlGenerator.new(:index, :_pkey, "Ohai, pkey!")
|
91
|
-
g.table_name = :foo
|
92
|
-
|
93
|
-
expect(g.generate).
|
94
|
-
to eq("COMMENT ON INDEX \"foo_pkey\" IS 'Ohai, pkey!'")
|
95
|
-
end
|
96
|
-
end
|