Skip to content

Commit ba54ceb

Browse files
committed
ext/pgsql: pg_convert/pg_insert/pg_update/pg_delete caching regexes.
Close GH-15039
1 parent 2cfcfe0 commit ba54ceb

File tree

3 files changed

+78
-77
lines changed

3 files changed

+78
-77
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ PHP NEWS
2020
. Added getApiVersion() and removed from getAttribute().
2121
(SakiTakamachi)
2222

23+
- PGSQL:
24+
. pg_convert/pg_insert/pg_update/pg_delete ; regexes are now cached.
25+
(David Carlier)
26+
2327
- Standard:
2428
. Fix references in request_parse_body() options array. (nielsdos)
2529
. Add RoundingMode enum. (timwolla, saki)

ext/pgsql/pgsql.c

Lines changed: 70 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,55 @@ static PHP_GINIT_FUNCTION(pgsql)
490490
#if defined(COMPILE_DL_PGSQL) && defined(ZTS)
491491
ZEND_TSRMLS_CACHE_UPDATE();
492492
#endif
493+
494+
size_t i = 0;
493495
memset(pgsql_globals, 0, sizeof(zend_pgsql_globals));
494496
zend_hash_init(&pgsql_globals->connections, 0, NULL, NULL, 1);
497+
498+
#define ADD_REGEX(reg) \
499+
do { \
500+
ZEND_ASSERT(i < PGSQL_MAX_REGEXES); \
501+
pgsql_globals->regexes[i ++] = zend_string_init(reg, strlen(reg), true);\
502+
} while(0)
503+
ADD_REGEX("#^([+-]{0,1}[0-9]+)$#n");
504+
ADD_REGEX("#^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$#n");
505+
ADD_REGEX("#^[+-]{0,1}(inf)(inity){0,1}$#ni");
506+
ADD_REGEX("#^[0-9]+$#n");
507+
ADD_REGEX("#^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])(\\/[0-9]{1,3})?$#n");
508+
ADD_REGEX("#^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\\/[0-9]{1,3})?$#n");
509+
ADD_REGEX("#^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})(([ \\t]+|T)(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$#ni");
510+
ADD_REGEX("#^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$#ni");
511+
ADD_REGEX("#^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}){0,1}$#ni");
512+
ADD_REGEX("#^(@?[ \\t]+)?("
513+
/* Textual time units and their abbreviations: */
514+
"(([-+]?[ \\t]+)?"
515+
"[0-9]+(\\.[0-9]*)?[ \\t]*"
516+
"(millenniums|millennia|millennium|mil|mils|"
517+
"centuries|century|cent|c|"
518+
"decades|decade|dec|decs|"
519+
"years|year|y|"
520+
"months|month|mon|"
521+
"weeks|week|w|"
522+
"days|day|d|"
523+
"hours|hour|hr|hrs|h|"
524+
"minutes|minute|mins|min|m|"
525+
"seconds|second|secs|sec|s))+|"
526+
/* Textual time units plus (dd)* hh[:mm[:ss]] */
527+
"((([-+]?[ \\t]+)?"
528+
"[0-9]+(\\.[0-9]*)?[ \\t]*"
529+
"(millenniums|millennia|millennium|mil|mils|"
530+
"centuries|century|cent|c|"
531+
"decades|decade|dec|decs|"
532+
"years|year|y|"
533+
"months|month|mon|"
534+
"weeks|week|w|"
535+
"days|day|d))+"
536+
"([-+]?[ \\t]+"
537+
"([0-9]+[ \\t]+)+" /* dd */
538+
"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */
539+
")?))"
540+
"([ \\t]+ago)?$#ni");
541+
ADD_REGEX("#^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$#ni");
495542
}
496543

497544
static void php_libpq_version(char *buf, size_t len)
@@ -560,6 +607,9 @@ PHP_MSHUTDOWN_FUNCTION(pgsql)
560607
UNREGISTER_INI_ENTRIES();
561608
zend_hash_destroy(&PGG(connections));
562609

610+
for (size_t i = 0; i < PGSQL_MAX_REGEXES; i ++)
611+
zend_string_release_ex(PGG(regexes[i]), true);
612+
563613
return SUCCESS;
564614
}
565615

@@ -4671,12 +4721,11 @@ static php_pgsql_data_type php_pgsql_get_data_type(const zend_string *type_name)
46714721
/* {{{ php_pgsql_convert_match
46724722
* test field value with regular expression specified.
46734723
*/
4674-
static int php_pgsql_convert_match(const zend_string *str, const char *regex , size_t regex_len, int icase)
4724+
static int php_pgsql_convert_match(const zend_string *str, zend_string *regex)
46754725
{
4726+
pcre_cache_entry *centry;
46764727
pcre2_code *re;
4677-
PCRE2_SIZE err_offset;
4678-
int res, errnumber;
4679-
uint32_t options = PCRE2_NO_AUTO_CAPTURE;
4728+
int res;
46804729
size_t i;
46814730
pcre2_match_data *match_data;
46824731

@@ -4689,27 +4738,21 @@ static int php_pgsql_convert_match(const zend_string *str, const char *regex , s
46894738
}
46904739
}
46914740

4692-
if (icase) {
4693-
options |= PCRE2_CASELESS;
4694-
}
4695-
4696-
re = pcre2_compile((PCRE2_SPTR)regex, regex_len, options, &errnumber, &err_offset, php_pcre_cctx());
4697-
if (NULL == re) {
4698-
PCRE2_UCHAR err_msg[128];
4699-
pcre2_get_error_message(errnumber, err_msg, sizeof(err_msg));
4700-
php_error_docref(NULL, E_WARNING, "Cannot compile regex: '%s'", err_msg);
4741+
centry = pcre_get_compiled_regex_cache(regex);
4742+
if (NULL == centry) {
47014743
return FAILURE;
47024744
}
47034745

4746+
re = php_pcre_pce_re(centry);
47044747
match_data = php_pcre_create_match_data(0, re);
47054748
if (NULL == match_data) {
4706-
pcre2_code_free(re);
47074749
php_error_docref(NULL, E_WARNING, "Cannot allocate match data");
47084750
return FAILURE;
47094751
}
4752+
php_pcre_pce_incref(centry);
47104753
res = pcre2_match(re, (PCRE2_SPTR)ZSTR_VAL(str), ZSTR_LEN(str), 0, 0, match_data, php_pcre_mctx());
47114754
php_pcre_free_match_data(match_data);
4712-
pcre2_code_free(re);
4755+
php_pcre_pce_decref(centry);
47134756

47144757
if (res == PCRE2_ERROR_NOMATCH) {
47154758
return FAILURE;
@@ -4890,14 +4933,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
48904933
}
48914934
else {
48924935
/* FIXME: better regex must be used */
4893-
#define REGEX0 "^([+-]{0,1}[0-9]+)$"
4894-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
4936+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[0])) == FAILURE) {
48954937
err = 1;
48964938
}
48974939
else {
48984940
ZVAL_STRINGL(&new_val, Z_STRVAL_P(val), Z_STRLEN_P(val));
48994941
}
4900-
#undef REGEX0
49014942
}
49024943
break;
49034944

@@ -4933,11 +4974,9 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
49334974
ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
49344975
}
49354976
else {
4936-
#define REGEX0 "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"
4937-
#define REGEX1 "^[+-]{0,1}(inf)(inity){0,1}$"
49384977
/* better regex? */
4939-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE) {
4940-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX1, sizeof(REGEX1)-1, 1) == FAILURE) {
4978+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[1])) == FAILURE) {
4979+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[2])) == FAILURE) {
49414980
err = 1;
49424981
} else {
49434982
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
@@ -4946,8 +4985,6 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
49464985
else {
49474986
ZVAL_STRING(&new_val, Z_STRVAL_P(val));
49484987
}
4949-
#undef REGEX0
4950-
#undef REGEX1
49514988
}
49524989
break;
49534990

@@ -5043,7 +5080,7 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
50435080
}
50445081
else {
50455082
/* better regex? */
5046-
if (php_pgsql_convert_match(Z_STR_P(val), "^[0-9]+$", sizeof("^[0-9]+$")-1, 0) == FAILURE) {
5083+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[3])) == FAILURE) {
50475084
err = 1;
50485085
}
50495086
else {
@@ -5083,20 +5120,16 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
50835120
ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
50845121
}
50855122
else {
5086-
#define REGEX0 "^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])(\\/[0-9]{1,3})?$"
5087-
#define REGEX1 "^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\\/[0-9]{1,3})?$"
50885123
/* The inet type holds an IPv4 or IPv6 host address, and optionally its subnet, all in one field. See more in the doc.
50895124
The regex might still be not perfect, but catches the most of IP variants. We might decide to remove the regex
50905125
at all though and let the server side to handle it.*/
5091-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 0) == FAILURE
5092-
&& php_pgsql_convert_match(Z_STR_P(val), REGEX1, sizeof(REGEX1)-1, 0) == FAILURE) {
5126+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[4])) == FAILURE
5127+
&& php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[5])) == FAILURE) {
50935128
err = 2;
50945129
}
50955130
else {
50965131
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
50975132
}
5098-
#undef REGEX0
5099-
#undef REGEX1
51005133
}
51015134
break;
51025135

@@ -5127,14 +5160,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
51275160
} else if (zend_string_equals_literal_ci(Z_STR_P(val), "now()")) {
51285161
ZVAL_STRINGL(&new_val, "NOW()", sizeof("NOW()")-1);
51295162
} else {
5130-
#define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})(([ \\t]+|T)(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}(\\.[0-9]+){0,1}([ \\t]*([+-][0-9]{1,4}(:[0-9]{1,2}){0,1}|[-a-zA-Z_/+]{1,50})){0,1})){0,1}$"
51315163
/* better regex? */
5132-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5164+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[6])) == FAILURE) {
51335165
err = 1;
51345166
} else {
51355167
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
51365168
}
5137-
#undef REGEX0
51385169
}
51395170
break;
51405171

@@ -5158,15 +5189,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
51585189
ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
51595190
}
51605191
else {
5161-
#define REGEX0 "^([0-9]{4}[/-][0-9]{1,2}[/-][0-9]{1,2})$"
51625192
/* FIXME: better regex must be used */
5163-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5193+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[7])) == FAILURE) {
51645194
err = 1;
51655195
}
51665196
else {
51675197
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
51685198
}
5169-
#undef REGEX0
51705199
}
51715200
break;
51725201

@@ -5190,15 +5219,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
51905219
ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
51915220
}
51925221
else {
5193-
#define REGEX0 "^(([0-9]{1,2}:[0-9]{1,2}){1}(:[0-9]{1,2}){0,1}){0,1}$"
51945222
/* FIXME: better regex must be used */
5195-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5223+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[8])) == FAILURE) {
51965224
err = 1;
51975225
}
51985226
else {
51995227
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
52005228
}
5201-
#undef REGEX0
52025229
}
52035230
break;
52045231

@@ -5239,44 +5266,13 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
52395266
unit markings. For example, '1 12:59:10' is read the same as '1 day 12 hours 59 min 10
52405267
sec'.
52415268
*/
5242-
#define REGEX0 \
5243-
"^(@?[ \\t]+)?(" \
5244-
/* Textual time units and their abbreviations: */ \
5245-
"(([-+]?[ \\t]+)?" \
5246-
"[0-9]+(\\.[0-9]*)?[ \\t]*" \
5247-
"(millenniums|millennia|millennium|mil|mils|" \
5248-
"centuries|century|cent|c|" \
5249-
"decades|decade|dec|decs|" \
5250-
"years|year|y|" \
5251-
"months|month|mon|" \
5252-
"weeks|week|w|" \
5253-
"days|day|d|" \
5254-
"hours|hour|hr|hrs|h|" \
5255-
"minutes|minute|mins|min|m|" \
5256-
"seconds|second|secs|sec|s))+|" \
5257-
/* Textual time units plus (dd)* hh[:mm[:ss]] */ \
5258-
"((([-+]?[ \\t]+)?" \
5259-
"[0-9]+(\\.[0-9]*)?[ \\t]*" \
5260-
"(millenniums|millennia|millennium|mil|mils|" \
5261-
"centuries|century|cent|c|" \
5262-
"decades|decade|dec|decs|" \
5263-
"years|year|y|" \
5264-
"months|month|mon|" \
5265-
"weeks|week|w|" \
5266-
"days|day|d))+" \
5267-
"([-+]?[ \\t]+" \
5268-
"([0-9]+[ \\t]+)+" /* dd */ \
5269-
"(([0-9]{1,2}:){0,2}[0-9]{0,2})" /* hh:[mm:[ss]] */ \
5270-
")?))" \
5271-
"([ \\t]+ago)?$"
5272-
5273-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5269+
5270+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[9])) == FAILURE) {
52745271
err = 1;
52755272
}
52765273
else {
52775274
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
52785275
}
5279-
#undef REGEX0
52805276
}
52815277
break;
52825278

@@ -5341,14 +5337,12 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
53415337
ZVAL_STR(&new_val, ZSTR_KNOWN(ZEND_STR_NULL));
53425338
}
53435339
else {
5344-
#define REGEX0 "^([0-9a-f]{2,2}:){5,5}[0-9a-f]{2,2}$"
5345-
if (php_pgsql_convert_match(Z_STR_P(val), REGEX0, sizeof(REGEX0)-1, 1) == FAILURE) {
5340+
if (php_pgsql_convert_match(Z_STR_P(val), PGG(regexes[10])) == FAILURE) {
53465341
err = 1;
53475342
}
53485343
else {
53495344
ZVAL_STR(&new_val, php_pgsql_add_quotes(Z_STR_P(val)));
53505345
}
5351-
#undef REGEX0
53525346
}
53535347
break;
53545348

ext/pgsql/php_pgsql.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,17 @@ static const php_stream_ops php_stream_pgsql_fd_ops = {
175175
php_pgsql_fd_set_option
176176
};
177177

178+
#define PGSQL_MAX_REGEXES 11
179+
178180
ZEND_BEGIN_MODULE_GLOBALS(pgsql)
179181
zend_long num_links,num_persistent;
180182
zend_long max_links,max_persistent;
181183
bool allow_persistent;
182-
int ignore_notices;
184+
int ignore_notices;
183185
zend_long auto_reset_persistent;
184186
int log_notices;
185187
zend_object *default_link; /* default link when connection is omitted */
188+
zend_string *regexes[PGSQL_MAX_REGEXES];
186189
HashTable field_oids;
187190
HashTable table_oids;
188191
HashTable connections;

0 commit comments

Comments
 (0)