schema_plus_views 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +79 -8
- data/README.md +43 -19
- data/gemfiles/Gemfile.base +1 -1
- data/gemfiles/activerecord-5.2/Gemfile.base +4 -0
- data/gemfiles/activerecord-5.2/Gemfile.mysql2 +10 -0
- data/gemfiles/activerecord-5.2/Gemfile.postgresql +10 -0
- data/gemfiles/{activerecord-4.2 → activerecord-5.2}/Gemfile.sqlite3 +3 -3
- data/lib/schema_plus/views/active_record/connection_adapters/abstract_adapter.rb +19 -5
- data/lib/schema_plus/views/active_record/connection_adapters/mysql2_adapter.rb +5 -11
- data/lib/schema_plus/views/active_record/connection_adapters/postgresql_adapter.rb +74 -17
- data/lib/schema_plus/views/active_record/connection_adapters/sqlite3_adapter.rb +5 -9
- data/lib/schema_plus/views/active_record/migration/command_recorder.rb +3 -1
- data/lib/schema_plus/views/middleware.rb +50 -32
- data/lib/schema_plus/views/version.rb +1 -1
- data/schema_dev.yml +5 -2
- data/schema_plus_views.gemspec +4 -4
- data/spec/dumper_spec.rb +58 -10
- data/spec/introspection_spec.rb +52 -6
- data/spec/middleware_spec.rb +1 -18
- data/spec/migration_spec.rb +106 -58
- data/spec/named_schemas_spec.rb +8 -42
- data/spec/spec_helper.rb +13 -1
- metadata +23 -24
- data/gemfiles/activerecord-4.2/Gemfile.base +0 -3
- data/gemfiles/activerecord-4.2/Gemfile.mysql2 +0 -10
- data/gemfiles/activerecord-4.2/Gemfile.postgresql +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4ff5c6a7f05fbebbb07b6b84ec71761cc533640f3affe94d3e3153645a5599cc
|
4
|
+
data.tar.gz: 8f79673795c32b02c6ca0ff80495e115cdd3c9caf24e73c8fcf7c4ade95b4923
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 928a52a3e5e42e5e745754a35ad03244dba6fadb7d8681dc48b20643aaaa34675ce0e9befd4d58a6a3139bdaafab132cc5815d4f7be021f78a77eddbf8486ab8
|
7
|
+
data.tar.gz: 674f0f9bcb0d7cbda52e9d32a2b21d67e3a095f9c2970a34b55db9146d8bee78379fe4e83c37f951b8aacf8c838d11159a35af12169f27bedf97b1ef6ea4adb2
|
data/.travis.yml
CHANGED
@@ -3,16 +3,87 @@
|
|
3
3
|
# Please do not edit this file; any changes will be overwritten next time
|
4
4
|
# schema_dev gets run.
|
5
5
|
---
|
6
|
-
sudo: false
|
7
6
|
rvm:
|
8
|
-
- 2.
|
7
|
+
- 2.5.9
|
8
|
+
- 2.7.3
|
9
9
|
gemfile:
|
10
|
-
- gemfiles/activerecord-
|
11
|
-
- gemfiles/activerecord-4.2/Gemfile.postgresql
|
12
|
-
- gemfiles/activerecord-4.2/Gemfile.sqlite3
|
13
|
-
env: POSTGRESQL_DB_USER=postgres MYSQL_DB_USER=travis
|
14
|
-
addons:
|
15
|
-
postgresql: '9.4'
|
10
|
+
- gemfiles/activerecord-5.2/Gemfile.sqlite3
|
16
11
|
before_script: bundle exec rake create_databases
|
17
12
|
after_script: bundle exec rake drop_databases
|
18
13
|
script: bundle exec rake travis
|
14
|
+
jobs:
|
15
|
+
include:
|
16
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.mysql2
|
17
|
+
rvm: 2.5.9
|
18
|
+
services:
|
19
|
+
- mysql
|
20
|
+
env: MYSQL_DB_USER=travis
|
21
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
22
|
+
rvm: 2.5.9
|
23
|
+
addons:
|
24
|
+
postgresql: '10'
|
25
|
+
apt:
|
26
|
+
packages:
|
27
|
+
- postgresql-10
|
28
|
+
- postgresql-client-10
|
29
|
+
env: POSTGRESQL_DB_USER=postgres
|
30
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
31
|
+
rvm: 2.5.9
|
32
|
+
addons:
|
33
|
+
postgresql: '11'
|
34
|
+
apt:
|
35
|
+
packages:
|
36
|
+
- postgresql-11
|
37
|
+
- postgresql-client-11
|
38
|
+
env: POSTGRESQL_DB_USER=travis PGPORT=5433
|
39
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
40
|
+
rvm: 2.5.9
|
41
|
+
addons:
|
42
|
+
postgresql: '12'
|
43
|
+
apt:
|
44
|
+
packages:
|
45
|
+
- postgresql-12
|
46
|
+
- postgresql-client-12
|
47
|
+
env: POSTGRESQL_DB_USER=travis PGPORT=5433
|
48
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
49
|
+
rvm: 2.5.9
|
50
|
+
addons:
|
51
|
+
postgresql: '9.6'
|
52
|
+
env: POSTGRESQL_DB_USER=postgres
|
53
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.mysql2
|
54
|
+
rvm: 2.7.3
|
55
|
+
services:
|
56
|
+
- mysql
|
57
|
+
env: MYSQL_DB_USER=travis
|
58
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
59
|
+
rvm: 2.7.3
|
60
|
+
addons:
|
61
|
+
postgresql: '11'
|
62
|
+
apt:
|
63
|
+
packages:
|
64
|
+
- postgresql-11
|
65
|
+
- postgresql-client-11
|
66
|
+
env: POSTGRESQL_DB_USER=travis PGPORT=5433
|
67
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
68
|
+
rvm: 2.7.3
|
69
|
+
addons:
|
70
|
+
postgresql: '12'
|
71
|
+
apt:
|
72
|
+
packages:
|
73
|
+
- postgresql-12
|
74
|
+
- postgresql-client-12
|
75
|
+
env: POSTGRESQL_DB_USER=travis PGPORT=5433
|
76
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
77
|
+
rvm: 2.7.3
|
78
|
+
addons:
|
79
|
+
postgresql: '10'
|
80
|
+
apt:
|
81
|
+
packages:
|
82
|
+
- postgresql-10
|
83
|
+
- postgresql-client-10
|
84
|
+
env: POSTGRESQL_DB_USER=postgres
|
85
|
+
- gemfile: gemfiles/activerecord-5.2/Gemfile.postgresql
|
86
|
+
rvm: 2.7.3
|
87
|
+
addons:
|
88
|
+
postgresql: '9.6'
|
89
|
+
env: POSTGRESQL_DB_USER=postgres
|
data/README.md
CHANGED
@@ -28,7 +28,8 @@ SchemaPlus::Views is tested on:
|
|
28
28
|
|
29
29
|
<!-- SCHEMA_DEV: MATRIX - begin -->
|
30
30
|
<!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
|
31
|
-
* ruby **2.
|
31
|
+
* ruby **2.5.9** with activerecord **5.2**, using **mysql2**, **sqlite3** or **postgresql**
|
32
|
+
* ruby **2.7.3** with activerecord **5.2**, using **mysql2**, **sqlite3** or **postgresql**
|
32
33
|
|
33
34
|
<!-- SCHEMA_DEV: MATRIX - end -->
|
34
35
|
|
@@ -56,8 +57,30 @@ Additional options can be provided:
|
|
56
57
|
|
57
58
|
* `:allow_replace => true` will use the command "CREATE OR REPLACE" when creating the view, for seamlessly redefining the view even if other views depend on it. It's only supported by MySQL and PostgreSQL, and each has some limitations on when a view can be replaced; see their docs for details.
|
58
59
|
|
60
|
+
* `:materialized => true` will create a materialized view instead of a standard view. This view caches its contents on disk and must be refreshed to update its contents. It is only supported on PostgreSQL. Further, allow_replace is not supported on materialized views.
|
61
|
+
|
59
62
|
SchemaPlus::Views also arranges to include the `create_view` statements (with literal SQL) in the schema dump.
|
60
63
|
|
64
|
+
#### Materialized views
|
65
|
+
|
66
|
+
Materialized views persist their data when created and must be manually refreshed to see new data.
|
67
|
+
Further materialized views can have indexes defined on them.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
create_view :posts_commented_by_staff, <<~SQL, materialized: true
|
71
|
+
SELECT * FROM posts LEFT OUTER JOIN comments ON comments.post_id = posts.id WHERE comments.id IS NULL
|
72
|
+
SQL
|
73
|
+
|
74
|
+
add_index :posts_commented_by_staff, :category
|
75
|
+
add_index :posts_commented_by_staff, :token, unique: true
|
76
|
+
```
|
77
|
+
|
78
|
+
To refresh a materialized view run the refresh_view connection command.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
ActiveRecord::Base.connection.refresh_view('posts_commented_by_staff')
|
82
|
+
```
|
83
|
+
|
61
84
|
### Dropping views
|
62
85
|
|
63
86
|
In a migration:
|
@@ -65,6 +88,10 @@ In a migration:
|
|
65
88
|
```ruby
|
66
89
|
drop_view :posts_commented_by_staff
|
67
90
|
drop_view :uncommented_posts, :if_exists => true
|
91
|
+
|
92
|
+
# materialized views
|
93
|
+
drop_view :posts_commented_by_staff, materialized: true
|
94
|
+
drop_view :uncommented_posts, :if_exists => true, materialized: true
|
68
95
|
```
|
69
96
|
|
70
97
|
### Using views
|
@@ -86,14 +113,16 @@ You can look up the defined views analogously to looking up tables:
|
|
86
113
|
|
87
114
|
```ruby
|
88
115
|
connection.tables # => array of table names [method provided by ActiveRecord]
|
89
|
-
connection.views # => array of view names [method
|
116
|
+
connection.views # => array of view names [method overridden by SchemaPlus::Views for postgres]
|
90
117
|
```
|
91
118
|
|
92
119
|
Notes:
|
93
120
|
|
94
|
-
1. For
|
95
|
-
|
96
|
-
|
121
|
+
1. For PostgreSQL, `connection.views` suppresses views prefixed with `pg_` as those are presumed to be internal. Also it suppresses the "postgis" specifically named tables
|
122
|
+
- geography_columns
|
123
|
+
- geometry_columns
|
124
|
+
- raster_columns
|
125
|
+
- raster_overviews
|
97
126
|
|
98
127
|
### Querying view definitions
|
99
128
|
|
@@ -105,26 +134,19 @@ connection.view_definition(view_name) # => returns SQL string
|
|
105
134
|
|
106
135
|
This returns just the body of the definition, i.e. the part after the `CREATE VIEW 'name' AS` command.
|
107
136
|
|
108
|
-
|
109
|
-
|
110
|
-
All the methods defined by SchemaPlus::Views provide middleware stacks, in case you need to do any custom filtering, rewriting, triggering, or whatever. For info on how to use middleware stacks, see the READMEs of [schema_monkey](https://github.com/SchemaPlus/schema_monkey) and [schema_plus_core](https://github.com/SchemaPlus/schema_plus_core).
|
111
|
-
|
137
|
+
You can also lookup the type of view (regular or materialized) using
|
112
138
|
|
113
|
-
|
114
|
-
|
115
|
-
|
139
|
+
```ruby
|
140
|
+
connection.view_type(view_name) # => returns a Symbol, either :view or :materialized
|
141
|
+
```
|
116
142
|
|
117
|
-
|
118
|
-
--- | --- | ---
|
119
|
-
`:views` | The result of the lookup | `[]`
|
120
|
-
`:connection` | The current ActiveRecord connection | *context*
|
121
|
-
`:query_name` | Optional label for ActiveRecord logging | *arg*
|
143
|
+
## Customization API: Middleware Stacks
|
122
144
|
|
123
|
-
|
145
|
+
All the methods defined by SchemaPlus::Views provide middleware stacks, in case you need to do any custom filtering, rewriting, triggering, or whatever. For info on how to use middleware stacks, see the READMEs of [schema_monkey](https://github.com/SchemaPlus/schema_monkey) and [schema_plus_core](https://github.com/SchemaPlus/schema_plus_core).
|
124
146
|
|
125
147
|
### `Schema::ViewDefinition` stack
|
126
148
|
|
127
|
-
Wraps the `connection.
|
149
|
+
Wraps the `connection.view_full_definition` method. Env contains:
|
128
150
|
|
129
151
|
Env Field | Description | Initialized
|
130
152
|
--- | --- | ---
|
@@ -132,6 +154,7 @@ Env Field | Description | Initialized
|
|
132
154
|
`:view_name` | The view to look up | *arg*
|
133
155
|
`:query_name` | Optional label for ActiveRecord logging | *arg*
|
134
156
|
`:definition` | The view definition SQL | `nil`
|
157
|
+
`:view_type` | The view type symbol. | :view`
|
135
158
|
|
136
159
|
The base implementation looks up the definition of the view named
|
137
160
|
`env.view_name` and assigns the result to `env.definition`
|
@@ -166,6 +189,7 @@ options in `env.options`
|
|
166
189
|
|
167
190
|
## History
|
168
191
|
|
192
|
+
* 0.4.0 - Added support for Rails 5.2 and materialized views in PostgreSQL
|
169
193
|
* 0.3.1 - Upgrade schema_plus_core and schema_dev dependencies
|
170
194
|
* 0.3.0
|
171
195
|
- Added middleware stacks
|
data/gemfiles/Gemfile.base
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
eval
|
1
|
+
base_gemfile = File.expand_path('../Gemfile.base', __FILE__)
|
2
|
+
eval File.read(base_gemfile), binding, base_gemfile
|
3
3
|
|
4
4
|
platform :ruby do
|
5
5
|
gem "sqlite3"
|
@@ -7,4 +7,4 @@ end
|
|
7
7
|
|
8
8
|
platform :jruby do
|
9
9
|
gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
|
10
|
-
end
|
10
|
+
end
|
@@ -6,6 +6,7 @@ module SchemaPlus::Views
|
|
6
6
|
# to first drop the view if it already exists.
|
7
7
|
def create_view(view_name, definition, options={})
|
8
8
|
SchemaMonkey::Middleware::Migration::CreateView.start(connection: self, view_name: view_name, definition: definition, options: options) do |env|
|
9
|
+
raise ArgumentError, 'Materialized views are not implemented or supported on this database' if options[:materialized]
|
9
10
|
definition = env.definition
|
10
11
|
view_name = env.view_name
|
11
12
|
options = env.options
|
@@ -28,6 +29,7 @@ module SchemaPlus::Views
|
|
28
29
|
# to fail silently if the view doesn't exist.
|
29
30
|
def drop_view(view_name, options = {})
|
30
31
|
SchemaMonkey::Middleware::Migration::DropView.start(connection: self, view_name: view_name, options: options) do |env|
|
32
|
+
raise ArgumentError, 'Materialized views are not implemented or supported on this database' if options[:materialized]
|
31
33
|
view_name = env.view_name
|
32
34
|
options = env.options
|
33
35
|
sql = "DROP VIEW"
|
@@ -37,19 +39,31 @@ module SchemaPlus::Views
|
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
42
|
+
# Returns the SQL definition of a given view. This is
|
43
|
+
# the literal SQL would come after 'CREATVE VIEW viewname AS ' in
|
44
|
+
# the SQL statement to create a view.
|
45
|
+
def view_definition(view_name, name = nil)
|
46
|
+
view_full_definition(view_name, name).first
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the view type of a given view. This is either :view or :materialized
|
50
|
+
def view_type(view_name, name = nil)
|
51
|
+
view_full_definition(view_name, name).second
|
52
|
+
end
|
53
|
+
|
40
54
|
#####################################################################
|
41
55
|
#
|
42
56
|
# The functions below here are abstract; each subclass should
|
43
57
|
# define them all. Defining them here only for reference.
|
44
58
|
#
|
45
59
|
|
46
|
-
# (abstract)
|
47
|
-
def
|
60
|
+
# (abstract) Refreshes the given materialized view.
|
61
|
+
def refresh_view(view_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; end
|
48
62
|
|
49
|
-
# (abstract) Returns the SQL definition of a given view. This is
|
63
|
+
# (abstract) Returns the SQL definition and type of a given view. This is
|
50
64
|
# the literal SQL would come after 'CREATVE VIEW viewname AS ' in
|
51
|
-
# the SQL statement to create a view.
|
52
|
-
def
|
65
|
+
# the SQL statement to create a view. The type is either :view, or :materialized
|
66
|
+
def view_full_definition(view_name, name = nil) raise "Internal Error: Connection adapter didn't override abstract function"; end
|
53
67
|
end
|
54
68
|
end
|
55
69
|
end
|
@@ -3,16 +3,8 @@ module SchemaPlus::Views
|
|
3
3
|
module ConnectionAdapters
|
4
4
|
module Mysql2Adapter
|
5
5
|
|
6
|
-
def
|
7
|
-
SchemaMonkey::Middleware::Schema::
|
8
|
-
select_all("SELECT table_name FROM information_schema.views WHERE table_schema = SCHEMA()", env.query_name).each do |row|
|
9
|
-
env.views << row["table_name"]
|
10
|
-
end
|
11
|
-
}.views
|
12
|
-
end
|
13
|
-
|
14
|
-
def view_definition(view_name, name = nil)
|
15
|
-
SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name) { |env|
|
6
|
+
def view_full_definition(view_name, name = nil)
|
7
|
+
data = SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name, view_type: :view) { |env|
|
16
8
|
results = select_all("SELECT view_definition, check_option FROM information_schema.views WHERE table_schema = SCHEMA() AND table_name = #{quote(view_name)}", name)
|
17
9
|
if results.any?
|
18
10
|
row = results.first
|
@@ -24,7 +16,9 @@ module SchemaPlus::Views
|
|
24
16
|
end
|
25
17
|
env.definition = sql
|
26
18
|
end
|
27
|
-
}
|
19
|
+
}
|
20
|
+
|
21
|
+
[data.definition, data.view_type]
|
28
22
|
end
|
29
23
|
|
30
24
|
end
|
@@ -2,31 +2,88 @@ module SchemaPlus::Views
|
|
2
2
|
module ActiveRecord
|
3
3
|
module ConnectionAdapters
|
4
4
|
module PostgresqlAdapter
|
5
|
+
POSTGIS_VIEWS = %W[
|
6
|
+
geography_columns
|
7
|
+
geometry_columns
|
8
|
+
raster_columns
|
9
|
+
raster_overviews
|
10
|
+
].freeze
|
5
11
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
# Create a view given the SQL definition. Specify :force => true
|
13
|
+
# to first drop the view if it already exists.
|
14
|
+
def create_view(view_name, definition, options={})
|
15
|
+
SchemaMonkey::Middleware::Migration::CreateView.start(connection: self, view_name: view_name, definition: definition, options: options) do |env|
|
16
|
+
definition = env.definition
|
17
|
+
view_name = env.view_name
|
18
|
+
options = env.options
|
19
|
+
definition = definition.to_sql if definition.respond_to? :to_sql
|
20
|
+
|
21
|
+
if options[:materialized] && options[:allow_replace]
|
22
|
+
raise ArgumentError, 'allow_replace is not supported for materialized views'
|
23
|
+
end
|
24
|
+
|
25
|
+
if options[:force]
|
26
|
+
drop_view(view_name, {if_exists: true}.merge(options.slice(:materialized)))
|
27
|
+
end
|
28
|
+
|
29
|
+
command = if options[:materialized]
|
30
|
+
"CREATE MATERIALIZED"
|
31
|
+
elsif options[:allow_replace]
|
32
|
+
"CREATE OR REPLACE"
|
33
|
+
else
|
34
|
+
"CREATE"
|
35
|
+
end
|
36
|
+
|
37
|
+
execute "#{command} VIEW #{quote_table_name(view_name)} AS #{definition}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Drop the named view. Specify :if_exists => true
|
42
|
+
# to fail silently if the view doesn't exist.
|
43
|
+
def drop_view(view_name, options = {})
|
44
|
+
SchemaMonkey::Middleware::Migration::DropView.start(connection: self, view_name: view_name, options: options) do |env|
|
45
|
+
view_name = env.view_name
|
46
|
+
options = env.options
|
47
|
+
materialized = options[:materialized] ? 'MATERIALIZED' : ''
|
48
|
+
sql = "DROP #{materialized} VIEW"
|
49
|
+
sql += " IF EXISTS" if options[:if_exists]
|
50
|
+
sql += " #{quote_table_name(view_name)}"
|
51
|
+
execute sql
|
52
|
+
end
|
17
53
|
end
|
18
54
|
|
19
|
-
|
20
|
-
|
55
|
+
# Refresh a materialized view.
|
56
|
+
def refresh_view(view_name, options = {})
|
57
|
+
SchemaMonkey::Middleware::Migration::RefreshView.start(connection: self, view_name: view_name, options: options) do |env|
|
58
|
+
view_name = env.view_name
|
59
|
+
sql = "REFRESH MATERIALIZED VIEW #{quote_table_name(view_name)}"
|
60
|
+
execute sql
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def views #:nodoc:
|
65
|
+
# Filter out any view that begins with "pg_"
|
66
|
+
super.reject do |c|
|
67
|
+
c.start_with?("pg_") || POSTGIS_VIEWS.include?(c)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def view_full_definition(view_name, name = nil) #:nodoc:
|
72
|
+
data = SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name, view_type: :view) { |env|
|
21
73
|
result = env.connection.query(<<-SQL, name)
|
22
|
-
SELECT pg_get_viewdef(oid)
|
74
|
+
SELECT pg_get_viewdef(oid), relkind
|
23
75
|
FROM pg_class
|
24
|
-
WHERE relkind
|
76
|
+
WHERE relkind in ('v', 'm')
|
25
77
|
AND relname = '#{env.view_name}'
|
26
78
|
SQL
|
27
79
|
row = result.first
|
28
|
-
|
29
|
-
|
80
|
+
unless row.nil?
|
81
|
+
env.definition = row.first.chomp(';').strip
|
82
|
+
env.view_type = :materialized if row.second == 'm'
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
[data.definition, data.view_type]
|
30
87
|
end
|
31
88
|
|
32
89
|
end
|
@@ -3,18 +3,14 @@ module SchemaPlus::Views
|
|
3
3
|
module ConnectionAdapters
|
4
4
|
module Sqlite3Adapter
|
5
5
|
|
6
|
-
def
|
7
|
-
SchemaMonkey::Middleware::Schema::
|
8
|
-
env.views += env.connection.execute("SELECT name FROM sqlite_master WHERE type='view'", env.query_name).collect{|row| row["name"]}
|
9
|
-
}.views
|
10
|
-
end
|
11
|
-
|
12
|
-
def view_definition(view_name, name = nil)
|
13
|
-
SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name) { |env|
|
6
|
+
def view_full_definition(view_name, name = nil)
|
7
|
+
data = SchemaMonkey::Middleware::Schema::ViewDefinition.start(connection: self, view_name: view_name, query_name: name, view_type: :view) { |env|
|
14
8
|
sql = env.connection.execute("SELECT sql FROM sqlite_master WHERE type='view' AND name=#{quote(env.view_name)}", env.query_name).collect{|row| row["sql"]}.first
|
15
9
|
sql.sub!(/^CREATE VIEW \S* AS\s+/im, '') unless sql.nil?
|
16
10
|
env.definition = sql
|
17
|
-
}
|
11
|
+
}
|
12
|
+
|
13
|
+
[data.definition, data.view_type]
|
18
14
|
end
|
19
15
|
|
20
16
|
end
|
@@ -11,7 +11,9 @@ module SchemaPlus::Views
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def invert_create_view(args)
|
14
|
-
|
14
|
+
options = {}
|
15
|
+
options[:materialized] = args[2][:materialized] if args[2].has_key?(:materialized)
|
16
|
+
[ :drop_view, [args.first, options] ]
|
15
17
|
end
|
16
18
|
|
17
19
|
end
|
@@ -9,45 +9,63 @@ module SchemaPlus::Views
|
|
9
9
|
re_view_referent = %r{(?:(?i)FROM|JOIN) \S*\b(\S+)\b}
|
10
10
|
env.connection.views.each do |view_name|
|
11
11
|
next if env.dumper.ignored?(view_name)
|
12
|
-
|
12
|
+
definition, view_type = env.connection.view_full_definition(view_name)
|
13
|
+
|
14
|
+
indexes = []
|
15
|
+
|
16
|
+
if view_type == :materialized
|
17
|
+
env.connection.indexes(view_name).each do |index|
|
18
|
+
indexes << SchemaPlus::Core::SchemaDump::Table::Index.new(
|
19
|
+
name: index.name, columns: index.columns, options: view_index_options(index, env.connection)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
view = View.new(
|
25
|
+
name: view_name,
|
26
|
+
definition: definition,
|
27
|
+
view_type: view_type,
|
28
|
+
indexes: indexes
|
29
|
+
)
|
30
|
+
|
13
31
|
env.dump.tables[view.name] = view
|
14
32
|
env.dump.depends(view.name, view.definition.scan(re_view_referent).flatten)
|
15
33
|
end
|
16
34
|
end
|
17
35
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
# Take from ActiveRecord::SchemaDumper#index_parts
|
37
|
+
def view_index_options(index, connection)
|
38
|
+
options = {}
|
39
|
+
options[:unique] = true if index.unique
|
40
|
+
options[:length] = index.lengths if index.lengths.present?
|
41
|
+
options[:order] = index.orders if index.orders.present?
|
42
|
+
options[:opclass] = index.opclasses if index.opclasses.present?
|
43
|
+
options[:where] = index.where if index.where
|
44
|
+
options[:using] = index.using if !connection.default_index_type?(index)
|
45
|
+
options[:type] = index.type if index.type
|
46
|
+
options[:comment] = index.comment if index.comment
|
26
47
|
|
27
|
-
|
28
|
-
end
|
48
|
+
options
|
29
49
|
end
|
30
|
-
end
|
31
|
-
end
|
32
50
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
51
|
+
# quacks like a SchemaMonkey Dump::Table
|
52
|
+
class View < KeyStruct[:name, :definition, :view_type, :indexes]
|
53
|
+
def assemble(stream)
|
54
|
+
extra_options = ", materialized: true" if view_type == :materialized
|
55
|
+
heredelim = "END_VIEW_#{name.upcase}"
|
56
|
+
stream.puts <<~ENDVIEW
|
57
|
+
create_view "#{name}", <<-'#{heredelim}', :force => true#{extra_options}
|
58
|
+
#{definition}
|
59
|
+
#{heredelim}
|
60
|
+
ENDVIEW
|
41
61
|
|
42
|
-
|
43
|
-
|
44
|
-
|
62
|
+
indexes.each do |index|
|
63
|
+
stream.write "add_index \"#{name}\", "
|
64
|
+
index.assemble(stream)
|
65
|
+
stream.puts ""
|
66
|
+
end
|
45
67
|
end
|
46
68
|
end
|
47
|
-
|
48
|
-
def self.filter_out_views(env)
|
49
|
-
env.tables -= env.connection.views(env.query_name)
|
50
|
-
end
|
51
69
|
end
|
52
70
|
end
|
53
71
|
|
@@ -56,11 +74,8 @@ module SchemaPlus::Views
|
|
56
74
|
# for tables
|
57
75
|
|
58
76
|
module Schema
|
59
|
-
module Views
|
60
|
-
ENV = [:connection, :query_name, :views]
|
61
|
-
end
|
62
77
|
module ViewDefinition
|
63
|
-
ENV = [:connection, :view_name, :query_name, :definition]
|
78
|
+
ENV = [:connection, :view_name, :query_name, :definition, :view_type]
|
64
79
|
end
|
65
80
|
end
|
66
81
|
|
@@ -71,6 +86,9 @@ module SchemaPlus::Views
|
|
71
86
|
module DropView
|
72
87
|
ENV = [:connection, :view_name, :options]
|
73
88
|
end
|
89
|
+
module RefreshView
|
90
|
+
ENV = [:connection, :view_name, :options]
|
91
|
+
end
|
74
92
|
end
|
75
93
|
end
|
76
94
|
|