Class: PgQuery

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_query/parse.rb,
lib/pg_query/version.rb,
lib/pg_query/parse_error.rb

Defined Under Namespace

Classes: ParseError

Constant Summary collapse

VERSION =
'0.3.2'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query, parsetree, warnings = []) ⇒ PgQuery

Returns a new instance of PgQuery.



25
26
27
28
29
# File 'lib/pg_query/parse.rb', line 25

def initialize(query, parsetree, warnings = [])
  @query = query
  @parsetree = parsetree
  @warnings = warnings
end

Instance Attribute Details

#parsetreeObject (readonly)

Returns the value of attribute parsetree.



23
24
25
# File 'lib/pg_query/parse.rb', line 23

def parsetree
  @parsetree
end

#queryObject (readonly)

Returns the value of attribute query.



22
23
24
# File 'lib/pg_query/parse.rb', line 22

def query
  @query
end

#warningsObject (readonly)

Returns the value of attribute warnings.



24
25
26
# File 'lib/pg_query/parse.rb', line 24

def warnings
  @warnings
end

Class Method Details

._raw_parse(input) ⇒ Object

#define DEBUG



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'ext/pg_query/pg_query.c', line 36

static VALUE pg_query_raw_parse(VALUE self, VALUE input)
{
	Check_Type(input, T_STRING);
	
	MemoryContext ctx = NULL;
	VALUE result = Qnil;
	VALUE error = Qnil;
	char stderr_buffer[STDERR_BUFFER_LEN + 1] = {0};
#ifndef DEBUG
	int stderr_global;
	int stderr_pipe[2];
#endif

	ctx = AllocSetContextCreate(TopMemoryContext,
								"pg_query_raw_parse",
								ALLOCSET_DEFAULT_MINSIZE,
								ALLOCSET_DEFAULT_INITSIZE,
								ALLOCSET_DEFAULT_MAXSIZE);
	MemoryContextSwitchTo(ctx);
	
#ifndef DEBUG
	// Setup pipe for stderr redirection
	if (pipe(stderr_pipe) != 0)
		rb_raise(rb_eIOError, "Failed to open pipe, too many open file descriptors");

	fcntl(stderr_pipe[0], F_SETFL, fcntl(stderr_pipe[0], F_GETFL) | O_NONBLOCK);
	
	// Redirect stderr to the pipe
	stderr_global = dup(STDERR_FILENO);
	dup2(stderr_pipe[1], STDERR_FILENO);
	close(stderr_pipe[1]);
#endif
	
	// Parse it!
	PG_TRY();
	{
		List *tree;
		char *str;
		
		str = StringValueCStr(input);
		tree = raw_parser(str);
		
		str = nodeToJSONString(tree);
	
#ifndef DEBUG
		// Save stderr for result
		read(stderr_pipe[0], stderr_buffer, STDERR_BUFFER_LEN);
#endif

		result = rb_ary_new();
		rb_ary_push(result, rb_str_new2(str));
		rb_ary_push(result, rb_str_new2(stderr_buffer));
	
		pfree(str);
	}
	PG_CATCH();
	{
		ErrorData* error_data = CopyErrorData();
		error = new_parse_error(error_data);
		FlushErrorState();
	}
	PG_END_TRY();
	
#ifndef DEBUG
	// Restore stderr, close pipe
	dup2(stderr_global, STDERR_FILENO);
	close(stderr_pipe[0]);
	close(stderr_global);
#endif

	// Return to previous PostgreSQL memory context
	MemoryContextSwitchTo(TopMemoryContext);
	MemoryContextDelete(ctx);
	
	// If we got an error, throw it
	if (!NIL_P(error)) rb_exc_raise(error);
	
	return result;
}

.normalize(input) ⇒ Object



408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'ext/pg_query/pg_query.c', line 408

static VALUE pg_query_normalize(VALUE self, VALUE input)
{
	Check_Type(input, T_STRING);
	
	MemoryContext ctx = NULL;
	VALUE result = Qnil;
	VALUE error = Qnil;
	
	ctx = AllocSetContextCreate(TopMemoryContext,
								"pg_query_normalize",
								ALLOCSET_DEFAULT_MINSIZE,
								ALLOCSET_DEFAULT_INITSIZE,
								ALLOCSET_DEFAULT_MAXSIZE);
	MemoryContextSwitchTo(ctx);
	
	PG_TRY();
	{
		List *tree;
		char *str;
		pgssConstLocations jstate;
		int query_len;
		
		/* Parse query */
		str = StringValueCStr(input);
		tree = raw_parser(str);
		
		/* Set up workspace for constant recording */
		jstate.clocations_buf_size = 32;
		jstate.clocations = (pgssLocationLen *)
			palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
		jstate.clocations_count = 0;
		
		/* Walk tree and record const locations */
		const_record_walker((Node *) tree, &jstate);
		
		/* Normalize query */
		query_len = (int) strlen(str);
		str = generate_normalized_query(&jstate, str, &query_len, PG_UTF8);
	
		result = rb_str_new2(str);
	
		pfree(str);
	}
	PG_CATCH();
	{
		ErrorData* error_data = CopyErrorData();
		error = new_parse_error(error_data);
		FlushErrorState();
	}
	PG_END_TRY();
	
	MemoryContextSwitchTo(TopMemoryContext);
	MemoryContextDelete(ctx);
	
	// If we got an error, throw it
	if (!NIL_P(error)) rb_exc_raise(error);
	
	return result;
}

.parse(query) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/pg_query/parse.rb', line 4

def self.parse(query)
  parsetree, stderr = _raw_parse(query)

  begin
    parsetree = JSON.parse(parsetree, max_nesting: 1000)
  rescue JSON::ParserError => e
    raise ParseError.new("Failed to parse JSON", -1)
  end

  warnings = []
  stderr.each_line do |line|
    next unless line[/^WARNING/]
    warnings << line.strip
  end

  PgQuery.new(query, parsetree, warnings)
end

Instance Method Details

#aliasesObject



36
37
38
39
# File 'lib/pg_query/parse.rb', line 36

def aliases
  load_tables_and_aliases! if @aliases.nil?
  @aliases
end

#tablesObject



31
32
33
34
# File 'lib/pg_query/parse.rb', line 31

def tables
  load_tables_and_aliases! if @tables.nil?
  @tables
end