pg_query 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/lib/pg_query/deparse.rb +143 -12
- data/lib/pg_query/deparse/alter_table.rb +143 -0
- data/lib/pg_query/deparse/interval.rb +105 -0
- data/lib/pg_query/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75abf971dc2e2ed3ba84e61b7964e7c52ac62faf
|
4
|
+
data.tar.gz: e80a542497240ffc2c36892422b972ff3049f02d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85e96b1d41a656569bf83a716194d71907f7c9d8c0a9c7282b30bc4b0110bdbc6c90d5cbf6df95c6211bd011d73763e58205de8eb626e5855c944ce96b6be0ac
|
7
|
+
data.tar.gz: 7f5b864b1e61711da44c0bca0d318edf719506633297310d9be4d7daf17729e7602daa2a4e191c19039670e54ac9a4f404fd3ab115caf0a2ffac21c7c9ce977d
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## 0.6.
|
3
|
+
## 0.6.5 UNRELEASED
|
4
4
|
|
5
5
|
* ...
|
6
6
|
|
7
7
|
|
8
|
+
## 0.6.4 2015-10-01
|
9
|
+
|
10
|
+
* Deparsing
|
11
|
+
* Constraints & Interval Types [#28](https://github.com/lfittl/pg_query/pull/28) [@JackDanger](https://github.com/JackDanger)
|
12
|
+
* Cross joins [#29](https://github.com/lfittl/pg_query/pull/29) [@mme](https://github.com/mme)
|
13
|
+
* ALTER TABLE [#30](https://github.com/lfittl/pg_query/pull/30) [@JackDanger](https://github.com/JackDanger)
|
14
|
+
* LIMIT and OFFSET [#33](https://github.com/lfittl/pg_query/pull/33) [@jcsjcs](https://github.com/jcsjcs)
|
15
|
+
|
16
|
+
|
8
17
|
## 0.6.3 2015-08-20
|
9
18
|
|
10
19
|
* Deparsing
|
data/lib/pg_query/deparse.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
require_relative 'deparse/interval'
|
2
|
+
require_relative 'deparse/alter_table'
|
1
3
|
class PgQuery
|
2
4
|
# Reconstruct all of the parsed queries into their original form
|
3
5
|
def deparse(tree = @parsetree)
|
4
6
|
tree.map do |item|
|
5
|
-
|
7
|
+
Deparse.from(item)
|
6
8
|
end.join('; ')
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
# rubocop:disable Metrics/ModuleLength
|
12
|
+
module Deparse
|
13
|
+
extend self
|
14
|
+
|
10
15
|
# Given one element of the PgQuery#parsetree reconstruct it back into the
|
11
16
|
# original query.
|
12
|
-
def
|
17
|
+
def from(item)
|
13
18
|
deparse_item(item)
|
14
19
|
end
|
15
20
|
|
@@ -17,6 +22,7 @@ class PgQuery
|
|
17
22
|
|
18
23
|
def deparse_item(item, context = nil) # rubocop:disable Metrics/CyclomaticComplexity
|
19
24
|
return if item.nil?
|
25
|
+
return item if item.is_a?(Fixnum)
|
20
26
|
|
21
27
|
type = item.keys[0]
|
22
28
|
node = item.values[0]
|
@@ -33,9 +39,13 @@ class PgQuery
|
|
33
39
|
when 'AEXPR OR'
|
34
40
|
deparse_aexpr_or(node)
|
35
41
|
when 'AEXPR'
|
36
|
-
deparse_aexpr(node)
|
42
|
+
deparse_aexpr(node, context)
|
37
43
|
when 'ALIAS'
|
38
44
|
deparse_alias(node)
|
45
|
+
when 'ALTER TABLE'
|
46
|
+
deparse_alter_table(node)
|
47
|
+
when 'ALTER TABLE CMD'
|
48
|
+
deparse_alter_table_cmd(node)
|
39
49
|
when 'A_ARRAYEXPR'
|
40
50
|
deparse_a_arrayexp(node)
|
41
51
|
when 'A_CONST'
|
@@ -68,6 +78,8 @@ class PgQuery
|
|
68
78
|
deparse_defelem(node)
|
69
79
|
when 'DELETE FROM'
|
70
80
|
deparse_delete_from(node)
|
81
|
+
when 'DROP'
|
82
|
+
deparse_drop(node)
|
71
83
|
when 'FUNCCALL'
|
72
84
|
deparse_funccall(node)
|
73
85
|
when 'FUNCTIONPARAMETER'
|
@@ -86,6 +98,8 @@ class PgQuery
|
|
86
98
|
deparse_rangesubselect(node)
|
87
99
|
when 'RANGEVAR'
|
88
100
|
deparse_rangevar(node)
|
101
|
+
when 'RENAMESTMT'
|
102
|
+
deparse_renamestmt(node)
|
89
103
|
when 'RESTARGET'
|
90
104
|
deparse_restarget(node, context)
|
91
105
|
when 'ROW'
|
@@ -125,6 +139,19 @@ class PgQuery
|
|
125
139
|
output.join(' ')
|
126
140
|
end
|
127
141
|
|
142
|
+
def deparse_renamestmt(node)
|
143
|
+
output = []
|
144
|
+
|
145
|
+
if node['renameType'] == 26 # table
|
146
|
+
output << 'ALTER TABLE'
|
147
|
+
output << deparse_item(node['relation'])
|
148
|
+
output << 'RENAME TO'
|
149
|
+
output << node['newname']
|
150
|
+
end
|
151
|
+
|
152
|
+
output.join(' ')
|
153
|
+
end
|
154
|
+
|
128
155
|
def deparse_columnref(node)
|
129
156
|
node['fields'].map do |field|
|
130
157
|
field.is_a?(String) ? field : deparse_item(field)
|
@@ -166,6 +193,33 @@ class PgQuery
|
|
166
193
|
end
|
167
194
|
end
|
168
195
|
|
196
|
+
def deparse_alter_table(node)
|
197
|
+
output = []
|
198
|
+
output << 'ALTER TABLE'
|
199
|
+
|
200
|
+
output << deparse_item(node['relation'])
|
201
|
+
|
202
|
+
output << node['cmds'].map do |item|
|
203
|
+
deparse_item(item)
|
204
|
+
end.join(', ')
|
205
|
+
|
206
|
+
output.join(' ')
|
207
|
+
end
|
208
|
+
|
209
|
+
def deparse_alter_table_cmd(node)
|
210
|
+
command, options = AlterTable.commands(node)
|
211
|
+
|
212
|
+
output = []
|
213
|
+
output << command if command
|
214
|
+
output << 'IF EXISTS' if node['missing_ok']
|
215
|
+
output << node['name']
|
216
|
+
output << options if options
|
217
|
+
output << deparse_item(node['def']) if node['def']
|
218
|
+
output << 'CASCADE' if node['behavior'] == 1
|
219
|
+
|
220
|
+
output.compact.join(' ')
|
221
|
+
end
|
222
|
+
|
169
223
|
def deparse_paramref(node)
|
170
224
|
if node['number'] == 0
|
171
225
|
'?'
|
@@ -194,7 +248,8 @@ class PgQuery
|
|
194
248
|
# COUNT(*)
|
195
249
|
args << '*' if node['agg_star']
|
196
250
|
|
197
|
-
|
251
|
+
name = (node['funcname'] - ['pg_catalog']).join('.')
|
252
|
+
output << format('%s(%s)', name, args.join(', '))
|
198
253
|
output << format('OVER (%s)', deparse_item(node['over'])) if node['over']
|
199
254
|
|
200
255
|
output.join(' ')
|
@@ -241,11 +296,16 @@ class PgQuery
|
|
241
296
|
output.join(' ')
|
242
297
|
end
|
243
298
|
|
244
|
-
def deparse_aexpr(node)
|
299
|
+
def deparse_aexpr(node, context = false)
|
245
300
|
output = []
|
246
|
-
output << deparse_item(node['lexpr'])
|
247
|
-
output << deparse_item(node['rexpr'])
|
248
|
-
output.join(' ' + node['name'][0] + ' ')
|
301
|
+
output << deparse_item(node['lexpr'], context || true)
|
302
|
+
output << deparse_item(node['rexpr'], context || true)
|
303
|
+
output = output.join(' ' + node['name'][0] + ' ')
|
304
|
+
if context
|
305
|
+
# This is a nested expression, add parentheses.
|
306
|
+
output = '(' + output + ')'
|
307
|
+
end
|
308
|
+
output
|
249
309
|
end
|
250
310
|
|
251
311
|
def deparse_aexpr_and(node)
|
@@ -273,6 +333,7 @@ class PgQuery
|
|
273
333
|
output = []
|
274
334
|
output << deparse_item(node['larg'])
|
275
335
|
output << 'LEFT' if node['jointype'] == 1
|
336
|
+
output << 'CROSS' if node['jointype'] == 0 && node['quals'].nil?
|
276
337
|
output << 'JOIN'
|
277
338
|
output << deparse_item(node['rarg'])
|
278
339
|
|
@@ -347,19 +408,35 @@ class PgQuery
|
|
347
408
|
def deparse_columndef(node)
|
348
409
|
output = [node['colname']]
|
349
410
|
output << deparse_item(node['typeName'])
|
411
|
+
if node['raw_default']
|
412
|
+
output << 'USING'
|
413
|
+
output << deparse_item(node['raw_default'])
|
414
|
+
end
|
350
415
|
if node['constraints']
|
351
416
|
output += node['constraints'].map do |item|
|
352
417
|
deparse_item(item)
|
353
418
|
end
|
354
419
|
end
|
355
|
-
output.join(' ')
|
420
|
+
output.compact.join(' ')
|
356
421
|
end
|
357
422
|
|
358
423
|
def deparse_constraint(node)
|
359
424
|
output = []
|
425
|
+
if node['conname']
|
426
|
+
output << 'CONSTRAINT'
|
427
|
+
output << node['conname']
|
428
|
+
end
|
360
429
|
# NOT_NULL -> NOT NULL
|
361
430
|
output << node['contype'].gsub('_', ' ')
|
362
|
-
|
431
|
+
|
432
|
+
if node['raw_expr']
|
433
|
+
expression = deparse_item(node['raw_expr'])
|
434
|
+
# Unless it's simple, put parentheses around it
|
435
|
+
expression = '(' + expression + ')' if node['raw_expr'].keys == ['AEXPR']
|
436
|
+
output << expression
|
437
|
+
end
|
438
|
+
output << '(' + node['keys'].join(', ') + ')' if node['keys']
|
439
|
+
output << "USING INDEX #{node['indexname']}" if node['indexname']
|
363
440
|
output.join(' ')
|
364
441
|
end
|
365
442
|
|
@@ -487,6 +564,16 @@ class PgQuery
|
|
487
564
|
end.join(', ')
|
488
565
|
end
|
489
566
|
|
567
|
+
if node['limitCount']
|
568
|
+
output << 'LIMIT'
|
569
|
+
output << deparse_item(node['limitCount'])
|
570
|
+
end
|
571
|
+
|
572
|
+
if node['limitOffset']
|
573
|
+
output << 'OFFSET'
|
574
|
+
output << deparse_item(node['limitOffset'])
|
575
|
+
end
|
576
|
+
|
490
577
|
output.join(' ')
|
491
578
|
end
|
492
579
|
|
@@ -547,6 +634,10 @@ class PgQuery
|
|
547
634
|
end
|
548
635
|
|
549
636
|
def deparse_typename(node)
|
637
|
+
# Intervals are tricky and should be handled in a separate method because
|
638
|
+
# they require performing some bitmask operations.
|
639
|
+
return deparse_interval_type(node) if node['names'] == %w(pg_catalog interval)
|
640
|
+
|
550
641
|
output = []
|
551
642
|
output << 'SETOF' if node['setof']
|
552
643
|
|
@@ -555,7 +646,6 @@ class PgQuery
|
|
555
646
|
deparse_item(item)
|
556
647
|
end.join(', ')
|
557
648
|
end
|
558
|
-
|
559
649
|
output << deparse_typename_cast(node['names'], arguments)
|
560
650
|
|
561
651
|
output.join(' ')
|
@@ -589,11 +679,39 @@ class PgQuery
|
|
589
679
|
'real'
|
590
680
|
when 'float8'
|
591
681
|
'double'
|
682
|
+
when 'time'
|
683
|
+
'time'
|
684
|
+
when 'timetz'
|
685
|
+
'time with time zone'
|
686
|
+
when 'timestamp'
|
687
|
+
'timestamp'
|
688
|
+
when 'timestamptz'
|
689
|
+
'timestamp with time zone'
|
592
690
|
else
|
593
691
|
fail format("Can't deparse type: %s", type)
|
594
692
|
end
|
595
693
|
end
|
596
694
|
|
695
|
+
# Deparses interval type expressions like `interval year to month` or
|
696
|
+
# `interval hour to second(5)`
|
697
|
+
def deparse_interval_type(node)
|
698
|
+
type = ['interval']
|
699
|
+
|
700
|
+
if node['typmods']
|
701
|
+
typmods = node['typmods'].map { |typmod| deparse_item(typmod) }
|
702
|
+
type << Interval.from_int(typmods.first.to_i).map do |part|
|
703
|
+
# only the `second` type can take an argument.
|
704
|
+
if part == 'second' && typmods.size == 2
|
705
|
+
"second(#{typmods.last})"
|
706
|
+
else
|
707
|
+
part
|
708
|
+
end.downcase
|
709
|
+
end.join(' to ')
|
710
|
+
end
|
711
|
+
|
712
|
+
type.join(' ')
|
713
|
+
end
|
714
|
+
|
597
715
|
def deparse_nulltest(node)
|
598
716
|
output = [deparse_item(node['arg'])]
|
599
717
|
if node['nulltesttype'] == 0
|
@@ -668,6 +786,19 @@ class PgQuery
|
|
668
786
|
output.join(' ')
|
669
787
|
end
|
670
788
|
|
789
|
+
def deparse_drop(node)
|
790
|
+
output = ['DROP']
|
791
|
+
output << 'TABLE' if node['removeType'] == 26
|
792
|
+
output << 'CONCURRENTLY' if node['concurrent']
|
793
|
+
output << 'IF EXISTS' if node['missing_ok']
|
794
|
+
|
795
|
+
output << node['objects'].join(', ')
|
796
|
+
|
797
|
+
output << 'CASCADE' if node['behavior'] == 1
|
798
|
+
|
799
|
+
output.join(' ')
|
800
|
+
end
|
801
|
+
|
671
802
|
# The PG parser adds several pieces of view data onto the RANGEVAR
|
672
803
|
# that need to be printed before deparse_rangevar is called.
|
673
804
|
def relpersistence(rangevar)
|
@@ -0,0 +1,143 @@
|
|
1
|
+
class PgQuery
|
2
|
+
module Deparse
|
3
|
+
module AlterTable
|
4
|
+
# Returns a list of strings of length one or length two. The first string
|
5
|
+
# will be placed before the column name and the second, if present, will be
|
6
|
+
# placed after.
|
7
|
+
#
|
8
|
+
# If node['subtype'] is the integer 4,
|
9
|
+
# Then the fifth ALTER_TABLE entry ('DropNotNull') will be selected
|
10
|
+
# And the return value of this method will be:
|
11
|
+
#
|
12
|
+
# ['ALTER COLUMN', 'DROP NOT NULL']
|
13
|
+
#
|
14
|
+
# Which will be composed into the SQL as:
|
15
|
+
#
|
16
|
+
# ALTER COLUMN {column_name} DROP NOT NULL
|
17
|
+
#
|
18
|
+
def self.commands(node)
|
19
|
+
action = ALTER_TABLE[ALTER_TABLE_COMMANDS[node['subtype']]]
|
20
|
+
PgQuery::Deparse.instance_exec(node, &action)
|
21
|
+
end
|
22
|
+
|
23
|
+
# From include/nodes/parsenodes.h:1455
|
24
|
+
#
|
25
|
+
# Many of these will not be implemented here as they'll never be part of
|
26
|
+
# valid SQL. We keep them all here in their original order because we look
|
27
|
+
# these values up by array index.
|
28
|
+
NOT_IMPLEMENTED = 'NotImplemented'
|
29
|
+
ALTER_TABLE = {
|
30
|
+
# add column
|
31
|
+
'AddColumn' => -> (_node) { ['ADD COLUMN'] },
|
32
|
+
# internal to commands/tablecmds.c
|
33
|
+
'AddColumnRecurse' => -> (_node) { NotImplemented },
|
34
|
+
# implicitly via CREATE OR REPLACE VIEW
|
35
|
+
'AddColumnToView' => -> (_node) { NotImplemented },
|
36
|
+
# alter column default
|
37
|
+
'ColumnDefault' => -> (node) { ['ALTER COLUMN', node['def'] ? 'SET DEFAULT' : 'DROP DEFAULT'] },
|
38
|
+
# alter column drop not null
|
39
|
+
'DropNotNull' => -> (_node) { ['ALTER COLUMN', 'DROP NOT NULL'] },
|
40
|
+
# alter column set not null
|
41
|
+
'SetNotNull' => -> (_node) { ['ALTER COLUMN', 'SET NOT NULL'] },
|
42
|
+
# alter column set statistics
|
43
|
+
'SetStatistics' => -> (_node) { ['ALTER COLUMN', 'SET STATISTICS'] },
|
44
|
+
# alter column set ( options )
|
45
|
+
'SetOptions' => -> (_node) { ['ALTER COLUMN', 'SET'] },
|
46
|
+
# alter column reset ( options )
|
47
|
+
'ResetOptions' => -> (_node) { ['ALTER COLUMN', 'RESET'] },
|
48
|
+
# alter column set storage
|
49
|
+
'SetStorage' => -> (_node) { ['ALTER COLUMN', 'SET STORAGE'] },
|
50
|
+
# drop column
|
51
|
+
'DropColumn' => -> (_node) { ['DROP'] },
|
52
|
+
# internal to commands/tablecmds.c
|
53
|
+
'DropColumnRecurse' => -> (_node) { NotImplemented },
|
54
|
+
# add index
|
55
|
+
'AddIndex' => -> (_node) { ['ADD INDEX'] },
|
56
|
+
# internal to commands/tablecmds.c
|
57
|
+
'ReAddIndex' => -> (_node) { NotImplemented },
|
58
|
+
# add constraint
|
59
|
+
'AddConstraint' => -> (_node) { ['ADD'] },
|
60
|
+
# internal to commands/tablecmds.c
|
61
|
+
'AddConstraintRecurse' => -> (_node) { NotImplemented },
|
62
|
+
# internal to commands/tablecmds.c
|
63
|
+
'ReAddConstraint' => -> (_node) { NotImplemented },
|
64
|
+
# alter constraint
|
65
|
+
'AlterConstraint' => -> (_node) { ['ALTER CONSTRAINT'] },
|
66
|
+
# validate constraint
|
67
|
+
'ValidateConstraint' => -> (_node) { ['VALIDATE CONSTRAINT'] },
|
68
|
+
# internal to commands/tablecmds.c
|
69
|
+
'ValidateConstraintRecurse' => -> (_node) { NotImplemented },
|
70
|
+
# pre-processed add constraint (local in parser/parse_utilcmd.c)
|
71
|
+
'ProcessedConstraint' => -> (_node) { NotImplemented },
|
72
|
+
# add constraint using existing index
|
73
|
+
'AddIndexConstraint' => -> (_node) { NotImplemented },
|
74
|
+
# drop constraint
|
75
|
+
'DropConstraint' => -> (_node) { ['DROP CONSTRAINT'] },
|
76
|
+
# internal to commands/tablecmds.c
|
77
|
+
'DropConstraintRecurse' => -> (_node) { NotImplemented },
|
78
|
+
# alter column type
|
79
|
+
'AlterColumnType' => -> (_node) { ['ALTER COLUMN', 'TYPE'] },
|
80
|
+
# alter column OPTIONS (...)
|
81
|
+
'AlterColumnGenericOptions' => -> (_node) { ['ALTER COLUMN', 'OPTIONS'] },
|
82
|
+
# change owner
|
83
|
+
'ChangeOwner' => -> (_node) { NotImplemented },
|
84
|
+
# CLUSTER ON
|
85
|
+
'ClusterOn' => -> (_node) { NotImplemented },
|
86
|
+
# SET WITHOUT CLUSTER
|
87
|
+
'DropCluster' => -> (_node) { NotImplemented },
|
88
|
+
# SET WITH OIDS
|
89
|
+
'AddOids' => -> (_node) { NotImplemented },
|
90
|
+
# internal to commands/tablecmds.c
|
91
|
+
'AddOidsRecurse' => -> (_node) { NotImplemented },
|
92
|
+
# SET WITHOUT OIDS
|
93
|
+
'DropOids' => -> (_node) { NotImplemented },
|
94
|
+
# SET TABLESPACE
|
95
|
+
'SetTableSpace' => -> (_node) { NotImplemented },
|
96
|
+
# SET (...) -- AM specific parameters
|
97
|
+
'SetRelOptions' => -> (_node) { NotImplemented },
|
98
|
+
# RESET (...) -- AM specific parameters
|
99
|
+
'ResetRelOptions' => -> (_node) { NotImplemented },
|
100
|
+
# replace reloption list in its entirety
|
101
|
+
'ReplaceRelOptions' => -> (_node) { NotImplemented },
|
102
|
+
# ENABLE TRIGGER name
|
103
|
+
'EnableTrig' => -> (_node) { NotImplemented },
|
104
|
+
# ENABLE ALWAYS TRIGGER name
|
105
|
+
'EnableAlwaysTrig' => -> (_node) { NotImplemented },
|
106
|
+
# ENABLE REPLICA TRIGGER name
|
107
|
+
'EnableReplicaTrig' => -> (_node) { NotImplemented },
|
108
|
+
# DISABLE TRIGGER name
|
109
|
+
'DisableTrig' => -> (_node) { NotImplemented },
|
110
|
+
# ENABLE TRIGGER ALL
|
111
|
+
'EnableTrigAll' => -> (_node) { NotImplemented },
|
112
|
+
# DISABLE TRIGGER ALL
|
113
|
+
'DisableTrigAll' => -> (_node) { NotImplemented },
|
114
|
+
# ENABLE TRIGGER USER
|
115
|
+
'EnableTrigUser' => -> (_node) { NotImplemented },
|
116
|
+
# DISABLE TRIGGER USER
|
117
|
+
'DisableTrigUser' => -> (_node) { NotImplemented },
|
118
|
+
# ENABLE RULE name
|
119
|
+
'EnableRule' => -> (_node) { NotImplemented },
|
120
|
+
# ENABLE ALWAYS RULE name
|
121
|
+
'EnableAlwaysRule' => -> (_node) { NotImplemented },
|
122
|
+
# ENABLE REPLICA RULE name
|
123
|
+
'EnableReplicaRule' => -> (_node) { NotImplemented },
|
124
|
+
# DISABLE RULE name
|
125
|
+
'DisableRule' => -> (_node) { NotImplemented },
|
126
|
+
# INHERIT parent
|
127
|
+
'AddInherit' => -> (_node) { NotImplemented },
|
128
|
+
# NO INHERIT parent
|
129
|
+
'DropInherit' => -> (_node) { NotImplemented },
|
130
|
+
# OF <type_name>
|
131
|
+
'AddOf' => -> (_node) { NotImplemented },
|
132
|
+
# NOT OF
|
133
|
+
'DropOf' => -> (_node) { NotImplemented },
|
134
|
+
# REPLICA IDENTITY
|
135
|
+
'ReplicaIdentity' => -> (_node) { NotImplemented },
|
136
|
+
# OPTIONS (...)
|
137
|
+
'GenericOptions' => -> (_node) { NotImplemented }
|
138
|
+
}
|
139
|
+
# Relying on Ruby's hashes maintaining key sort order
|
140
|
+
ALTER_TABLE_COMMANDS = ALTER_TABLE.keys
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class PgQuery
|
2
|
+
module Deparse
|
3
|
+
module Interval
|
4
|
+
# A type called 'interval hour to minute' is stored in a compressed way by
|
5
|
+
# simplifying 'hour to minute' to a simple integer. This integer is computed
|
6
|
+
# by looking up the arbitrary number (always a power of two) for 'hour' and
|
7
|
+
# the one for 'minute' and XORing them together.
|
8
|
+
#
|
9
|
+
# For example, when parsing "interval hour to minute":
|
10
|
+
#
|
11
|
+
# HOUR_MASK = 10
|
12
|
+
# MINUTE_MASK = 11
|
13
|
+
# mask = (1 << 10) | (1 << 11)
|
14
|
+
# mask = 1024 | 2048
|
15
|
+
# mask = (010000000000
|
16
|
+
# xor
|
17
|
+
# 100000000000)
|
18
|
+
# mask = 110000000000
|
19
|
+
# mask = 3072
|
20
|
+
#
|
21
|
+
# Postgres will store this type as 'interval,3072'
|
22
|
+
# We deparse it by simply reversing that process.
|
23
|
+
#
|
24
|
+
def self.from_int(int)
|
25
|
+
SQL_BY_MASK[int]
|
26
|
+
end
|
27
|
+
|
28
|
+
# From src/include/utils/datetime.h
|
29
|
+
# The number is the power of 2 used for the mask.
|
30
|
+
MASKS = {
|
31
|
+
0 => 'RESERV',
|
32
|
+
1 => 'MONTH',
|
33
|
+
2 => 'YEAR',
|
34
|
+
3 => 'DAY',
|
35
|
+
4 => 'JULIAN',
|
36
|
+
5 => 'TZ',
|
37
|
+
6 => 'DTZ',
|
38
|
+
7 => 'DYNTZ',
|
39
|
+
8 => 'IGNORE_DTF',
|
40
|
+
9 => 'AMPM',
|
41
|
+
10 => 'HOUR',
|
42
|
+
11 => 'MINUTE',
|
43
|
+
12 => 'SECOND',
|
44
|
+
13 => 'MILLISECOND',
|
45
|
+
14 => 'MICROSECOND',
|
46
|
+
15 => 'DOY',
|
47
|
+
16 => 'DOW',
|
48
|
+
17 => 'UNITS',
|
49
|
+
18 => 'ADBC',
|
50
|
+
19 => 'AGO',
|
51
|
+
20 => 'ABS_BEFORE',
|
52
|
+
21 => 'ABS_AFTER',
|
53
|
+
22 => 'ISODATE',
|
54
|
+
23 => 'ISOTIME',
|
55
|
+
24 => 'WEEK',
|
56
|
+
25 => 'DECADE',
|
57
|
+
26 => 'CENTURY',
|
58
|
+
27 => 'MILLENNIUM',
|
59
|
+
28 => 'DTZMOD'
|
60
|
+
}
|
61
|
+
KEYS = MASKS.invert
|
62
|
+
|
63
|
+
# Postgres stores the interval 'day second' as 'day hour minute second' so
|
64
|
+
# we need to reconstruct the sql with only the largest and smallest time
|
65
|
+
# values. Since the rules for this are hardcoded in the grammar (and the
|
66
|
+
# above list is not sorted in any sensible way) it makes sense to hardcode
|
67
|
+
# the patterns here, too.
|
68
|
+
#
|
69
|
+
# This hash takes the form:
|
70
|
+
#
|
71
|
+
# { (1 << 1) | (1 << 2) => 'year to month' }
|
72
|
+
#
|
73
|
+
# Which is:
|
74
|
+
#
|
75
|
+
# { 6 => 'year to month' }
|
76
|
+
#
|
77
|
+
SQL_BY_MASK = {
|
78
|
+
(1 << KEYS['YEAR']) => %w(year),
|
79
|
+
(1 << KEYS['MONTH']) => %w(month),
|
80
|
+
(1 << KEYS['DAY']) => %w(day),
|
81
|
+
(1 << KEYS['HOUR']) => %w(hour),
|
82
|
+
(1 << KEYS['MINUTE']) => %w(minute),
|
83
|
+
(1 << KEYS['SECOND']) => %w(second),
|
84
|
+
(1 << KEYS['YEAR'] |
|
85
|
+
1 << KEYS['MONTH']) => %w(year month),
|
86
|
+
(1 << KEYS['DAY'] |
|
87
|
+
1 << KEYS['HOUR']) => %w(day hour),
|
88
|
+
(1 << KEYS['DAY'] |
|
89
|
+
1 << KEYS['HOUR'] |
|
90
|
+
1 << KEYS['MINUTE']) => %w(day minute),
|
91
|
+
(1 << KEYS['DAY'] |
|
92
|
+
1 << KEYS['HOUR'] |
|
93
|
+
1 << KEYS['MINUTE'] |
|
94
|
+
1 << KEYS['SECOND']) => %w(day second),
|
95
|
+
(1 << KEYS['HOUR'] |
|
96
|
+
1 << KEYS['MINUTE']) => %w(hour minute),
|
97
|
+
(1 << KEYS['HOUR'] |
|
98
|
+
1 << KEYS['MINUTE'] |
|
99
|
+
1 << KEYS['SECOND']) => %w(hour second),
|
100
|
+
(1 << KEYS['MINUTE'] |
|
101
|
+
1 << KEYS['SECOND']) => %w(minute second)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/pg_query/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukas Fittl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -102,6 +102,8 @@ files:
|
|
102
102
|
- ext/pg_query/pg_query_parse.c
|
103
103
|
- lib/pg_query.rb
|
104
104
|
- lib/pg_query/deparse.rb
|
105
|
+
- lib/pg_query/deparse/alter_table.rb
|
106
|
+
- lib/pg_query/deparse/interval.rb
|
105
107
|
- lib/pg_query/filter_columns.rb
|
106
108
|
- lib/pg_query/fingerprint.rb
|
107
109
|
- lib/pg_query/param_refs.rb
|
@@ -134,9 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
136
|
version: '0'
|
135
137
|
requirements: []
|
136
138
|
rubyforge_project:
|
137
|
-
rubygems_version: 2.4.5
|
139
|
+
rubygems_version: 2.4.5.1
|
138
140
|
signing_key:
|
139
141
|
specification_version: 4
|
140
142
|
summary: PostgreSQL query parsing and normalization library
|
141
143
|
test_files: []
|
142
|
-
has_rdoc:
|