@@ -314,55 +314,70 @@ char *database_build_base_ref (const char *schema, const char *table_name) {
314314// Schema parameter: pass empty string to fall back to current_schema() via SQL.
315315char * sql_build_delete_cols_not_in_schema_query (const char * schema , const char * table_name , const char * meta_ref , const char * pkcol ) {
316316 const char * schema_param = schema ? schema : "" ;
317+
318+ char esc_table [1024 ], esc_schema [1024 ];
319+ sql_escape_literal (table_name , esc_table , sizeof (esc_table ));
320+ sql_escape_literal (schema_param , esc_schema , sizeof (esc_schema ));
321+
317322 return cloudsync_memory_mprintf (
318323 "DELETE FROM %s WHERE col_name NOT IN ("
319324 "SELECT column_name FROM information_schema.columns WHERE table_name = '%s' "
320325 "AND table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
321326 "UNION SELECT '%s'"
322327 ");" ,
323- meta_ref , table_name , schema_param , pkcol
328+ meta_ref , esc_table , esc_schema , pkcol
324329 );
325330}
326331
327332// Builds query to get comma-separated list of primary key column names.
328333char * sql_build_pk_collist_query (const char * schema , const char * table_name ) {
329334 const char * schema_param = schema ? schema : "" ;
335+
336+ char esc_table [1024 ], esc_schema [1024 ];
337+ sql_escape_literal (table_name , esc_table , sizeof (esc_table ));
338+ sql_escape_literal (schema_param , esc_schema , sizeof (esc_schema ));
339+
330340 return cloudsync_memory_mprintf (
331341 "SELECT string_agg(quote_ident(column_name), ',') "
332342 "FROM information_schema.key_column_usage "
333343 "WHERE table_name = '%s' AND table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
334344 "AND constraint_name LIKE '%%_pkey';" ,
335- table_name , schema_param
345+ esc_table , esc_schema
336346 );
337347}
338348
339349// Builds query to get SELECT list of decoded primary key columns.
340350char * sql_build_pk_decode_selectlist_query (const char * schema , const char * table_name ) {
341351 const char * schema_param = schema ? schema : "" ;
352+
353+ char esc_table [1024 ], esc_schema [1024 ];
354+ sql_escape_literal (table_name , esc_table , sizeof (esc_table ));
355+ sql_escape_literal (schema_param , esc_schema , sizeof (esc_schema ));
356+
342357 return cloudsync_memory_mprintf (
343358 "SELECT string_agg("
344359 "'cloudsync_pk_decode(pk, ' || ordinal_position || ') AS ' || quote_ident(column_name), ',' ORDER BY ordinal_position"
345360 ") "
346361 "FROM information_schema.key_column_usage "
347362 "WHERE table_name = '%s' AND table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
348363 "AND constraint_name LIKE '%%_pkey';" ,
349- table_name , schema_param
364+ esc_table , esc_schema
350365 );
351366}
352367
353368// Builds query to get qualified (schema.table.column) primary key column list.
354369char * sql_build_pk_qualified_collist_query (const char * schema , const char * table_name ) {
355370 const char * schema_param = schema ? schema : "" ;
356-
357- char buffer [1024 ];
358- char * singlequote_escaped_table_name = sql_escape_literal (table_name , buffer , sizeof (buffer ));
359- if (! singlequote_escaped_table_name ) return NULL ;
360-
371+
372+ char esc_table [ 1024 ], esc_schema [1024 ];
373+ sql_escape_literal (table_name , esc_table , sizeof (esc_table ));
374+ sql_escape_literal ( schema_param , esc_schema , sizeof ( esc_schema )) ;
375+
361376 return cloudsync_memory_mprintf (
362377 "SELECT string_agg(quote_ident(column_name), ',' ORDER BY ordinal_position) "
363378 "FROM information_schema.key_column_usage "
364379 "WHERE table_name = '%s' AND table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
365- "AND constraint_name LIKE '%%_pkey';" , singlequote_escaped_table_name , schema_param
380+ "AND constraint_name LIKE '%%_pkey';" , esc_table , esc_schema
366381 );
367382}
368383
@@ -645,28 +660,29 @@ static bool database_system_exists (cloudsync_context *data, const char *name, c
645660 return false;
646661 }
647662
663+ bool need_schema_param = !force_public && strcmp (type , "trigger" ) != 0 ;
664+ Datum datum_name = CStringGetTextDatum (name );
665+ Datum datum_schema = need_schema_param ? CStringGetTextDatum (schema_param ) : (Datum )0 ;
666+
648667 MemoryContext oldcontext = CurrentMemoryContext ;
649668 PG_TRY ();
650669 {
651- if (force_public || strcmp ( type , "trigger" ) == 0 ) {
670+ if (! need_schema_param ) {
652671 // force_public or trigger: only need table/trigger name parameter
653672 Oid argtypes [1 ] = {TEXTOID };
654- Datum values [1 ] = {CStringGetTextDatum ( name ) };
673+ Datum values [1 ] = {datum_name };
655674 char nulls [1 ] = {' ' };
656675 int rc = SPI_execute_with_args (query , 1 , argtypes , values , nulls , true, 0 );
657676 exists = (rc >= 0 && SPI_processed > 0 );
658677 if (SPI_tuptable ) SPI_freetuptable (SPI_tuptable );
659- pfree (DatumGetPointer (values [0 ]));
660678 } else {
661679 // table with schema parameter
662680 Oid argtypes [2 ] = {TEXTOID , TEXTOID };
663- Datum values [2 ] = {CStringGetTextDatum ( name ), CStringGetTextDatum ( schema_param ) };
681+ Datum values [2 ] = {datum_name , datum_schema };
664682 char nulls [2 ] = {' ' , ' ' };
665683 int rc = SPI_execute_with_args (query , 2 , argtypes , values , nulls , true, 0 );
666684 exists = (rc >= 0 && SPI_processed > 0 );
667685 if (SPI_tuptable ) SPI_freetuptable (SPI_tuptable );
668- pfree (DatumGetPointer (values [0 ]));
669- pfree (DatumGetPointer (values [1 ]));
670686 }
671687 }
672688 PG_CATCH ();
@@ -680,6 +696,9 @@ static bool database_system_exists (cloudsync_context *data, const char *name, c
680696 }
681697 PG_END_TRY ();
682698
699+ pfree (DatumGetPointer (datum_name ));
700+ if (need_schema_param ) pfree (DatumGetPointer (datum_schema ));
701+
683702 elog (DEBUG1 , "database_system_exists %s: %d" , name , exists );
684703 return exists ;
685704 }
@@ -1135,6 +1154,10 @@ static int database_create_insert_trigger_internal (cloudsync_context *data, con
11351154
11361155 if (database_trigger_exists (data , trigger_name )) return DBRES_OK ;
11371156
1157+ char esc_tbl_literal [1024 ], esc_schema_literal [1024 ];
1158+ sql_escape_literal (table_name , esc_tbl_literal , sizeof (esc_tbl_literal ));
1159+ sql_escape_literal (schema_param , esc_schema_literal , sizeof (esc_schema_literal ));
1160+
11381161 char sql [2048 ];
11391162 snprintf (sql , sizeof (sql ),
11401163 "SELECT string_agg('NEW.' || quote_ident(kcu.column_name), ',' ORDER BY kcu.ordinal_position) "
@@ -1144,7 +1167,7 @@ static int database_create_insert_trigger_internal (cloudsync_context *data, con
11441167 " AND tc.table_schema = kcu.table_schema "
11451168 "WHERE tc.table_name = '%s' AND tc.table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
11461169 "AND tc.constraint_type = 'PRIMARY KEY';" ,
1147- table_name , schema_param );
1170+ esc_tbl_literal , esc_schema_literal );
11481171
11491172 char * pk_list = NULL ;
11501173 int rc = database_select_text (data , sql , & pk_list );
@@ -1162,7 +1185,7 @@ static int database_create_insert_trigger_internal (cloudsync_context *data, con
11621185 " RETURN NEW; "
11631186 "END; "
11641187 "$$ LANGUAGE plpgsql;" ,
1165- func_name , table_name , table_name , pk_list );
1188+ func_name , esc_tbl_literal , esc_tbl_literal , pk_list );
11661189 cloudsync_memory_free (pk_list );
11671190 if (!sql2 ) return DBRES_NOMEM ;
11681191
@@ -1197,13 +1220,16 @@ static int database_create_update_trigger_gos_internal (cloudsync_context *data,
11971220
11981221 if (database_trigger_exists (data , trigger_name )) return DBRES_OK ;
11991222
1223+ char esc_tbl_literal [1024 ];
1224+ sql_escape_literal (table_name , esc_tbl_literal , sizeof (esc_tbl_literal ));
1225+
12001226 char * sql = cloudsync_memory_mprintf (
12011227 "CREATE OR REPLACE FUNCTION \"%s\"() RETURNS trigger AS $$ "
12021228 "BEGIN "
12031229 " RAISE EXCEPTION 'Error: UPDATE operation is not allowed on table %s.'; "
12041230 "END; "
12051231 "$$ LANGUAGE plpgsql;" ,
1206- func_name , table_name );
1232+ func_name , esc_tbl_literal );
12071233 if (!sql ) return DBRES_NOMEM ;
12081234
12091235 int rc = database_exec (data , sql );
@@ -1217,7 +1243,7 @@ static int database_create_update_trigger_gos_internal (cloudsync_context *data,
12171243 "CREATE TRIGGER \"%s\" BEFORE UPDATE ON %s "
12181244 "FOR EACH ROW WHEN (cloudsync_is_enabled('%s') = true) "
12191245 "EXECUTE FUNCTION \"%s\"();" ,
1220- trigger_name , base_ref , table_name , func_name );
1246+ trigger_name , base_ref , esc_tbl_literal , func_name );
12211247 cloudsync_memory_free (base_ref );
12221248 if (!sql ) return DBRES_NOMEM ;
12231249
@@ -1240,6 +1266,10 @@ static int database_create_update_trigger_internal (cloudsync_context *data, con
12401266
12411267 if (database_trigger_exists (data , trigger_name )) return DBRES_OK ;
12421268
1269+ char esc_tbl_literal [1024 ], esc_schema_literal [1024 ];
1270+ sql_escape_literal (table_name , esc_tbl_literal , sizeof (esc_tbl_literal ));
1271+ sql_escape_literal (schema_param , esc_schema_literal , sizeof (esc_schema_literal ));
1272+
12431273 char sql [2048 ];
12441274 snprintf (sql , sizeof (sql ),
12451275 "SELECT string_agg("
@@ -1253,7 +1283,7 @@ static int database_create_update_trigger_internal (cloudsync_context *data, con
12531283 " AND tc.table_schema = kcu.table_schema "
12541284 "WHERE tc.table_name = '%s' AND tc.table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
12551285 "AND tc.constraint_type = 'PRIMARY KEY';" ,
1256- table_name , table_name , schema_param );
1286+ esc_tbl_literal , esc_tbl_literal , esc_schema_literal );
12571287
12581288 char * pk_values_list = NULL ;
12591289 int rc = database_select_text (data , sql , & pk_values_list );
@@ -1282,7 +1312,7 @@ static int database_create_update_trigger_internal (cloudsync_context *data, con
12821312 " AND tc.constraint_type = 'PRIMARY KEY' "
12831313 " AND kcu.column_name = c.column_name"
12841314 ");" ,
1285- table_name , table_name , schema_param );
1315+ esc_tbl_literal , esc_tbl_literal , esc_schema_literal );
12861316
12871317 char * col_values_list = NULL ;
12881318 rc = database_select_text (data , sql , & col_values_list );
@@ -1311,7 +1341,7 @@ static int database_create_update_trigger_internal (cloudsync_context *data, con
13111341 " RETURN NEW; "
13121342 "END; "
13131343 "$$ LANGUAGE plpgsql;" ,
1314- func_name , table_name , values_query );
1344+ func_name , esc_tbl_literal , values_query );
13151345 cloudsync_memory_free (values_query );
13161346 if (!sql2 ) return DBRES_NOMEM ;
13171347
@@ -1346,13 +1376,16 @@ static int database_create_delete_trigger_gos_internal (cloudsync_context *data,
13461376
13471377 if (database_trigger_exists (data , trigger_name )) return DBRES_OK ;
13481378
1379+ char esc_tbl_literal [1024 ];
1380+ sql_escape_literal (table_name , esc_tbl_literal , sizeof (esc_tbl_literal ));
1381+
13491382 char * sql = cloudsync_memory_mprintf (
13501383 "CREATE OR REPLACE FUNCTION \"%s\"() RETURNS trigger AS $$ "
13511384 "BEGIN "
13521385 " RAISE EXCEPTION 'Error: DELETE operation is not allowed on table %s.'; "
13531386 "END; "
13541387 "$$ LANGUAGE plpgsql;" ,
1355- func_name , table_name );
1388+ func_name , esc_tbl_literal );
13561389 if (!sql ) return DBRES_NOMEM ;
13571390
13581391 int rc = database_exec (data , sql );
@@ -1366,7 +1399,7 @@ static int database_create_delete_trigger_gos_internal (cloudsync_context *data,
13661399 "CREATE TRIGGER \"%s\" BEFORE DELETE ON %s "
13671400 "FOR EACH ROW WHEN (cloudsync_is_enabled('%s') = true) "
13681401 "EXECUTE FUNCTION \"%s\"();" ,
1369- trigger_name , base_ref , table_name , func_name );
1402+ trigger_name , base_ref , esc_tbl_literal , func_name );
13701403 cloudsync_memory_free (base_ref );
13711404 if (!sql ) return DBRES_NOMEM ;
13721405
@@ -1389,6 +1422,10 @@ static int database_create_delete_trigger_internal (cloudsync_context *data, con
13891422
13901423 if (database_trigger_exists (data , trigger_name )) return DBRES_OK ;
13911424
1425+ char esc_tbl_literal [1024 ], esc_schema_literal [1024 ];
1426+ sql_escape_literal (table_name , esc_tbl_literal , sizeof (esc_tbl_literal ));
1427+ sql_escape_literal (schema_param , esc_schema_literal , sizeof (esc_schema_literal ));
1428+
13921429 char sql [2048 ];
13931430 snprintf (sql , sizeof (sql ),
13941431 "SELECT string_agg('OLD.' || quote_ident(kcu.column_name), ',' ORDER BY kcu.ordinal_position) "
@@ -1398,7 +1435,7 @@ static int database_create_delete_trigger_internal (cloudsync_context *data, con
13981435 " AND tc.table_schema = kcu.table_schema "
13991436 "WHERE tc.table_name = '%s' AND tc.table_schema = COALESCE(NULLIF('%s', ''), current_schema()) "
14001437 "AND tc.constraint_type = 'PRIMARY KEY';" ,
1401- table_name , schema_param );
1438+ esc_tbl_literal , esc_schema_literal );
14021439
14031440 char * pk_list = NULL ;
14041441 int rc = database_select_text (data , sql , & pk_list );
@@ -1416,15 +1453,15 @@ static int database_create_delete_trigger_internal (cloudsync_context *data, con
14161453 " RETURN OLD; "
14171454 "END; "
14181455 "$$ LANGUAGE plpgsql;" ,
1419- func_name , table_name , table_name , pk_list );
1456+ func_name , esc_tbl_literal , esc_tbl_literal , pk_list );
14201457 cloudsync_memory_free (pk_list );
14211458 if (!sql2 ) return DBRES_NOMEM ;
14221459
14231460 rc = database_exec (data , sql2 );
14241461 cloudsync_memory_free (sql2 );
14251462 if (rc != DBRES_OK ) return rc ;
14261463
1427- char * base_ref = database_build_base_ref (cloudsync_schema ( data ) , table_name );
1464+ char * base_ref = database_build_base_ref (schema , table_name );
14281465 if (!base_ref ) return DBRES_NOMEM ;
14291466
14301467 sql2 = cloudsync_memory_mprintf (
0 commit comments