pg_query 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|