prestogres 0.3.0 → 0.4.0
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.
- data/ChangeLog +11 -0
- data/README.md +100 -39
- data/VERSION +1 -1
- data/bin/prestogres +5 -5
- data/config/pgpool.conf +1 -1
- data/ext/depend +11 -12
- data/pgpool2/child.c +1 -1
- data/pgpool2/config.h.in +3 -0
- data/pgpool2/configure +39 -0
- data/pgpool2/configure.in +1 -0
- data/pgpool2/pool_query_context.c +101 -19
- data/pgsql/prestogres.py +50 -28
- data/pgsql/setup.sql +18 -0
- metadata +3 -8
data/ChangeLog
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
|
2
|
+
2014-03-05 version 0.4.0:
|
3
|
+
|
4
|
+
* Preserve OID of tables when clients SELECT from system catalogs
|
5
|
+
* Increased maximum rewritten query length to 32768 bytes
|
6
|
+
* Fixed protocol version v2 handling
|
7
|
+
* Fixed 'too long error message' errors
|
8
|
+
* Fixed build scripts
|
9
|
+
* Add "LIMIT 1000" to "SELECT * FROM table" queries automatically
|
10
|
+
(ad-hoc fix for Tableau)
|
11
|
+
|
12
|
+
|
2
13
|
2014-02-07 version 0.3.0:
|
3
14
|
|
4
15
|
* Fixed system catalog queries to support multiple schemas
|
data/README.md
CHANGED
@@ -14,54 +14,92 @@ You can use any PostgreSQL clients (see also *Limitation* section):
|
|
14
14
|
|
15
15
|
Prestogres also offers password-based authentication and SSL.
|
16
16
|
|
17
|
+
## Documents
|
18
|
+
|
19
|
+
* [How it works?](#how-it-works)
|
20
|
+
* [Limitation](#limitation)
|
21
|
+
* [Installation](#installation)
|
22
|
+
* [Running servers](#running-servers)
|
23
|
+
* [Mac OS X](#mac-os-x)
|
24
|
+
* [Configuration](#configuration)
|
25
|
+
* [pgpool.conf file](#pgpoolconf-file)
|
26
|
+
* [pool_hba.conf file](#pool_hbaconf-file)
|
27
|
+
* [prestogres_md5 method](#prestogres_md5-method)
|
28
|
+
* [prestogres_external method](#prestogres_external-method)
|
29
|
+
* [prestogres command](#prestogres-command)
|
30
|
+
* [Development](#development)
|
31
|
+
|
32
|
+
---
|
33
|
+
|
17
34
|
## How it works?
|
18
35
|
|
19
36
|
Prestogres uses modified **[pgpool-II](http://www.pgpool.net/)** to rewrite queries before sending them to PostgreSQL.
|
20
|
-
pgpool-II is originally an open-source middleware to provide connection pool and other features to PostgreSQL.
|
37
|
+
pgpool-II is originally an open-source middleware to provide connection pool and other features to PostgreSQL. For regular SQL query, patched pgpool-II wraps it in **run_presto_astemp_table(..., 'SELECT ... FROM ...')** function. This function sends the query to Presto:
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
+-----------+ +------------+ +--------+
|
28
|
-
\ \
|
29
|
-
rewrite queries run custom functions
|
30
|
-
| |
|
31
|
-
+-----------------------------------+
|
32
|
-
Prestogres
|
33
|
-
```
|
39
|
+

|
40
|
+
|
41
|
+
If the query selects records from system catalog (e.g. `\\d` command by psql), patched pgpool-II wraps the query in `run_system_catalog_as_temp_table` function. It gets table list from Presto, and actually runs **CREATE TABLE** to create records in system catalog on PostgreSQL. Then the original query runs as usual on PostgreSQL:
|
42
|
+
|
43
|
+

|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
In fact there're some other tricks. See [pgsql/prestogres.py](pgsql/prestogres.py) for the real behavior.
|
46
|
+
|
47
|
+
---
|
38
48
|
|
39
49
|
## Limitation
|
40
50
|
|
41
51
|
* Extended query is not supported ([PostgreSQL Frontend/Backend Protocol](http://www.postgresql.org/docs/9.3/static/protocol.html))
|
42
|
-
* ODBC driver needs to set
|
43
|
-
|
44
|
-
|
45
|
-
*
|
52
|
+
* ODBC driver needs to set:
|
53
|
+
* **UseServerSidePrepare=0** (Server side prepare: no) property
|
54
|
+
* **UseDeclareFetch=0** (Use Declare/Fetch: no) property
|
55
|
+
* **Unicode** mode
|
56
|
+
* JDBC driver needs to set:
|
57
|
+
* **protocolVersion=2** property
|
58
|
+
* Cursor (DECLARE/FETCH) is not supported
|
59
|
+
|
60
|
+
---
|
46
61
|
|
47
62
|
## Installation
|
48
63
|
|
64
|
+
### 1. Install PostgreSQL >= 9.3
|
65
|
+
|
66
|
+
You need to install PostgreSQL separately. Following commands install PostgreSQL 9.3 from postgresql.org:
|
67
|
+
|
68
|
+
**Ubuntu/Debian:**
|
69
|
+
|
49
70
|
```
|
50
|
-
|
71
|
+
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
72
|
+
sudo apt-get update
|
73
|
+
sudo apt-get install postgresql-9.3 postgresql-server-dev-9.3 postgresql-plpython-9.3
|
74
|
+
sudo apt-get install gcc make libssl-dev libpcre3-dev
|
75
|
+
sudo apt-get install ruby ruby-dev
|
51
76
|
```
|
52
77
|
|
53
|
-
|
78
|
+
**RedHat/CentOS:**
|
54
79
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
80
|
+
```
|
81
|
+
sudo yum install http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-redhat93-9.3-1.noarch.rpm
|
82
|
+
sudo yum install postgresql93-server postgresql93-contrib postgresql93-devel
|
83
|
+
sudo yum install gcc make openssl-devel pcre-devel
|
84
|
+
sudo yum install ruby ruby-devel
|
85
|
+
```
|
86
|
+
|
87
|
+
**Mac OS X:**
|
60
88
|
|
61
|
-
|
89
|
+
```
|
90
|
+
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
|
91
|
+
brew install postgresql
|
92
|
+
```
|
93
|
+
|
94
|
+
### 2. Install Prestogres
|
95
|
+
|
96
|
+
```
|
97
|
+
$ sudo gem install prestogres --no-ri --no-rdoc
|
98
|
+
```
|
62
99
|
|
100
|
+
---
|
63
101
|
|
64
|
-
##
|
102
|
+
## Running servers
|
65
103
|
|
66
104
|
You need to run 2 server programs: pgpool-II and PostgreSQL.
|
67
105
|
You can use `prestogres` command to setup & run them as following:
|
@@ -86,26 +124,42 @@ $ psql -h localhost -p 9900 -U pg postgres
|
|
86
124
|
|
87
125
|
If configuration is correct, you can run `SELECT * FROM sys.node;` query. Otherwise, see log files in **./pgdata/log/** directory.
|
88
126
|
|
89
|
-
####
|
127
|
+
#### Setting shmem max parameter
|
90
128
|
|
91
|
-
|
129
|
+
Probably above command fails first time! Error message is:
|
92
130
|
|
93
131
|
```
|
94
|
-
|
95
|
-
|
132
|
+
FATAL: could not create shared memory segment: Cannot allocate memory
|
133
|
+
DETAIL: Failed system call was shmget(key=6432001, size=3809280, 03600).
|
134
|
+
HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded
|
135
|
+
available memory or swap space, or exceeded your kernel's SHMALL parameter. You can either
|
136
|
+
reduce the request size or reconfigure the kernel with larger SHMALL. To reduce the request
|
137
|
+
size (currently 3809280 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing
|
138
|
+
shared_buffers or max_connections.
|
96
139
|
```
|
97
140
|
|
98
|
-
|
141
|
+
You need to set 2 kernel parameters to run PostgreSQL.
|
142
|
+
|
143
|
+
**Linux:**
|
99
144
|
|
100
145
|
```
|
101
|
-
|
102
|
-
|
103
|
-
|
146
|
+
sudo sudo -c "echo kernel.shmmax = 17179869184 >> /etc/sysctl.conf"
|
147
|
+
sudo sudo -c "echo kernel.shmall = 4194304 >> /etc/sysctl.conf"
|
148
|
+
sudo sysctl -p /etc/sysctl.conf
|
104
149
|
```
|
105
150
|
|
151
|
+
**Mac OS X:**
|
152
|
+
|
153
|
+
```
|
154
|
+
$ sudo sysctl -w kern.sysv.shmmax=1073741824
|
155
|
+
$ sudo sysctl -w kern.sysv.shmall=1073741824
|
156
|
+
```
|
157
|
+
|
158
|
+
---
|
159
|
+
|
106
160
|
## Configuration
|
107
161
|
|
108
|
-
###
|
162
|
+
### pgpool.conf file
|
109
163
|
|
110
164
|
Please read [pgpool-II documentation](http://www.pgpool.net/docs/latest/pgpool-en.html) for most of parameters.
|
111
165
|
Following parameters are unique to Prestogres:
|
@@ -184,7 +238,6 @@ See *pgool.conf file* section for available parameters.
|
|
184
238
|
|
185
239
|
If you want to reject this connection, the program exists with non-0 status code.
|
186
240
|
|
187
|
-
|
188
241
|
### Creating database on PostgreSQL
|
189
242
|
|
190
243
|
Prestogres setups a database named *postgres* on PostgreSQL by default. But you may want to create other databases
|
@@ -221,6 +274,8 @@ commands:
|
|
221
274
|
show_init_sql display statements to initialize a new PostgreSQL database
|
222
275
|
```
|
223
276
|
|
277
|
+
---
|
278
|
+
|
224
279
|
## Development
|
225
280
|
|
226
281
|
To install git HEAD, use following commands to build:
|
@@ -242,3 +297,9 @@ $ bundle exec rake
|
|
242
297
|
$ gem install --no-ri --no-rdoc pkg/prestogres-*.gem
|
243
298
|
# if this command failed, you may need to install toolchain (gcc, etc.) to build pgpool-II
|
244
299
|
```
|
300
|
+
|
301
|
+
___
|
302
|
+
|
303
|
+
Prestogres is licensed under Apache License, Version 2.0.
|
304
|
+
Copyright (C) 2014 Sadayuki Furuhashi
|
305
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/prestogres
CHANGED
@@ -68,11 +68,11 @@ usage nil unless config[:command]
|
|
68
68
|
args = ARGV[arg_offset..-1]
|
69
69
|
|
70
70
|
setup_params = {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
71
|
+
port: 9900,
|
72
|
+
backend_port: 6432,
|
73
|
+
pgpool_pid_file: nil,
|
74
|
+
pgpool_status_dir: nil,
|
75
|
+
unix_socket_directory: nil,
|
76
76
|
}
|
77
77
|
|
78
78
|
ENV['PATH'] = "#{ENV['PATH']}:#{config[:bin_path]}"
|
data/config/pgpool.conf
CHANGED
@@ -269,7 +269,7 @@ white_function_list = ''
|
|
269
269
|
# Comma separated list of function names
|
270
270
|
# that don't write to database
|
271
271
|
# Regexp are accepted
|
272
|
-
black_function_list = 'currval,lastval,nextval,setval,current_setting'
|
272
|
+
black_function_list = 'currval,lastval,nextval,setval,current_setting,version,pg_client_encoding'
|
273
273
|
# Comma separated list of function names
|
274
274
|
# that write to database
|
275
275
|
# Regexp are accepted
|
data/ext/depend
CHANGED
@@ -6,29 +6,28 @@ narchdir = $(if $(rubyarchdir),$(rubyarchdir),$(RUBYARCHDIR))
|
|
6
6
|
# hack for RubyGems >= 1.8.26
|
7
7
|
final_archdir = $(if $(findstring .gem.,$(narchdir)),$(dir $(narchdir)),$(narchdir))
|
8
8
|
|
9
|
-
|
9
|
+
NATIVE_PREFIX = $(abspath $(final_archdir)/prestogres)
|
10
10
|
|
11
|
-
|
11
|
+
CFLAGS += -DPRESTOGRES_PREFIX="\"$(NATIVE_PREFIX)\""
|
12
12
|
|
13
|
-
|
13
|
+
all: native
|
14
|
+
clean: clean-native
|
15
|
+
install: install-native
|
14
16
|
|
15
|
-
|
17
|
+
PGPOOL2_PATH = $(abspath $(srcdir)/../pgpool2)
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
install: install-pgpool2
|
20
|
-
|
21
|
-
pgpool2:
|
19
|
+
native:
|
22
20
|
mkdir -p build
|
23
21
|
cd build && "$(PGPOOL2_PATH)/configure" \
|
24
22
|
CFLAGS="-I$(PGPOOL2_PATH)" \
|
25
23
|
--with-openssl \
|
26
|
-
--prefix="$(
|
24
|
+
--prefix="$(NATIVE_PREFIX)"
|
27
25
|
cd build && make sysconfdir=/opt/prestogres/etc
|
28
26
|
|
29
|
-
clean-
|
27
|
+
clean-native:
|
30
28
|
@rm -rf build
|
31
29
|
|
32
|
-
install-
|
30
|
+
install-native:
|
33
31
|
cd build && make install
|
34
32
|
|
33
|
+
.PHONY: native clean-native install-native
|
data/pgpool2/child.c
CHANGED
@@ -99,7 +99,7 @@ static void rebuild_startup_packet(StartupPacket *sp)
|
|
99
99
|
{
|
100
100
|
if (sp->major == PROTO_MAJOR_V2) {
|
101
101
|
StartupPacket_v2 *sp2 = (StartupPacket_v2 *)(sp->startup_packet);
|
102
|
-
strncpy(sp2->database,
|
102
|
+
strncpy(sp2->database, sp->database, SM_DATABASE);
|
103
103
|
strncpy(sp2->user, sp->user, SM_USER);
|
104
104
|
|
105
105
|
} else if (sp->major == PROTO_MAJOR_V3) {
|
data/pgpool2/config.h.in
CHANGED
data/pgpool2/configure
CHANGED
@@ -11450,6 +11450,45 @@ _ACEOF
|
|
11450
11450
|
|
11451
11451
|
fi
|
11452
11452
|
|
11453
|
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpcre" >&5
|
11454
|
+
$as_echo_n "checking for main in -lpcre... " >&6; }
|
11455
|
+
if test "${ac_cv_lib_pcre_main+set}" = set; then :
|
11456
|
+
$as_echo_n "(cached) " >&6
|
11457
|
+
else
|
11458
|
+
ac_check_lib_save_LIBS=$LIBS
|
11459
|
+
LIBS="-lpcre $LIBS"
|
11460
|
+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
11461
|
+
/* end confdefs.h. */
|
11462
|
+
|
11463
|
+
|
11464
|
+
int
|
11465
|
+
main ()
|
11466
|
+
{
|
11467
|
+
return main ();
|
11468
|
+
;
|
11469
|
+
return 0;
|
11470
|
+
}
|
11471
|
+
_ACEOF
|
11472
|
+
if ac_fn_c_try_link "$LINENO"; then :
|
11473
|
+
ac_cv_lib_pcre_main=yes
|
11474
|
+
else
|
11475
|
+
ac_cv_lib_pcre_main=no
|
11476
|
+
fi
|
11477
|
+
rm -f core conftest.err conftest.$ac_objext \
|
11478
|
+
conftest$ac_exeext conftest.$ac_ext
|
11479
|
+
LIBS=$ac_check_lib_save_LIBS
|
11480
|
+
fi
|
11481
|
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcre_main" >&5
|
11482
|
+
$as_echo "$ac_cv_lib_pcre_main" >&6; }
|
11483
|
+
if test "x$ac_cv_lib_pcre_main" = x""yes; then :
|
11484
|
+
cat >>confdefs.h <<_ACEOF
|
11485
|
+
#define HAVE_LIBPCRE 1
|
11486
|
+
_ACEOF
|
11487
|
+
|
11488
|
+
LIBS="-lpcre $LIBS"
|
11489
|
+
|
11490
|
+
fi
|
11491
|
+
|
11453
11492
|
|
11454
11493
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
|
11455
11494
|
$as_echo_n "checking for ANSI C header files... " >&6; }
|
data/pgpool2/configure.in
CHANGED
@@ -32,6 +32,8 @@
|
|
32
32
|
#include <netinet/in.h>
|
33
33
|
#include <stdlib.h>
|
34
34
|
|
35
|
+
#include <pcre.h>
|
36
|
+
|
35
37
|
/*
|
36
38
|
* Where to send query
|
37
39
|
*/
|
@@ -293,7 +295,11 @@ int pool_virtual_master_db_node_id(void)
|
|
293
295
|
#define PRESTO_RESULT_TABLE_NAME "presto_result"
|
294
296
|
#endif
|
295
297
|
|
296
|
-
|
298
|
+
#ifndef PRESTO_REWRITE_QUERY_SIZE_LIMIT
|
299
|
+
#define PRESTO_REWRITE_QUERY_SIZE_LIMIT 32768
|
300
|
+
#endif
|
301
|
+
|
302
|
+
char rewrite_query_string_buffer[PRESTO_REWRITE_QUERY_SIZE_LIMIT];
|
297
303
|
|
298
304
|
static char *strcpy_capped(char *buffer, int length, const char *string)
|
299
305
|
{
|
@@ -362,7 +368,7 @@ static void do_replace_query(POOL_QUERY_CONTEXT* query_context, const char *quer
|
|
362
368
|
query_context->original_length = strlen(dupq) + 1;
|
363
369
|
}
|
364
370
|
|
365
|
-
static void
|
371
|
+
static void rewrite_error_query_static(POOL_QUERY_CONTEXT* query_context, const char *message, const char* errcode)
|
366
372
|
{
|
367
373
|
char *buffer, *bufend;
|
368
374
|
|
@@ -381,14 +387,39 @@ static void rewrite_error_query(POOL_QUERY_CONTEXT* query_context, const char *m
|
|
381
387
|
buffer = strcpy_capped(buffer, bufend - buffer, "; end $$ language plpgsql");
|
382
388
|
|
383
389
|
if (buffer == NULL) {
|
384
|
-
|
385
|
-
|
386
|
-
|
390
|
+
buffer = rewrite_query_string_buffer;
|
391
|
+
bufend = buffer + sizeof(rewrite_query_string_buffer);
|
392
|
+
|
393
|
+
buffer = strcpy_capped(buffer, bufend - buffer, "do $$ begin raise ecxeption 'too long error message'");
|
394
|
+
buffer = strcpy_capped(buffer, bufend - buffer, " using errcode = E'");
|
395
|
+
buffer = strcpy_capped_escaped(buffer, bufend - buffer, errcode, "'\\$");
|
396
|
+
buffer = strcpy_capped(buffer, bufend - buffer, "'; end $$ language plpgsql");
|
397
|
+
|
398
|
+
if (buffer == NULL) {
|
399
|
+
do_replace_query(query_context,
|
400
|
+
"do $$ begin raise ecxeption 'too long error message'; end $$ language plpgsql");
|
401
|
+
return;
|
402
|
+
}
|
387
403
|
}
|
388
404
|
|
389
405
|
do_replace_query(query_context, rewrite_query_string_buffer);
|
390
406
|
}
|
391
407
|
|
408
|
+
static void rewrite_error_query(POOL_QUERY_CONTEXT* query_context, char *message, const char* errcode)
|
409
|
+
{
|
410
|
+
/* 20 is for escape characters */
|
411
|
+
const size_t static_length = strlen("do $$ begin raise exception '%', E'' using errcode = E'XXXXX'; end $$ language plpgsql") + 20;
|
412
|
+
|
413
|
+
if (sizeof(rewrite_query_string_buffer) < strlen(message) + static_length) {
|
414
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 3] = '.';
|
415
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 2] = '.';
|
416
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 1] = '.';
|
417
|
+
message[sizeof(rewrite_query_string_buffer) - static_length - 0] = '\0';
|
418
|
+
}
|
419
|
+
|
420
|
+
rewrite_error_query_static(query_context, message, errcode);
|
421
|
+
}
|
422
|
+
|
392
423
|
static void run_clear_query(POOL_SESSION_CONTEXT* session_context, POOL_QUERY_CONTEXT* query_context)
|
393
424
|
{
|
394
425
|
// TODO
|
@@ -413,7 +444,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
413
444
|
buffer = strcpy_capped(buffer, bufend - buffer, "'");
|
414
445
|
|
415
446
|
if (buffer == NULL) {
|
416
|
-
|
447
|
+
rewrite_error_query_static(query_context, "schema name too long", NULL);
|
417
448
|
return;
|
418
449
|
}
|
419
450
|
|
@@ -425,7 +456,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
425
456
|
if (errmsg != NULL) {
|
426
457
|
rewrite_error_query(query_context, errmsg, errcode);
|
427
458
|
} else if (status != POOL_CONTINUE) {
|
428
|
-
|
459
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
429
460
|
}
|
430
461
|
|
431
462
|
/* build run_system_catalog_as_temp_table query */
|
@@ -447,7 +478,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
447
478
|
buffer = strcpy_capped(buffer, bufend - buffer, "')");
|
448
479
|
|
449
480
|
if (buffer == NULL) {
|
450
|
-
|
481
|
+
rewrite_error_query_static(query_context, "metadata too long", NULL);
|
451
482
|
return;
|
452
483
|
}
|
453
484
|
|
@@ -459,7 +490,7 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
459
490
|
if (errmsg != NULL) {
|
460
491
|
rewrite_error_query(query_context, errmsg, errcode);
|
461
492
|
} else if (status != POOL_CONTINUE) {
|
462
|
-
|
493
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
463
494
|
}
|
464
495
|
|
465
496
|
/* rewrite query */
|
@@ -470,13 +501,47 @@ static void run_and_rewrite_system_catalog_query(POOL_SESSION_CONTEXT* session_c
|
|
470
501
|
buffer = strcpy_capped(buffer, bufend - buffer, PRESTO_RESULT_TABLE_NAME);
|
471
502
|
|
472
503
|
if (buffer == NULL) {
|
473
|
-
|
504
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
474
505
|
return;
|
475
506
|
}
|
476
507
|
|
477
508
|
do_replace_query(query_context, rewrite_query_string_buffer);
|
478
509
|
}
|
479
510
|
|
511
|
+
/*
|
512
|
+
* /\A\s*select\s*\*\s*from\s+(("[^\\"]*([\\"][^\\"]*)*")|[a-zA-Z_][a-zA-Z0-9_]*)(\.(("[^\\"]*([\\"][^\\"]*)*")|[a-zA-Z_][a-zA-Z0-9_]*))?\s*(;|\z)/.to_s
|
513
|
+
*/
|
514
|
+
#define AUTO_LIMIT_QUERY_PATTERN "\\A\\s*select\\s*\\*\\s*from\\s+((\"[^\\\\\"]*([\\\\\"][^\\\\\"]*)*\")|[a-zA-Z_][a-zA-Z0-9_]*)(\\.((\"[^\\\\\"]*([\\\\\"][^\\\\\"]*)*\")|[a-zA-Z_][a-zA-Z0-9_]*))?\\s*(;|\\z)"
|
515
|
+
|
516
|
+
static bool match_auto_limit_pattern(const char* query)
|
517
|
+
{
|
518
|
+
const char* errptr;
|
519
|
+
int erroffset;
|
520
|
+
pcre* pattern;
|
521
|
+
int ret;
|
522
|
+
int ovec[10];
|
523
|
+
|
524
|
+
pattern = pcre_compile(AUTO_LIMIT_QUERY_PATTERN,
|
525
|
+
PCRE_CASELESS | PCRE_NO_AUTO_CAPTURE | PCRE_UTF8, &errptr, &erroffset, NULL);
|
526
|
+
if (pattern == NULL) {
|
527
|
+
// TODO pcre pattern should be precompiled. see also pcre_study.
|
528
|
+
pool_error("match_auto_limit_pattern: invalid regexp %s at %d", errptr, erroffset);
|
529
|
+
pcre_free(pattern);
|
530
|
+
return false;
|
531
|
+
}
|
532
|
+
|
533
|
+
ret = pcre_exec(pattern, NULL, query, strlen(query), 0, 0, &ovec, sizeof(ovec));
|
534
|
+
if (ret < 0) {
|
535
|
+
// error. pattern didn't match in most of cases
|
536
|
+
pcre_free(pattern);
|
537
|
+
return false;
|
538
|
+
}
|
539
|
+
|
540
|
+
pcre_free(pattern);
|
541
|
+
|
542
|
+
return true;
|
543
|
+
}
|
544
|
+
|
480
545
|
static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context, POOL_QUERY_CONTEXT* query_context)
|
481
546
|
{
|
482
547
|
char *buffer, *bufend;
|
@@ -519,11 +584,17 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
519
584
|
if (c != NULL) {
|
520
585
|
*c = save;
|
521
586
|
}
|
587
|
+
|
588
|
+
if (match_auto_limit_pattern(query)) {
|
589
|
+
// TODO send warning message to client
|
590
|
+
pool_debug("run_and_rewrite_presto_query: adding 'limit 1000' to a SELECT * query");
|
591
|
+
buffer = strcpy_capped(buffer, bufend - buffer, " limit 1000");
|
592
|
+
}
|
522
593
|
}
|
523
594
|
buffer = strcpy_capped(buffer, bufend - buffer, "')");
|
524
595
|
|
525
596
|
if (buffer == NULL) {
|
526
|
-
|
597
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
527
598
|
return;
|
528
599
|
}
|
529
600
|
|
@@ -536,7 +607,7 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
536
607
|
rewrite_error_query(query_context, errmsg, errcode);
|
537
608
|
return;
|
538
609
|
} else if (status != POOL_CONTINUE) {
|
539
|
-
|
610
|
+
rewrite_error_query_static(query_context, "Unknown execution error", NULL);
|
540
611
|
return;
|
541
612
|
}
|
542
613
|
|
@@ -548,7 +619,7 @@ static void run_and_rewrite_presto_query(POOL_SESSION_CONTEXT* session_context,
|
|
548
619
|
buffer = strcpy_capped(buffer, bufend - buffer, PRESTO_RESULT_TABLE_NAME);
|
549
620
|
|
550
621
|
if (buffer == NULL) {
|
551
|
-
|
622
|
+
rewrite_error_query_static(query_context, "query too long", NULL);
|
552
623
|
return;
|
553
624
|
}
|
554
625
|
|
@@ -564,7 +635,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
564
635
|
POOL_CONNECTION_POOL *backend;
|
565
636
|
int i;
|
566
637
|
|
567
|
-
const char*
|
638
|
+
const char* static_error_message;
|
568
639
|
enum {
|
569
640
|
REWRITE_CLEAR,
|
570
641
|
REWRITE_SYSTEM_CATALOG,
|
@@ -676,7 +747,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
676
747
|
*/
|
677
748
|
if (TSTATE(backend, PRIMARY_NODE_ID) == 'I' ||
|
678
749
|
(!pool_is_writing_transaction() &&
|
679
|
-
!pool_is_failed_transaction() &&
|
750
|
+
!(MAJOR(backend) == PROTO_MAJOR_V3 && pool_is_failed_transaction()) &&
|
680
751
|
pool_get_transaction_isolation() != POOL_SERIALIZABLE))
|
681
752
|
{
|
682
753
|
BackendInfo *bkinfo = pool_get_node_info(session_context->load_balance_node_id);
|
@@ -695,7 +766,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
695
766
|
pool_debug("prestogres: send_to_where: replication delay");
|
696
767
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
697
768
|
rewrite_mode = REWRITE_ERROR;
|
698
|
-
|
769
|
+
static_error_message = "unexpected replication delay";
|
699
770
|
}
|
700
771
|
|
701
772
|
/*
|
@@ -776,16 +847,27 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
776
847
|
pool_debug("prestogres: send_to_where: invalid session state");
|
777
848
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
778
849
|
rewrite_mode = REWRITE_ERROR;
|
779
|
-
|
850
|
+
static_error_message = "invalid session state";
|
780
851
|
}
|
781
852
|
}
|
853
|
+
else if (query_context->is_parse_error)
|
854
|
+
{
|
855
|
+
/*
|
856
|
+
* If failed to parse the query, run it on Presto because
|
857
|
+
* it may include Presto's SQL syntax extensions.
|
858
|
+
*/
|
859
|
+
pool_debug("prestogres: send_to_where: parse-error");
|
860
|
+
pool_set_node_to_be_sent(query_context,
|
861
|
+
session_context->load_balance_node_id);
|
862
|
+
rewrite_mode = REWRITE_PRESTO;
|
863
|
+
}
|
782
864
|
else
|
783
865
|
{
|
784
866
|
/* Send to the primary only */
|
785
867
|
pool_debug("prestogres: send_to_where: non-select");
|
786
868
|
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
787
869
|
rewrite_mode = REWRITE_ERROR;
|
788
|
-
|
870
|
+
static_error_message = "only SELECT is supported";
|
789
871
|
}
|
790
872
|
}
|
791
873
|
}
|
@@ -891,7 +973,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no
|
|
891
973
|
break;
|
892
974
|
|
893
975
|
case REWRITE_ERROR:
|
894
|
-
|
976
|
+
rewrite_error_query_static(query_context, static_error_message, NULL);
|
895
977
|
break;
|
896
978
|
}
|
897
979
|
}
|
data/pgsql/prestogres.py
CHANGED
@@ -48,25 +48,22 @@ def _build_create_temp_table_sql(table_name, column_names, column_types):
|
|
48
48
|
return create_sql
|
49
49
|
|
50
50
|
# build CREATE TABLE statement
|
51
|
-
def
|
52
|
-
|
51
|
+
def _build_alter_table_holder_sql(schema_name, table_name, column_names, column_types, not_nulls):
|
52
|
+
alter_sql = "alter table %s.%s \n " % (plpy.quote_ident(schema_name), plpy.quote_ident(table_name))
|
53
53
|
|
54
54
|
first = True
|
55
55
|
for column_name, column_type, not_null in zip(column_names, column_types, not_nulls):
|
56
56
|
if first:
|
57
57
|
first = False
|
58
58
|
else:
|
59
|
-
|
59
|
+
alter_sql += ",\n "
|
60
60
|
|
61
|
-
|
62
|
-
create_sql += " "
|
63
|
-
create_sql += column_type
|
61
|
+
alter_sql += "add %s %s" % (plpy.quote_ident(column_name), column_type)
|
64
62
|
|
65
63
|
if not_null:
|
66
|
-
|
64
|
+
alter_sql += " not null"
|
67
65
|
|
68
|
-
|
69
|
-
return create_sql
|
66
|
+
return alter_sql
|
70
67
|
|
71
68
|
# build INSERT INTO statement and string format to build VALUES (..), ...
|
72
69
|
def _build_insert_into_sql(table_name, column_names):
|
@@ -231,6 +228,8 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
231
228
|
statements = []
|
232
229
|
schema_names = []
|
233
230
|
|
231
|
+
table_holder_id = 0
|
232
|
+
|
234
233
|
for schema_name, tables in schemas.items():
|
235
234
|
if schema_name == "sys" or schema_name == "information_schema":
|
236
235
|
# skip system schemas
|
@@ -248,8 +247,17 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
248
247
|
column_types.append(_pg_table_type(column.type))
|
249
248
|
not_nulls.append(not column.nullable)
|
250
249
|
|
251
|
-
|
252
|
-
statements.append(
|
250
|
+
# rename table holder into the schema
|
251
|
+
statements.append("alter table prestogres_catalog.table_holder_%d set schema %s" % \
|
252
|
+
(table_holder_id, plpy.quote_ident(schema_name)))
|
253
|
+
statements.append("alter table %s.table_holder_%d rename to %s" % \
|
254
|
+
(plpy.quote_ident(schema_name), table_holder_id, plpy.quote_ident(table_name)))
|
255
|
+
|
256
|
+
# change columns
|
257
|
+
alter_sql = _build_alter_table_holder_sql(schema_name, table_name, column_names, column_types, not_nulls)
|
258
|
+
statements.append(alter_sql)
|
259
|
+
|
260
|
+
table_holder_id += 1
|
253
261
|
|
254
262
|
# cache expires after 60 seconds
|
255
263
|
SchemaCacheEntry.set_cache(server, user, catalog, schema, schema_names, statements, time.time() + 60)
|
@@ -267,45 +275,50 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
267
275
|
subxact = plpy.subtransaction()
|
268
276
|
subxact.enter()
|
269
277
|
try:
|
270
|
-
#
|
278
|
+
# drop all schemas excepting prestogres_catalog, pg_catalog, information_schema, public
|
279
|
+
# and schema holders
|
271
280
|
sql = "select n.nspname as schema_name from pg_catalog.pg_namespace n" \
|
272
281
|
" where n.nspname not in ('prestogres_catalog', 'pg_catalog', 'information_schema', 'public')" \
|
282
|
+
" and n.nspname not like 'prestogres_catalog_schema_holder_%'" \
|
273
283
|
" and n.nspname !~ '^pg_toast'"
|
274
284
|
for row in plpy.cursor(sql):
|
275
285
|
plpy.execute("drop schema %s cascade" % plpy.quote_ident(row["schema_name"]))
|
276
286
|
|
277
|
-
#
|
278
|
-
|
279
|
-
sql = "select n.nspname as schema_name, c.relname as table_name from pg_catalog.pg_class c" \
|
280
|
-
" left join pg_catalog.pg_namespace n on n.oid = c.relnamespace" \
|
281
|
-
" where c.relkind in ('r')" \
|
282
|
-
" and n.nspname in ('prestogres_catalog')" \
|
283
|
-
" and n.nspname !~ '^pg_toast'"
|
284
|
-
for row in plpy.cursor(sql):
|
285
|
-
plpy.execute("drop table %s.%s" % (plpy.quote_ident(row["schema_name"]), plpy.quote_ident(row["table_name"])))
|
286
|
-
|
287
|
-
# create schemas
|
287
|
+
# alter schema holders
|
288
|
+
schema_holder_id = 0
|
288
289
|
for schema_name in schema_names:
|
289
290
|
try:
|
290
|
-
plpy.execute("
|
291
|
+
plpy.execute("alter schema prestogres_catalog_schema_holder_%s rename to %s" % \
|
292
|
+
(schema_holder_id, plpy.quote_ident(schema_name)))
|
293
|
+
schema_holder_id += 1
|
291
294
|
except:
|
292
|
-
# ignore error
|
295
|
+
# ignore error?
|
293
296
|
pass
|
294
297
|
|
295
|
-
#
|
298
|
+
# alter table holders in prestogres_catalog schema
|
296
299
|
for statement in statements:
|
297
300
|
plpy.execute(statement)
|
298
301
|
|
299
|
-
#
|
302
|
+
# drop prestogres_catalog schema
|
303
|
+
plpy.execute("drop schema prestogres_catalog cascade")
|
304
|
+
|
305
|
+
# drop schema holders
|
306
|
+
sql = "select n.nspname as schema_name from pg_catalog.pg_namespace n" \
|
307
|
+
" where n.nspname like 'prestogres_catalog_schema_holder_%'"
|
308
|
+
for row in plpy.cursor(sql):
|
309
|
+
plpy.execute("drop schema %s" % plpy.quote_ident(row["schema_name"]))
|
310
|
+
|
311
|
+
# run the actual query and save result
|
300
312
|
metadata = plpy.execute(query)
|
301
313
|
column_names = metadata.colnames()
|
302
314
|
column_type_oids = metadata.coltypes()
|
303
315
|
result = map(lambda row: map(row.get, column_names), metadata)
|
304
316
|
|
305
|
-
#
|
317
|
+
# save result schema
|
306
318
|
oid_to_type_name = _load_oid_to_type_name_mapping(column_type_oids)
|
307
319
|
column_types = map(oid_to_type_name.get, column_type_oids)
|
308
320
|
|
321
|
+
# store query cache
|
309
322
|
query_cache[query] = QueryResult(column_names, column_types, result)
|
310
323
|
|
311
324
|
finally:
|
@@ -325,3 +338,12 @@ def run_system_catalog_as_temp_table(server, user, catalog, schema, result_table
|
|
325
338
|
e.__class__.__module__ = "__main__"
|
326
339
|
raise
|
327
340
|
|
341
|
+
def create_schema_holders(count):
|
342
|
+
for i in range(count):
|
343
|
+
plpy.execute("drop schema if exists prestogres_catalog_schema_holder_%d cascade" % i)
|
344
|
+
plpy.execute("create schema prestogres_catalog_schema_holder_%d" % i)
|
345
|
+
|
346
|
+
def create_table_holders(count):
|
347
|
+
for i in range(count):
|
348
|
+
plpy.execute("create table prestogres_catalog.table_holder_%d ()" % i)
|
349
|
+
|
data/pgsql/setup.sql
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
create language "plpythonu";
|
3
3
|
|
4
|
+
drop schema if exists prestogres_catalog cascade;
|
5
|
+
|
4
6
|
create schema prestogres_catalog;
|
5
7
|
|
6
8
|
create or replace function prestogres_catalog.run_presto_as_temp_table(
|
@@ -27,3 +29,19 @@ import prestogres
|
|
27
29
|
prestogres.run_system_catalog_as_temp_table(server, user, catalog, schema, table_name, query)
|
28
30
|
$$ language plpythonu;
|
29
31
|
|
32
|
+
create or replace function prestogres_catalog.create_schema_holders("count" int)
|
33
|
+
returns void as $$
|
34
|
+
import prestogres
|
35
|
+
prestogres.create_schema_holders(count);
|
36
|
+
$$ language plpythonu;
|
37
|
+
|
38
|
+
create or replace function prestogres_catalog.create_table_holders("count" int)
|
39
|
+
returns void as $$
|
40
|
+
import prestogres
|
41
|
+
prestogres.create_table_holders(count);
|
42
|
+
$$ language plpythonu;
|
43
|
+
|
44
|
+
select prestogres_catalog.create_schema_holders(512);
|
45
|
+
|
46
|
+
select prestogres_catalog.create_table_holders(2048);
|
47
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prestogres
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-03-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -479,18 +479,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
479
479
|
- - ! '>='
|
480
480
|
- !ruby/object:Gem::Version
|
481
481
|
version: '0'
|
482
|
-
segments:
|
483
|
-
- 0
|
484
|
-
hash: -1048444669451031740
|
485
482
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
486
483
|
none: false
|
487
484
|
requirements:
|
488
485
|
- - ! '>='
|
489
486
|
- !ruby/object:Gem::Version
|
490
487
|
version: '0'
|
491
|
-
segments:
|
492
|
-
- 0
|
493
|
-
hash: -1048444669451031740
|
494
488
|
requirements: []
|
495
489
|
rubyforge_project:
|
496
490
|
rubygems_version: 1.8.23
|
@@ -498,3 +492,4 @@ signing_key:
|
|
498
492
|
specification_version: 3
|
499
493
|
summary: Presto PostgreSQL protocol gateway
|
500
494
|
test_files: []
|
495
|
+
has_rdoc: false
|